]> andersk Git - openssh.git/blob - auth2.c
- markus@cvs.openbsd.org 2002/05/25 18:51:07
[openssh.git] / auth2.c
1 /*
2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "includes.h"
26 RCSID("$OpenBSD: auth2.c,v 1.92 2002/05/25 18:51:07 markus Exp $");
27
28 #include "ssh2.h"
29 #include "xmalloc.h"
30 #include "packet.h"
31 #include "log.h"
32 #include "servconf.h"
33 #include "compat.h"
34 #include "auth.h"
35 #include "dispatch.h"
36 #include "pathnames.h"
37 #include "monitor_wrap.h"
38
39 /* import */
40 extern ServerOptions options;
41 extern u_char *session_id2;
42 extern int session_id2_len;
43
44 Authctxt *x_authctxt = NULL;
45 static int one = 1;
46
47 typedef struct Authmethod Authmethod;
48 struct Authmethod {
49         char    *name;
50         int     (*userauth)(Authctxt *authctxt);
51         int     *enabled;
52 };
53
54 /* protocol */
55
56 static void input_service_request(int, u_int32_t, void *);
57 static void input_userauth_request(int, u_int32_t, void *);
58
59 /* helper */
60 static Authmethod *authmethod_lookup(const char *);
61 static char *authmethods_get(void);
62 int user_key_allowed(struct passwd *, Key *);
63 int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
64
65 /* auth */
66
67 Authmethod authmethods[] = {
68         {"none",
69                 userauth_none,
70                 &one},
71         {"publickey",
72                 userauth_pubkey,
73                 &options.pubkey_authentication},
74         {"password",
75                 userauth_passwd,
76                 &options.password_authentication},
77         {"keyboard-interactive",
78                 userauth_kbdint,
79                 &options.kbd_interactive_authentication},
80         {"hostbased",
81                 userauth_hostbased,
82                 &options.hostbased_authentication},
83         {NULL, NULL, NULL}
84 };
85
86 /*
87  * loop until authctxt->success == TRUE
88  */
89
90 Authctxt *
91 do_authentication2(void)
92 {
93         Authctxt *authctxt = authctxt_new();
94
95         x_authctxt = authctxt;          /*XXX*/
96
97         /* challenge-response is implemented via keyboard interactive */
98         if (options.challenge_response_authentication)
99                 options.kbd_interactive_authentication = 1;
100         if (options.pam_authentication_via_kbd_int)
101                 options.kbd_interactive_authentication = 1;
102         if (use_privsep)
103                 options.pam_authentication_via_kbd_int = 0;
104
105         dispatch_init(&dispatch_protocol_error);
106         dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
107         dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
108
109         return (authctxt);
110 }
111
112 static void
113 input_service_request(int type, u_int32_t seq, void *ctxt)
114 {
115         Authctxt *authctxt = ctxt;
116         u_int len;
117         int accept = 0;
118         char *service = packet_get_string(&len);
119         packet_check_eom();
120
121         if (authctxt == NULL)
122                 fatal("input_service_request: no authctxt");
123
124         if (strcmp(service, "ssh-userauth") == 0) {
125                 if (!authctxt->success) {
126                         accept = 1;
127                         /* now we can handle user-auth requests */
128                         dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
129                 }
130         }
131         /* XXX all other service requests are denied */
132
133         if (accept) {
134                 packet_start(SSH2_MSG_SERVICE_ACCEPT);
135                 packet_put_cstring(service);
136                 packet_send();
137                 packet_write_wait();
138         } else {
139                 debug("bad service request %s", service);
140                 packet_disconnect("bad service request %s", service);
141         }
142         xfree(service);
143 }
144
145 static void
146 input_userauth_request(int type, u_int32_t seq, void *ctxt)
147 {
148         Authctxt *authctxt = ctxt;
149         Authmethod *m = NULL;
150         char *user, *service, *method, *style = NULL;
151         int authenticated = 0;
152
153         if (authctxt == NULL)
154                 fatal("input_userauth_request: no authctxt");
155
156         user = packet_get_string(NULL);
157         service = packet_get_string(NULL);
158         method = packet_get_string(NULL);
159         debug("userauth-request for user %s service %s method %s", user, service, method);
160         debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
161
162         if ((style = strchr(user, ':')) != NULL)
163                 *style++ = 0;
164
165         if (authctxt->attempt++ == 0) {
166                 /* setup auth context */
167                 authctxt->pw = PRIVSEP(getpwnamallow(user));
168                 if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
169                         authctxt->valid = 1;
170                         debug2("input_userauth_request: setting up authctxt for %s", user);
171 #ifdef USE_PAM
172                         PRIVSEP(start_pam(authctxt->pw->pw_name));
173 #endif
174                 } else {
175                         log("input_userauth_request: illegal user %s", user);
176 #ifdef USE_PAM
177                         PRIVSEP(start_pam("NOUSER"));
178 #endif
179                 }
180                 setproctitle("%s%s", authctxt->pw ? user : "unknown",
181                     use_privsep ? " [net]" : "");
182                 authctxt->user = xstrdup(user);
183                 authctxt->service = xstrdup(service);
184                 authctxt->style = style ? xstrdup(style) : NULL;
185                 if (use_privsep)
186                         mm_inform_authserv(service, style);
187         } else if (strcmp(user, authctxt->user) != 0 ||
188             strcmp(service, authctxt->service) != 0) {
189                 packet_disconnect("Change of username or service not allowed: "
190                     "(%s,%s) -> (%s,%s)",
191                     authctxt->user, authctxt->service, user, service);
192         }
193         /* reset state */
194         auth2_challenge_stop(authctxt);
195         authctxt->postponed = 0;
196
197         /* try to authenticate user */
198         m = authmethod_lookup(method);
199         if (m != NULL) {
200                 debug2("input_userauth_request: try method %s", method);
201                 authenticated = m->userauth(authctxt);
202         }
203         userauth_finish(authctxt, authenticated, method);
204
205         xfree(service);
206         xfree(user);
207         xfree(method);
208 }
209
210 void
211 userauth_finish(Authctxt *authctxt, int authenticated, char *method)
212 {
213         char *methods;
214
215         if (!authctxt->valid && authenticated)
216                 fatal("INTERNAL ERROR: authenticated invalid user %s",
217                     authctxt->user);
218
219         /* Special handling for root */
220         if (authenticated && authctxt->pw->pw_uid == 0 &&
221             !auth_root_allowed(method))
222                 authenticated = 0;
223
224 #ifdef USE_PAM
225         if (!use_privsep && authenticated && authctxt->user && 
226             !do_pam_account(authctxt->user, NULL))
227                 authenticated = 0;
228 #endif /* USE_PAM */
229
230         /* Log before sending the reply */
231         auth_log(authctxt, authenticated, method, " ssh2");
232
233         if (authctxt->postponed)
234                 return;
235
236         /* XXX todo: check if multiple auth methods are needed */
237         if (authenticated == 1) {
238                 /* turn off userauth */
239                 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
240                 packet_start(SSH2_MSG_USERAUTH_SUCCESS);
241                 packet_send();
242                 packet_write_wait();
243                 /* now we can break out */
244                 authctxt->success = 1;
245         } else {
246                 if (authctxt->failures++ > AUTH_FAIL_MAX) {
247 #ifdef WITH_AIXAUTHENTICATE
248                         loginfailed(authctxt->user,
249                             get_canonical_hostname(options.verify_reverse_mapping),
250                             "ssh");
251 #endif /* WITH_AIXAUTHENTICATE */
252                         packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
253                 }
254                 methods = authmethods_get();
255                 packet_start(SSH2_MSG_USERAUTH_FAILURE);
256                 packet_put_cstring(methods);
257                 packet_put_char(0);     /* XXX partial success, unused */
258                 packet_send();
259                 packet_write_wait();
260                 xfree(methods);
261         }
262 }
263
264 char *
265 auth2_read_banner(void)
266 {
267         struct stat st;
268         char *banner = NULL;
269         off_t len, n;
270         int fd;
271
272         if ((fd = open(options.banner, O_RDONLY)) == -1)
273                 return (NULL);
274         if (fstat(fd, &st) == -1) {
275                 close(fd);
276                 return (NULL);
277         }
278         len = st.st_size;
279         banner = xmalloc(len + 1);
280         n = atomicio(read, fd, banner, len);
281         close(fd);
282
283         if (n != len) {
284                 free(banner);
285                 return (NULL);
286         }
287         banner[n] = '\0';
288         
289         return (banner);
290 }
291
292 static void
293 userauth_banner(void)
294 {
295         char *banner = NULL;
296
297         if (options.banner == NULL || (datafellows & SSH_BUG_BANNER))
298                 return;
299
300         if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
301                 goto done;
302
303         packet_start(SSH2_MSG_USERAUTH_BANNER);
304         packet_put_cstring(banner);
305         packet_put_cstring("");         /* language, unused */
306         packet_send();
307         debug("userauth_banner: sent");
308 done:
309         if (banner)
310                 xfree(banner);
311         return;
312 }
313
314 static int
315 userauth_none(Authctxt *authctxt)
316 {
317         /* disable method "none", only allowed one time */
318         Authmethod *m = authmethod_lookup("none");
319         if (m != NULL)
320                 m->enabled = NULL;
321         packet_check_eom();
322         userauth_banner();
323
324         if (authctxt->valid == 0)
325                 return(0);
326
327 #ifdef HAVE_CYGWIN
328         if (check_nt_auth(1, authctxt->pw) == 0)
329                 return(0);
330 #endif
331         return PRIVSEP(auth_password(authctxt, ""));
332 }
333
334 static int
335 userauth_passwd(Authctxt *authctxt)
336 {
337         char *password;
338         int authenticated = 0;
339         int change;
340         u_int len;
341         change = packet_get_char();
342         if (change)
343                 log("password change not supported");
344         password = packet_get_string(&len);
345         packet_check_eom();
346         if (authctxt->valid &&
347 #ifdef HAVE_CYGWIN
348             check_nt_auth(1, authctxt->pw) &&
349 #endif
350             PRIVSEP(auth_password(authctxt, password)) == 1)
351                 authenticated = 1;
352         memset(password, 0, len);
353         xfree(password);
354         return authenticated;
355 }
356
357 static int
358 userauth_kbdint(Authctxt *authctxt)
359 {
360         int authenticated = 0;
361         char *lang, *devs;
362
363         lang = packet_get_string(NULL);
364         devs = packet_get_string(NULL);
365         packet_check_eom();
366
367         debug("keyboard-interactive devs %s", devs);
368
369         if (options.challenge_response_authentication)
370                 authenticated = auth2_challenge(authctxt, devs);
371
372 #ifdef USE_PAM
373         if (authenticated == 0 && options.pam_authentication_via_kbd_int)
374                 authenticated = auth2_pam(authctxt);
375 #endif
376         xfree(devs);
377         xfree(lang);
378 #ifdef HAVE_CYGWIN
379         if (check_nt_auth(0, authctxt->pw) == 0)
380                 return(0);
381 #endif
382         return authenticated;
383 }
384
385 static int
386 userauth_pubkey(Authctxt *authctxt)
387 {
388         Buffer b;
389         Key *key = NULL;
390         char *pkalg;
391         u_char *pkblob, *sig;
392         u_int alen, blen, slen;
393         int have_sig, pktype;
394         int authenticated = 0;
395
396         if (!authctxt->valid) {
397                 debug2("userauth_pubkey: disabled because of invalid user");
398                 return 0;
399         }
400         have_sig = packet_get_char();
401         if (datafellows & SSH_BUG_PKAUTH) {
402                 debug2("userauth_pubkey: SSH_BUG_PKAUTH");
403                 /* no explicit pkalg given */
404                 pkblob = packet_get_string(&blen);
405                 buffer_init(&b);
406                 buffer_append(&b, pkblob, blen);
407                 /* so we have to extract the pkalg from the pkblob */
408                 pkalg = buffer_get_string(&b, &alen);
409                 buffer_free(&b);
410         } else {
411                 pkalg = packet_get_string(&alen);
412                 pkblob = packet_get_string(&blen);
413         }
414         pktype = key_type_from_name(pkalg);
415         if (pktype == KEY_UNSPEC) {
416                 /* this is perfectly legal */
417                 log("userauth_pubkey: unsupported public key algorithm: %s",
418                     pkalg);
419                 goto done;
420         }
421         key = key_from_blob(pkblob, blen);
422         if (key == NULL) {
423                 error("userauth_pubkey: cannot decode key: %s", pkalg);
424                 goto done;
425         }
426         if (key->type != pktype) {
427                 error("userauth_pubkey: type mismatch for decoded key "
428                     "(received %d, expected %d)", key->type, pktype);
429                 goto done;
430         }
431         if (have_sig) {
432                 sig = packet_get_string(&slen);
433                 packet_check_eom();
434                 buffer_init(&b);
435                 if (datafellows & SSH_OLD_SESSIONID) {
436                         buffer_append(&b, session_id2, session_id2_len);
437                 } else {
438                         buffer_put_string(&b, session_id2, session_id2_len);
439                 }
440                 /* reconstruct packet */
441                 buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
442                 buffer_put_cstring(&b, authctxt->user);
443                 buffer_put_cstring(&b,
444                     datafellows & SSH_BUG_PKSERVICE ?
445                     "ssh-userauth" :
446                     authctxt->service);
447                 if (datafellows & SSH_BUG_PKAUTH) {
448                         buffer_put_char(&b, have_sig);
449                 } else {
450                         buffer_put_cstring(&b, "publickey");
451                         buffer_put_char(&b, have_sig);
452                         buffer_put_cstring(&b, pkalg);
453                 }
454                 buffer_put_string(&b, pkblob, blen);
455 #ifdef DEBUG_PK
456                 buffer_dump(&b);
457 #endif
458                 /* test for correct signature */
459                 authenticated = 0;
460                 if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
461                     PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
462                                 buffer_len(&b))) == 1)
463                         authenticated = 1;
464                 buffer_clear(&b);
465                 xfree(sig);
466         } else {
467                 debug("test whether pkalg/pkblob are acceptable");
468                 packet_check_eom();
469
470                 /* XXX fake reply and always send PK_OK ? */
471                 /*
472                  * XXX this allows testing whether a user is allowed
473                  * to login: if you happen to have a valid pubkey this
474                  * message is sent. the message is NEVER sent at all
475                  * if a user is not allowed to login. is this an
476                  * issue? -markus
477                  */
478                 if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
479                         packet_start(SSH2_MSG_USERAUTH_PK_OK);
480                         packet_put_string(pkalg, alen);
481                         packet_put_string(pkblob, blen);
482                         packet_send();
483                         packet_write_wait();
484                         authctxt->postponed = 1;
485                 }
486         }
487         if (authenticated != 1)
488                 auth_clear_options();
489 done:
490         debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
491         if (key != NULL)
492                 key_free(key);
493         xfree(pkalg);
494         xfree(pkblob);
495 #ifdef HAVE_CYGWIN
496         if (check_nt_auth(0, authctxt->pw) == 0)
497                 return(0);
498 #endif
499         return authenticated;
500 }
501
502 static int
503 userauth_hostbased(Authctxt *authctxt)
504 {
505         Buffer b;
506         Key *key = NULL;
507         char *pkalg, *cuser, *chost, *service;
508         u_char *pkblob, *sig;
509         u_int alen, blen, slen;
510         int pktype;
511         int authenticated = 0;
512
513         if (!authctxt->valid) {
514                 debug2("userauth_hostbased: disabled because of invalid user");
515                 return 0;
516         }
517         pkalg = packet_get_string(&alen);
518         pkblob = packet_get_string(&blen);
519         chost = packet_get_string(NULL);
520         cuser = packet_get_string(NULL);
521         sig = packet_get_string(&slen);
522
523         debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
524             cuser, chost, pkalg, slen);
525 #ifdef DEBUG_PK
526         debug("signature:");
527         buffer_init(&b);
528         buffer_append(&b, sig, slen);
529         buffer_dump(&b);
530         buffer_free(&b);
531 #endif
532         pktype = key_type_from_name(pkalg);
533         if (pktype == KEY_UNSPEC) {
534                 /* this is perfectly legal */
535                 log("userauth_hostbased: unsupported "
536                     "public key algorithm: %s", pkalg);
537                 goto done;
538         }
539         key = key_from_blob(pkblob, blen);
540         if (key == NULL) {
541                 error("userauth_hostbased: cannot decode key: %s", pkalg);
542                 goto done;
543         }
544         if (key->type != pktype) {
545                 error("userauth_hostbased: type mismatch for decoded key "
546                     "(received %d, expected %d)", key->type, pktype);
547                 goto done;
548         }
549         service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
550             authctxt->service;
551         buffer_init(&b);
552         buffer_put_string(&b, session_id2, session_id2_len);
553         /* reconstruct packet */
554         buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
555         buffer_put_cstring(&b, authctxt->user);
556         buffer_put_cstring(&b, service);
557         buffer_put_cstring(&b, "hostbased");
558         buffer_put_string(&b, pkalg, alen);
559         buffer_put_string(&b, pkblob, blen);
560         buffer_put_cstring(&b, chost);
561         buffer_put_cstring(&b, cuser);
562 #ifdef DEBUG_PK
563         buffer_dump(&b);
564 #endif
565         /* test for allowed key and correct signature */
566         authenticated = 0;
567         if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
568             PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
569                         buffer_len(&b))) == 1)
570                 authenticated = 1;
571
572         buffer_clear(&b);
573 done:
574         debug2("userauth_hostbased: authenticated %d", authenticated);
575         if (key != NULL)
576                 key_free(key);
577         xfree(pkalg);
578         xfree(pkblob);
579         xfree(cuser);
580         xfree(chost);
581         xfree(sig);
582         return authenticated;
583 }
584
585 /* get current user */
586
587 struct passwd*
588 auth_get_user(void)
589 {
590         return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
591 }
592
593 #define DELIM   ","
594
595 static char *
596 authmethods_get(void)
597 {
598         Authmethod *method = NULL;
599         Buffer b;
600         char *list;
601
602         buffer_init(&b);
603         for (method = authmethods; method->name != NULL; method++) {
604                 if (strcmp(method->name, "none") == 0)
605                         continue;
606                 if (method->enabled != NULL && *(method->enabled) != 0) {
607                         if (buffer_len(&b) > 0)
608                                 buffer_append(&b, ",", 1);
609                         buffer_append(&b, method->name, strlen(method->name));
610                 }
611         }
612         buffer_append(&b, "\0", 1);
613         list = xstrdup(buffer_ptr(&b));
614         buffer_free(&b);
615         return list;
616 }
617
618 static Authmethod *
619 authmethod_lookup(const char *name)
620 {
621         Authmethod *method = NULL;
622         if (name != NULL)
623                 for (method = authmethods; method->name != NULL; method++)
624                         if (method->enabled != NULL &&
625                             *(method->enabled) != 0 &&
626                             strcmp(name, method->name) == 0)
627                                 return method;
628         debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
629         return NULL;
630 }
This page took 0.139195 seconds and 5 git commands to generate.