Mercurial > dropbear
comparison svr-auth.c @ 1546:bb8eaa26bc93 fuzz
merge from main
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 26 Feb 2018 22:44:48 +0800 |
parents | 5916af64acd4 51df3d53b050 |
children | 61a793b6e471 |
comparison
equal
deleted
inserted
replaced
1530:63fa53d3b6c7 | 1546:bb8eaa26bc93 |
---|---|
23 * SOFTWARE. */ | 23 * SOFTWARE. */ |
24 | 24 |
25 /* This file (auth.c) handles authentication requests, passing it to the | 25 /* This file (auth.c) handles authentication requests, passing it to the |
26 * particular type (auth-passwd, auth-pubkey). */ | 26 * particular type (auth-passwd, auth-pubkey). */ |
27 | 27 |
28 #include <limits.h> | |
29 | |
28 #include "includes.h" | 30 #include "includes.h" |
29 #include "dbutil.h" | 31 #include "dbutil.h" |
30 #include "session.h" | 32 #include "session.h" |
31 #include "buffer.h" | 33 #include "buffer.h" |
32 #include "ssh.h" | 34 #include "ssh.h" |
33 #include "packet.h" | 35 #include "packet.h" |
34 #include "auth.h" | 36 #include "auth.h" |
35 #include "runopts.h" | 37 #include "runopts.h" |
36 #include "dbrandom.h" | 38 #include "dbrandom.h" |
37 | 39 |
38 static void authclear(void); | 40 static int checkusername(const char *username, unsigned int userlen); |
39 static int checkusername(char *username, unsigned int userlen); | |
40 | 41 |
41 /* initialise the first time for a session, resetting all parameters */ | 42 /* initialise the first time for a session, resetting all parameters */ |
42 void svr_authinitialise() { | 43 void svr_authinitialise() { |
43 | |
44 ses.authstate.failcount = 0; | |
45 ses.authstate.pw_name = NULL; | |
46 ses.authstate.pw_dir = NULL; | |
47 ses.authstate.pw_shell = NULL; | |
48 ses.authstate.pw_passwd = NULL; | |
49 authclear(); | |
50 | |
51 } | |
52 | |
53 /* Reset the auth state, but don't reset the failcount. This is for if the | |
54 * user decides to try with a different username etc, and is also invoked | |
55 * on initialisation */ | |
56 static void authclear() { | |
57 | |
58 memset(&ses.authstate, 0, sizeof(ses.authstate)); | 44 memset(&ses.authstate, 0, sizeof(ses.authstate)); |
59 #if DROPBEAR_SVR_PUBKEY_AUTH | 45 #if DROPBEAR_SVR_PUBKEY_AUTH |
60 ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; | 46 ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; |
61 #endif | 47 #endif |
62 #if DROPBEAR_SVR_PASSWORD_AUTH || DROPBEAR_SVR_PAM_AUTH | 48 #if DROPBEAR_SVR_PASSWORD_AUTH || DROPBEAR_SVR_PAM_AUTH |
63 if (!svr_opts.noauthpass) { | 49 if (!svr_opts.noauthpass) { |
64 ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; | 50 ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; |
65 } | 51 } |
66 #endif | 52 #endif |
67 if (ses.authstate.pw_name) { | |
68 m_free(ses.authstate.pw_name); | |
69 } | |
70 if (ses.authstate.pw_shell) { | |
71 m_free(ses.authstate.pw_shell); | |
72 } | |
73 if (ses.authstate.pw_dir) { | |
74 m_free(ses.authstate.pw_dir); | |
75 } | |
76 if (ses.authstate.pw_passwd) { | |
77 m_free(ses.authstate.pw_passwd); | |
78 } | |
79 | |
80 } | 53 } |
81 | 54 |
82 /* Send a banner message if specified to the client. The client might | 55 /* Send a banner message if specified to the client. The client might |
83 * ignore this, but possibly serves as a legal "no trespassing" sign */ | 56 * ignore this, but possibly serves as a legal "no trespassing" sign */ |
84 void send_msg_userauth_banner(const buffer *banner) { | 57 void send_msg_userauth_banner(const buffer *banner) { |
222 m_free(username); | 195 m_free(username); |
223 m_free(servicename); | 196 m_free(servicename); |
224 m_free(methodname); | 197 m_free(methodname); |
225 } | 198 } |
226 | 199 |
200 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ | |
201 static int check_group_membership(gid_t check_gid, const char* username, gid_t user_gid) { | |
202 int ngroups, i, ret; | |
203 gid_t *grouplist = NULL; | |
204 int match = DROPBEAR_FAILURE; | |
205 | |
206 for (ngroups = 32; ngroups <= DROPBEAR_NGROUP_MAX; ngroups *= 2) { | |
207 grouplist = m_malloc(sizeof(gid_t) * ngroups); | |
208 | |
209 /* BSD returns ret==0 on success. Linux returns ret==ngroups on success */ | |
210 ret = getgrouplist(username, user_gid, grouplist, &ngroups); | |
211 if (ret >= 0) { | |
212 break; | |
213 } | |
214 m_free(grouplist); | |
215 grouplist = NULL; | |
216 } | |
217 | |
218 if (!grouplist) { | |
219 dropbear_log(LOG_ERR, "Too many groups for user '%s'", username); | |
220 return DROPBEAR_FAILURE; | |
221 } | |
222 | |
223 for (i = 0; i < ngroups; i++) { | |
224 if (grouplist[i] == check_gid) { | |
225 match = DROPBEAR_SUCCESS; | |
226 break; | |
227 } | |
228 } | |
229 m_free(grouplist); | |
230 | |
231 return match; | |
232 } | |
233 | |
227 | 234 |
228 /* Check that the username exists and isn't disallowed (root), and has a valid shell. | 235 /* Check that the username exists and isn't disallowed (root), and has a valid shell. |
229 * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */ | 236 * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */ |
230 static int checkusername(char *username, unsigned int userlen) { | 237 static int checkusername(const char *username, unsigned int userlen) { |
231 | 238 |
232 char* listshell = NULL; | 239 char* listshell = NULL; |
233 char* usershell = NULL; | 240 char* usershell = NULL; |
234 uid_t uid; | 241 uid_t uid; |
242 | |
235 TRACE(("enter checkusername")) | 243 TRACE(("enter checkusername")) |
236 if (userlen > MAX_USERNAME_LEN) { | 244 if (userlen > MAX_USERNAME_LEN) { |
237 return DROPBEAR_FAILURE; | 245 return DROPBEAR_FAILURE; |
238 } | 246 } |
239 | 247 |
240 /* new user or username has changed */ | 248 if (strlen(username) != userlen) { |
241 if (ses.authstate.username == NULL || | 249 dropbear_exit("Attempted username with a null byte from %s", |
242 strcmp(username, ses.authstate.username) != 0) { | 250 svr_ses.addrstring); |
243 /* the username needs resetting */ | 251 } |
244 if (ses.authstate.username != NULL) { | 252 |
245 dropbear_log(LOG_WARNING, "Client trying multiple usernames from %s", | 253 if (ses.authstate.username == NULL) { |
246 svr_ses.addrstring); | 254 /* first request */ |
247 m_free(ses.authstate.username); | 255 fill_passwd(username); |
248 } | 256 ses.authstate.username = m_strdup(username); |
249 authclear(); | 257 } else { |
250 fill_passwd(username); | 258 /* check username hasn't changed */ |
251 ses.authstate.username = m_strdup(username); | 259 if (strcmp(username, ses.authstate.username) != 0) { |
260 dropbear_exit("Client trying multiple usernames from %s", | |
261 svr_ses.addrstring); | |
262 } | |
263 } | |
264 | |
265 /* avoids cluttering logs with repeated failure messages from | |
266 consecutive authentication requests in a sesssion */ | |
267 if (ses.authstate.checkusername_failed) { | |
268 TRACE(("checkusername: returning cached failure")) | |
269 return DROPBEAR_FAILURE; | |
252 } | 270 } |
253 | 271 |
254 /* check that user exists */ | 272 /* check that user exists */ |
255 if (!ses.authstate.pw_name) { | 273 if (!ses.authstate.pw_name) { |
256 TRACE(("leave checkusername: user '%s' doesn't exist", username)) | 274 TRACE(("leave checkusername: user '%s' doesn't exist", username)) |
257 dropbear_log(LOG_WARNING, | 275 dropbear_log(LOG_WARNING, |
258 "Login attempt for nonexistent user from %s", | 276 "Login attempt for nonexistent user from %s", |
259 svr_ses.addrstring); | 277 svr_ses.addrstring); |
278 ses.authstate.checkusername_failed = 1; | |
260 return DROPBEAR_FAILURE; | 279 return DROPBEAR_FAILURE; |
261 } | 280 } |
262 | 281 |
263 /* check if we are running as non-root, and login user is different from the server */ | 282 /* check if we are running as non-root, and login user is different from the server */ |
264 uid = geteuid(); | 283 uid = geteuid(); |
266 TRACE(("running as nonroot, only server uid is allowed")) | 285 TRACE(("running as nonroot, only server uid is allowed")) |
267 dropbear_log(LOG_WARNING, | 286 dropbear_log(LOG_WARNING, |
268 "Login attempt with wrong user %s from %s", | 287 "Login attempt with wrong user %s from %s", |
269 ses.authstate.pw_name, | 288 ses.authstate.pw_name, |
270 svr_ses.addrstring); | 289 svr_ses.addrstring); |
290 ses.authstate.checkusername_failed = 1; | |
271 return DROPBEAR_FAILURE; | 291 return DROPBEAR_FAILURE; |
272 } | 292 } |
273 | 293 |
274 /* check for non-root if desired */ | 294 /* check for non-root if desired */ |
275 if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) { | 295 if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) { |
276 TRACE(("leave checkusername: root login disabled")) | 296 TRACE(("leave checkusername: root login disabled")) |
277 dropbear_log(LOG_WARNING, "root login rejected"); | 297 dropbear_log(LOG_WARNING, "root login rejected"); |
278 return DROPBEAR_FAILURE; | 298 ses.authstate.checkusername_failed = 1; |
299 return DROPBEAR_FAILURE; | |
300 } | |
301 | |
302 /* check for login restricted to certain group if desired */ | |
303 if (svr_opts.restrict_group) { | |
304 if (check_group_membership(svr_opts.restrict_group_gid, | |
305 ses.authstate.pw_name, ses.authstate.pw_gid) == DROPBEAR_FAILURE) { | |
306 dropbear_log(LOG_WARNING, | |
307 "Logins are restricted to the group %s but user '%s' is not a member", | |
308 svr_opts.restrict_group, ses.authstate.pw_name); | |
309 ses.authstate.checkusername_failed = 1; | |
310 return DROPBEAR_FAILURE; | |
311 } | |
279 } | 312 } |
280 | 313 |
281 TRACE(("shell is %s", ses.authstate.pw_shell)) | 314 TRACE(("shell is %s", ses.authstate.pw_shell)) |
282 | 315 |
283 /* check that the shell is set */ | 316 /* check that the shell is set */ |
299 } | 332 } |
300 } | 333 } |
301 /* no matching shell */ | 334 /* no matching shell */ |
302 endusershell(); | 335 endusershell(); |
303 TRACE(("no matching shell")) | 336 TRACE(("no matching shell")) |
337 ses.authstate.checkusername_failed = 1; | |
304 dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected", | 338 dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected", |
305 ses.authstate.pw_name); | 339 ses.authstate.pw_name); |
306 return DROPBEAR_FAILURE; | 340 return DROPBEAR_FAILURE; |
307 | 341 |
308 goodshell: | 342 goodshell: |