OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From 52adbb34c32d3e2e1bcdb941e20a6f81138b8248 Mon Sep 17 00:00:00 2001 |
2 | From: Matt Johnston <matt@ucc.asn.au> |
||
3 | Date: Thu, 23 Aug 2018 23:43:12 +0800 |
||
4 | Subject: [PATCH 2/2] Wait to fail invalid usernames |
||
5 | |||
6 | --- |
||
7 | auth.h | 6 +++--- |
||
8 | svr-auth.c | 19 +++++-------------- |
||
9 | svr-authpam.c | 26 ++++++++++++++++++++++---- |
||
10 | svr-authpasswd.c | 27 ++++++++++++++------------- |
||
11 | svr-authpubkey.c | 11 ++++++++++- |
||
12 | 5 files changed, 54 insertions(+), 35 deletions(-) |
||
13 | |||
14 | --- a/auth.h |
||
15 | +++ b/auth.h |
||
16 | @@ -37,9 +37,9 @@ void recv_msg_userauth_request(void); |
||
17 | void send_msg_userauth_failure(int partial, int incrfail); |
||
18 | void send_msg_userauth_success(void); |
||
19 | void send_msg_userauth_banner(buffer *msg); |
||
20 | -void svr_auth_password(void); |
||
21 | -void svr_auth_pubkey(void); |
||
22 | -void svr_auth_pam(void); |
||
23 | +void svr_auth_password(int valid_user); |
||
24 | +void svr_auth_pubkey(int valid_user); |
||
25 | +void svr_auth_pam(int valid_user); |
||
26 | |||
27 | #ifdef ENABLE_SVR_PUBKEY_OPTIONS |
||
28 | int svr_pubkey_allows_agentfwd(void); |
||
29 | --- a/svr-auth.c |
||
30 | +++ b/svr-auth.c |
||
31 | @@ -176,10 +176,8 @@ void recv_msg_userauth_request() { |
||
32 | if (methodlen == AUTH_METHOD_PASSWORD_LEN && |
||
33 | strncmp(methodname, AUTH_METHOD_PASSWORD, |
||
34 | AUTH_METHOD_PASSWORD_LEN) == 0) { |
||
35 | - if (valid_user) { |
||
36 | - svr_auth_password(); |
||
37 | - goto out; |
||
38 | - } |
||
39 | + svr_auth_password(valid_user); |
||
40 | + goto out; |
||
41 | } |
||
42 | } |
||
43 | #endif |
||
44 | @@ -191,10 +189,8 @@ void recv_msg_userauth_request() { |
||
45 | if (methodlen == AUTH_METHOD_PASSWORD_LEN && |
||
46 | strncmp(methodname, AUTH_METHOD_PASSWORD, |
||
47 | AUTH_METHOD_PASSWORD_LEN) == 0) { |
||
48 | - if (valid_user) { |
||
49 | - svr_auth_pam(); |
||
50 | - goto out; |
||
51 | - } |
||
52 | + svr_auth_pam(valid_user); |
||
53 | + goto out; |
||
54 | } |
||
55 | } |
||
56 | #endif |
||
57 | @@ -204,12 +200,7 @@ void recv_msg_userauth_request() { |
||
58 | if (methodlen == AUTH_METHOD_PUBKEY_LEN && |
||
59 | strncmp(methodname, AUTH_METHOD_PUBKEY, |
||
60 | AUTH_METHOD_PUBKEY_LEN) == 0) { |
||
61 | - if (valid_user) { |
||
62 | - svr_auth_pubkey(); |
||
63 | - } else { |
||
64 | - /* pubkey has no failure delay */ |
||
65 | - send_msg_userauth_failure(0, 0); |
||
66 | - } |
||
67 | + svr_auth_pubkey(valid_user); |
||
68 | goto out; |
||
69 | } |
||
70 | #endif |
||
71 | --- a/svr-authpam.c |
||
72 | +++ b/svr-authpam.c |
||
73 | @@ -178,13 +178,14 @@ pamConvFunc(int num_msg, |
||
74 | * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it |
||
75 | * gets very messy trying to send the interactive challenges, and read the |
||
76 | * interactive responses, over the network. */ |
||
77 | -void svr_auth_pam() { |
||
78 | +void svr_auth_pam(int valid_user) { |
||
79 | |||
80 | struct UserDataS userData = {NULL, NULL}; |
||
81 | struct pam_conv pamConv = { |
||
82 | pamConvFunc, |
||
83 | &userData /* submitted to pamvConvFunc as appdata_ptr */ |
||
84 | }; |
||
85 | + const char* printable_user = NULL; |
||
86 | |||
87 | pam_handle_t* pamHandlep = NULL; |
||
88 | |||
89 | @@ -204,12 +205,23 @@ void svr_auth_pam() { |
||
90 | |||
91 | password = buf_getstring(ses.payload, &passwordlen); |
||
92 | |||
93 | + /* We run the PAM conversation regardless of whether the username is valid |
||
94 | + in case the conversation function has an inherent delay. |
||
95 | + Use ses.authstate.username rather than ses.authstate.pw_name. |
||
96 | + After PAM succeeds we then check the valid_user flag too */ |
||
97 | + |
||
98 | /* used to pass data to the PAM conversation function - don't bother with |
||
99 | * strdup() etc since these are touched only by our own conversation |
||
100 | * function (above) which takes care of it */ |
||
101 | - userData.user = ses.authstate.pw_name; |
||
102 | + userData.user = ses.authstate.username; |
||
103 | userData.passwd = password; |
||
104 | |||
105 | + if (ses.authstate.pw_name) { |
||
106 | + printable_user = ses.authstate.pw_name; |
||
107 | + } else { |
||
108 | + printable_user = "<invalid username>"; |
||
109 | + } |
||
110 | + |
||
111 | /* Init pam */ |
||
112 | if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) { |
||
113 | dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", |
||
114 | @@ -236,7 +248,7 @@ void svr_auth_pam() { |
||
115 | rc, pam_strerror(pamHandlep, rc)); |
||
116 | dropbear_log(LOG_WARNING, |
||
117 | "Bad PAM password attempt for '%s' from %s", |
||
118 | - ses.authstate.pw_name, |
||
119 | + printable_user, |
||
120 | svr_ses.addrstring); |
||
121 | send_msg_userauth_failure(0, 1); |
||
122 | goto cleanup; |
||
123 | @@ -247,12 +259,18 @@ void svr_auth_pam() { |
||
124 | rc, pam_strerror(pamHandlep, rc)); |
||
125 | dropbear_log(LOG_WARNING, |
||
126 | "Bad PAM password attempt for '%s' from %s", |
||
127 | - ses.authstate.pw_name, |
||
128 | + printable_user, |
||
129 | svr_ses.addrstring); |
||
130 | send_msg_userauth_failure(0, 1); |
||
131 | goto cleanup; |
||
132 | } |
||
133 | |||
134 | + if (!valid_user) { |
||
135 | + /* PAM auth succeeded but the username isn't allowed in for another reason |
||
136 | + (checkusername() failed) */ |
||
137 | + send_msg_userauth_failure(0, 1); |
||
138 | + } |
||
139 | + |
||
140 | /* successful authentication */ |
||
141 | dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", |
||
142 | ses.authstate.pw_name, |
||
143 | --- a/svr-authpasswd.c |
||
144 | +++ b/svr-authpasswd.c |
||
145 | @@ -48,22 +48,14 @@ static int constant_time_strcmp(const ch |
||
146 | |||
147 | /* Process a password auth request, sending success or failure messages as |
||
148 | * appropriate */ |
||
149 | -void svr_auth_password() { |
||
150 | +void svr_auth_password(int valid_user) { |
||
151 | |||
152 | char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ |
||
153 | char * testcrypt = NULL; /* crypt generated from the user's password sent */ |
||
154 | - char * password; |
||
155 | + char * password = NULL; |
||
156 | unsigned int passwordlen; |
||
157 | - |
||
158 | unsigned int changepw; |
||
159 | |||
160 | - passwdcrypt = ses.authstate.pw_passwd; |
||
161 | - |
||
162 | -#ifdef DEBUG_HACKCRYPT |
||
163 | - /* debugging crypt for non-root testing with shadows */ |
||
164 | - passwdcrypt = DEBUG_HACKCRYPT; |
||
165 | -#endif |
||
166 | - |
||
167 | /* check if client wants to change password */ |
||
168 | changepw = buf_getbool(ses.payload); |
||
169 | if (changepw) { |
||
170 | @@ -73,12 +65,21 @@ void svr_auth_password() { |
||
171 | } |
||
172 | |||
173 | password = buf_getstring(ses.payload, &passwordlen); |
||
174 | - |
||
175 | - /* the first bytes of passwdcrypt are the salt */ |
||
176 | - testcrypt = crypt(password, passwdcrypt); |
||
177 | + if (valid_user) { |
||
178 | + /* the first bytes of passwdcrypt are the salt */ |
||
179 | + passwdcrypt = ses.authstate.pw_passwd; |
||
180 | + testcrypt = crypt(password, passwdcrypt); |
||
181 | + } |
||
182 | m_burn(password, passwordlen); |
||
183 | m_free(password); |
||
184 | |||
185 | + /* After we have got the payload contents we can exit if the username |
||
186 | + is invalid. Invalid users have already been logged. */ |
||
187 | + if (!valid_user) { |
||
188 | + send_msg_userauth_failure(0, 1); |
||
189 | + return; |
||
190 | + } |
||
191 | + |
||
192 | if (testcrypt == NULL) { |
||
193 | /* crypt() with an invalid salt like "!!" */ |
||
194 | dropbear_log(LOG_WARNING, "User account '%s' is locked", |
||
195 | --- a/svr-authpubkey.c |
||
196 | +++ b/svr-authpubkey.c |
||
197 | @@ -79,7 +79,7 @@ static int checkfileperm(char * filename |
||
198 | |||
199 | /* process a pubkey auth request, sending success or failure message as |
||
200 | * appropriate */ |
||
201 | -void svr_auth_pubkey() { |
||
202 | +void svr_auth_pubkey(int valid_user) { |
||
203 | |||
204 | unsigned char testkey; /* whether we're just checking if a key is usable */ |
||
205 | char* algo = NULL; /* pubkey algo */ |
||
206 | @@ -102,6 +102,15 @@ void svr_auth_pubkey() { |
||
207 | keybloblen = buf_getint(ses.payload); |
||
208 | keyblob = buf_getptr(ses.payload, keybloblen); |
||
209 | |||
210 | + if (!valid_user) { |
||
211 | + /* Return failure once we have read the contents of the packet |
||
212 | + required to validate a public key. |
||
213 | + Avoids blind user enumeration though it isn't possible to prevent |
||
214 | + testing for user existence if the public key is known */ |
||
215 | + send_msg_userauth_failure(0, 0); |
||
216 | + goto out; |
||
217 | + } |
||
218 | + |
||
219 | /* check if the key is valid */ |
||
220 | if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) { |
||
221 | send_msg_userauth_failure(0, 0); |