]> andersk Git - openssh.git/blob - auth2.c
Please grep through the source and look for 'ISSUE' comments and verify
[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.28 2001/01/18 17:00:00 markus Exp $");
27
28 #ifdef HAVE_OSF_SIA
29 # include <sia.h>
30 # include <siad.h>
31 #endif
32
33 #include <openssl/dsa.h>
34 #include <openssl/rsa.h>
35 #include <openssl/evp.h>
36
37 #include "xmalloc.h"
38 #include "rsa.h"
39 #include "ssh.h"
40 #include "pty.h"
41 #include "packet.h"
42 #include "buffer.h"
43 #include "servconf.h"
44 #include "compat.h"
45 #include "channels.h"
46 #include "bufaux.h"
47 #include "ssh2.h"
48 #include "auth.h"
49 #include "session.h"
50 #include "dispatch.h"
51 #include "auth.h"
52 #include "key.h"
53 #include "kex.h"
54
55 #include "uidswap.h"
56 #include "auth-options.h"
57
58 /* import */
59 extern ServerOptions options;
60 extern u_char *session_id2;
61 extern int session_id2_len;
62
63 #ifdef WITH_AIXAUTHENTICATE
64 extern char *aixloginmsg;
65 #endif
66 #ifdef HAVE_OSF_SIA
67 extern int saved_argc;
68 extern char **saved_argv;
69 #endif
70
71 static Authctxt *x_authctxt = NULL;
72 static int one = 1;
73
74 typedef struct Authmethod Authmethod;
75 struct Authmethod {
76         char    *name;
77         int     (*userauth)(Authctxt *authctxt);
78         int     *enabled;
79 };
80
81 /* protocol */
82
83 void    input_service_request(int type, int plen, void *ctxt);
84 void    input_userauth_request(int type, int plen, void *ctxt);
85 void    protocol_error(int type, int plen, void *ctxt);
86
87 /* helper */
88 Authmethod      *authmethod_lookup(const char *name);
89 struct passwd   *pwcopy(struct passwd *pw);
90 int     user_key_allowed(struct passwd *pw, Key *key);
91 char    *authmethods_get(void);
92
93 /* auth */
94 void    userauth_banner(void);
95 int     userauth_none(Authctxt *authctxt);
96 int     userauth_passwd(Authctxt *authctxt);
97 int     userauth_pubkey(Authctxt *authctxt);
98 int     userauth_kbdint(Authctxt *authctxt);
99
100 Authmethod authmethods[] = {
101         {"none",
102                 userauth_none,
103                 &one},
104         {"publickey",
105                 userauth_pubkey,
106                 &options.pubkey_authentication},
107         {"keyboard-interactive",
108                 userauth_kbdint,
109                 &options.kbd_interactive_authentication},
110         {"password",
111                 userauth_passwd,
112                 &options.password_authentication},
113         {NULL, NULL, NULL}
114 };
115
116 /*
117  * loop until authctxt->success == TRUE
118  */
119
120 void
121 do_authentication2()
122 {
123         Authctxt *authctxt = authctxt_new();
124
125         x_authctxt = authctxt;          /*XXX*/
126
127 #ifdef AFS
128         /* If machine has AFS, set process authentication group. */
129         if (k_hasafs()) {
130                 k_setpag();
131                 k_unlog();
132         }
133 #endif
134         dispatch_init(&protocol_error);
135         dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
136         dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
137         do_authenticated2(authctxt);
138 }
139
140 void
141 protocol_error(int type, int plen, void *ctxt)
142 {
143         log("auth: protocol error: type %d plen %d", type, plen);
144         packet_start(SSH2_MSG_UNIMPLEMENTED);
145         packet_put_int(0);
146         packet_send();
147         packet_write_wait();
148 }
149
150 void
151 input_service_request(int type, int plen, void *ctxt)
152 {
153         Authctxt *authctxt = ctxt;
154         u_int len;
155         int accept = 0;
156         char *service = packet_get_string(&len);
157         packet_done();
158
159         if (authctxt == NULL)
160                 fatal("input_service_request: no authctxt");
161
162         if (strcmp(service, "ssh-userauth") == 0) {
163                 if (!authctxt->success) {
164                         accept = 1;
165                         /* now we can handle user-auth requests */
166                         dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
167                 }
168         }
169         /* XXX all other service requests are denied */
170
171         if (accept) {
172                 packet_start(SSH2_MSG_SERVICE_ACCEPT);
173                 packet_put_cstring(service);
174                 packet_send();
175                 packet_write_wait();
176         } else {
177                 debug("bad service request %s", service);
178                 packet_disconnect("bad service request %s", service);
179         }
180         xfree(service);
181 }
182
183 void
184 input_userauth_request(int type, int plen, void *ctxt)
185 {
186         Authctxt *authctxt = ctxt;
187         Authmethod *m = NULL;
188         char *user, *service, *method, *style = NULL;
189         int authenticated = 0;
190
191         if (authctxt == NULL)
192                 fatal("input_userauth_request: no authctxt");
193
194         user = packet_get_string(NULL);
195         service = packet_get_string(NULL);
196         method = packet_get_string(NULL);
197         debug("userauth-request for user %s service %s method %s", user, service, method);
198         debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
199
200         if ((style = strchr(user, ':')) != NULL)
201                 *style++ = 0;
202
203         if (authctxt->attempt++ == 0) {
204                 /* setup auth context */
205                 struct passwd *pw = NULL;
206                 setproctitle("%s", user);
207                 pw = getpwnam(user);
208                 if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
209                         authctxt->pw = pwcopy(pw);
210                         authctxt->valid = 1;
211                         debug2("input_userauth_request: setting up authctxt for %s", user);
212 #ifdef USE_PAM
213                         start_pam(pw);
214 #endif
215                 } else {
216                         log("input_userauth_request: illegal user %s", user);
217                 }
218                 authctxt->user = xstrdup(user);
219                 authctxt->service = xstrdup(service);
220                 authctxt->style = style ? xstrdup(style) : NULL; /* currently unused */
221         } else if (authctxt->valid) {
222                 if (strcmp(user, authctxt->user) != 0 ||
223                     strcmp(service, authctxt->service) != 0) {
224                         log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)",
225                             user, service, authctxt->user, authctxt->service);
226                         authctxt->valid = 0;
227                 }
228         }
229         /* reset state */
230         dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, &protocol_error);
231         authctxt->postponed = 0;
232
233         /* try to authenticate user */
234         m = authmethod_lookup(method);
235         if (m != NULL) {
236                 debug2("input_userauth_request: try method %s", method);
237                 authenticated = m->userauth(authctxt);
238         }
239         if (!authctxt->valid && authenticated)
240                 fatal("INTERNAL ERROR: authenticated invalid user %s",
241                     authctxt->user);
242
243         /* Special handling for root */
244         if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed())
245                 authenticated = 0;
246
247 #ifdef USE_PAM
248         if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
249                 authenticated = 0;
250 #endif /* USE_PAM */
251
252         /* Log before sending the reply */
253         auth_log(authctxt, authenticated, method, " ssh2");
254
255         if (!authctxt->postponed)
256                 userauth_reply(authctxt, authenticated);
257
258         xfree(service);
259         xfree(user);
260         xfree(method);
261 }
262
263 void
264 userauth_banner(void)
265 {
266         struct stat st;
267         char *banner = NULL;
268         off_t len, n;
269         int fd;
270
271         if (options.banner == NULL || (datafellows & SSH_BUG_BANNER))
272                 return;
273         if ((fd = open(options.banner, O_RDONLY)) < 0) {
274                 error("userauth_banner: open %s failed: %s",
275                     options.banner, strerror(errno));
276                 return;
277         }
278         if (fstat(fd, &st) < 0)
279                 goto done;
280         len = st.st_size;
281         banner = xmalloc(len + 1);
282         if ((n = read(fd, banner, len)) < 0)
283                 goto done;
284         banner[n] = '\0';
285         packet_start(SSH2_MSG_USERAUTH_BANNER);
286         packet_put_cstring(banner);
287         packet_put_cstring("");         /* language, unused */
288         packet_send();
289         debug("userauth_banner: sent");
290 done:
291         if (banner)
292                 xfree(banner);
293         close(fd);
294         return;
295 }
296
297 void   
298 userauth_reply(Authctxt *authctxt, int authenticated)
299 {
300         char *methods;
301
302         /* XXX todo: check if multiple auth methods are needed */
303         if (authenticated) {
304 #ifdef WITH_AIXAUTHENTICATE
305                 /* We don't have a pty yet, so just label the line as "ssh" */
306                 if (loginsuccess(authctxt->user?authctxt->user:"NOUSER", 
307                         get_canonical_hostname(), "ssh", &aixloginmsg) < 0)
308                         aixloginmsg = NULL;
309 #endif /* WITH_AIXAUTHENTICATE */
310                 /* turn off userauth */
311                 dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
312                 packet_start(SSH2_MSG_USERAUTH_SUCCESS);
313                 packet_send();
314                 packet_write_wait();
315                 /* now we can break out */
316                 authctxt->success = 1;
317         } else {
318                 if (authctxt->failures++ > AUTH_FAIL_MAX)
319                         packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
320                 methods = authmethods_get();
321                 packet_start(SSH2_MSG_USERAUTH_FAILURE);
322                 packet_put_cstring(methods);
323                 packet_put_char(0);     /* XXX partial success, unused */
324                 packet_send();
325                 packet_write_wait();
326                 xfree(methods);
327         }
328 }
329
330 int
331 userauth_none(Authctxt *authctxt)
332 {
333         /* disable method "none", only allowed one time */
334         Authmethod *m = authmethod_lookup("none");
335         if (m != NULL)
336                 m->enabled = NULL;
337         packet_done();
338         userauth_banner();
339
340         if (authctxt->valid == 0)
341                 return(0);
342                 
343 #ifdef HAVE_CYGWIN
344         if (check_nt_auth(1, authctxt->pw->pw_uid) == 0)
345                 return(0);
346 #endif
347 #ifdef USE_PAM
348         return auth_pam_password(authctxt->pw, "");
349 #elif defined(HAVE_OSF_SIA)
350         return (sia_validate_user(NULL, saved_argc, saved_argv, 
351                 get_canonical_hostname(), authctxt->user?authctxt->user:"NOUSER", 
352                         NULL, 0, NULL, "") == SIASUCCESS);
353 #else /* !HAVE_OSF_SIA && !USE_PAM */
354         return auth_password(authctxt->pw, "");
355 #endif /* USE_PAM */
356 }
357
358 int
359 userauth_passwd(Authctxt *authctxt)
360 {
361         char *password;
362         int authenticated = 0;
363         int change;
364         u_int len;
365         change = packet_get_char();
366         if (change)
367                 log("password change not supported");
368         password = packet_get_string(&len);
369         packet_done();
370         if (authctxt->valid &&
371 #ifdef HAVE_CYGWIN
372                 check_nt_auth(1, authctxt->pw->pw_uid) &&
373 #endif
374 #ifdef USE_PAM
375             auth_pam_password(authctxt->pw, password) == 1)
376 #elif defined(HAVE_OSF_SIA)
377             sia_validate_user(NULL, saved_argc, saved_argv, 
378                         get_canonical_hostname(), authctxt->user?authctxt->user:"NOUSER", 
379                         NULL, 0, NULL, password) == SIASUCCESS)
380 #else /* !USE_PAM && !HAVE_OSF_SIA */
381             auth_password(authctxt->pw, password) == 1)
382 #endif /* USE_PAM */
383                 authenticated = 1;
384         memset(password, 0, len);
385         xfree(password);
386         return authenticated;
387 }
388
389 int
390 userauth_kbdint(Authctxt *authctxt)
391 {
392         int authenticated = 0;
393         char *lang = NULL;
394         char *devs = NULL;
395
396         lang = packet_get_string(NULL);
397         devs = packet_get_string(NULL);
398         packet_done();
399
400         debug("keyboard-interactive language %s devs %s", lang, devs);
401
402         authenticated = auth2_challenge(authctxt, devs);
403
404 #ifdef USE_PAM
405         if (authenticated == 0)
406                 authenticated = auth2_pam(authctxt);
407 #endif
408         xfree(lang);
409         xfree(devs);
410 #ifdef HAVE_CYGWIN
411         if (check_nt_auth(0, authctxt->pw->pw_uid) == 0)
412                 return(0);
413 #endif
414         return authenticated;
415 }
416
417 int
418 userauth_pubkey(Authctxt *authctxt)
419 {
420         Buffer b;
421         Key *key;
422         char *pkalg, *pkblob, *sig;
423         u_int alen, blen, slen;
424         int have_sig, pktype;
425         int authenticated = 0;
426
427         if (!authctxt->valid) {
428                 debug2("userauth_pubkey: disabled because of invalid user");
429                 return 0;
430         }
431         have_sig = packet_get_char();
432         if (datafellows & SSH_BUG_PKAUTH) {
433                 debug2("userauth_pubkey: SSH_BUG_PKAUTH");
434                 /* no explicit pkalg given */
435                 pkblob = packet_get_string(&blen);
436                 buffer_init(&b);
437                 buffer_append(&b, pkblob, blen);
438                 /* so we have to extract the pkalg from the pkblob */
439                 pkalg = buffer_get_string(&b, &alen);
440                 buffer_free(&b);
441         } else {
442                 pkalg = packet_get_string(&alen);
443                 pkblob = packet_get_string(&blen);
444         }
445         pktype = key_type_from_name(pkalg);
446         if (pktype == KEY_UNSPEC) {
447                 /* this is perfectly legal */
448                 log("userauth_pubkey: unsupported public key algorithm: %s", pkalg);
449                 xfree(pkalg);
450                 xfree(pkblob);
451                 return 0;
452         }
453         key = key_from_blob(pkblob, blen);
454         if (key != NULL) {
455                 if (have_sig) {
456                         sig = packet_get_string(&slen);
457                         packet_done();
458                         buffer_init(&b);
459                         if (datafellows & SSH_OLD_SESSIONID) {
460                                 buffer_append(&b, session_id2, session_id2_len);
461                         } else {
462                                 buffer_put_string(&b, session_id2, session_id2_len);
463                         }
464                         /* reconstruct packet */
465                         buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
466                         buffer_put_cstring(&b, authctxt->user);
467                         buffer_put_cstring(&b,
468                             datafellows & SSH_BUG_PKSERVICE ?
469                             "ssh-userauth" :
470                             authctxt->service);
471                         if (datafellows & SSH_BUG_PKAUTH) {
472                                 buffer_put_char(&b, have_sig);
473                         } else {
474                                 buffer_put_cstring(&b, "publickey");
475                                 buffer_put_char(&b, have_sig);
476                                 buffer_put_cstring(&b, key_ssh_name(key));
477                         }
478                         buffer_put_string(&b, pkblob, blen);
479 #ifdef DEBUG_PK
480                         buffer_dump(&b);
481 #endif
482                         /* test for correct signature */
483                         if (user_key_allowed(authctxt->pw, key) &&
484                             key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
485                                 authenticated = 1;
486                         buffer_clear(&b);
487                         xfree(sig);
488                 } else {
489                         debug("test whether pkalg/pkblob are acceptable");
490                         packet_done();
491
492                         /* XXX fake reply and always send PK_OK ? */
493                         /*
494                          * XXX this allows testing whether a user is allowed
495                          * to login: if you happen to have a valid pubkey this
496                          * message is sent. the message is NEVER sent at all
497                          * if a user is not allowed to login. is this an
498                          * issue? -markus
499                          */
500                         if (user_key_allowed(authctxt->pw, key)) {
501                                 packet_start(SSH2_MSG_USERAUTH_PK_OK);
502                                 packet_put_string(pkalg, alen);
503                                 packet_put_string(pkblob, blen);
504                                 packet_send();
505                                 packet_write_wait();
506                                 authenticated = -1;
507                         }
508                 }
509                 if (authenticated != 1)
510                         auth_clear_options();
511                 key_free(key);
512         }
513         debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
514         xfree(pkalg);
515         xfree(pkblob);
516 #ifdef HAVE_CYGWIN
517         if (check_nt_auth(0, authctxt->pw->pw_uid) == 0)
518                 return(0);
519 #endif
520         return authenticated;
521 }
522
523 /* get current user */
524
525 struct passwd*
526 auth_get_user(void)
527 {
528         return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
529 }
530
531 #define DELIM   ","
532
533 char *
534 authmethods_get(void)
535 {
536         Authmethod *method = NULL;
537         u_int size = 0;
538         char *list;
539
540         for (method = authmethods; method->name != NULL; method++) {
541                 if (strcmp(method->name, "none") == 0)
542                         continue;
543                 if (method->enabled != NULL && *(method->enabled) != 0) {
544                         if (size != 0)
545                                 size += strlen(DELIM);
546                         size += strlen(method->name);
547                 }
548         }
549         size++;                 /* trailing '\0' */
550         list = xmalloc(size);
551         list[0] = '\0';
552
553         for (method = authmethods; method->name != NULL; method++) {
554                 if (strcmp(method->name, "none") == 0)
555                         continue;
556                 if (method->enabled != NULL && *(method->enabled) != 0) {
557                         if (list[0] != '\0')
558                                 strlcat(list, DELIM, size);
559                         strlcat(list, method->name, size);
560                 }
561         }
562         return list;
563 }
564
565 Authmethod *
566 authmethod_lookup(const char *name)
567 {
568         Authmethod *method = NULL;
569         if (name != NULL)
570                 for (method = authmethods; method->name != NULL; method++)
571                         if (method->enabled != NULL &&
572                             *(method->enabled) != 0 &&
573                             strcmp(name, method->name) == 0)
574                                 return method;
575         debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
576         return NULL;
577 }
578
579 /* return 1 if user allows given key */
580 int
581 user_key_allowed(struct passwd *pw, Key *key)
582 {
583         char line[8192], file[1024];
584         int found_key = 0;
585         FILE *f;
586         u_long linenum = 0;
587         struct stat st;
588         Key *found;
589
590         if (pw == NULL)
591                 return 0;
592
593         /* Temporarily use the user's uid. */
594         temporarily_use_uid(pw->pw_uid);
595
596         /* The authorized keys. */
597         snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
598             SSH_USER_PERMITTED_KEYS2);
599
600         /* Fail quietly if file does not exist */
601         if (stat(file, &st) < 0) {
602                 /* Restore the privileged uid. */
603                 restore_uid();
604                 return 0;
605         }
606         /* Open the file containing the authorized keys. */
607         f = fopen(file, "r");
608         if (!f) {
609                 /* Restore the privileged uid. */
610                 restore_uid();
611                 return 0;
612         }
613         if (options.strict_modes) {
614                 int fail = 0;
615                 char buf[1024];
616                 /* Check open file in order to avoid open/stat races */
617                 if (fstat(fileno(f), &st) < 0 ||
618                     (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
619                     (st.st_mode & 022) != 0) {
620                         snprintf(buf, sizeof buf,
621                             "%s authentication refused for %.100s: "
622                             "bad ownership or modes for '%s'.",
623                             key_type(key), pw->pw_name, file);
624                         fail = 1;
625                 } else {
626                         /* Check path to SSH_USER_PERMITTED_KEYS */
627                         int i;
628                         static const char *check[] = {
629                                 "", SSH_USER_DIR, NULL
630                         };
631                         for (i = 0; check[i]; i++) {
632                                 snprintf(line, sizeof line, "%.500s/%.100s",
633                                     pw->pw_dir, check[i]);
634                                 if (stat(line, &st) < 0 ||
635                                     (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
636                                     (st.st_mode & 022) != 0) {
637                                         snprintf(buf, sizeof buf,
638                                             "%s authentication refused for %.100s: "
639                                             "bad ownership or modes for '%s'.",
640                                             key_type(key), pw->pw_name, line);
641                                         fail = 1;
642                                         break;
643                                 }
644                         }
645                 }
646                 if (fail) {
647                         fclose(f);
648                         log("%s",buf);
649                         restore_uid();
650                         return 0;
651                 }
652         }
653         found_key = 0;
654         found = key_new(key->type);
655
656         while (fgets(line, sizeof(line), f)) {
657                 char *cp, *options = NULL;
658                 linenum++;
659                 /* Skip leading whitespace, empty and comment lines. */
660                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
661                         ;
662                 if (!*cp || *cp == '\n' || *cp == '#')
663                         continue;
664
665                 if (key_read(found, &cp) == -1) {
666                         /* no key?  check if there are options for this key */
667                         int quoted = 0;
668                         debug2("user_key_allowed: check options: '%s'", cp);
669                         options = cp;
670                         for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
671                                 if (*cp == '\\' && cp[1] == '"')
672                                         cp++;   /* Skip both */
673                                 else if (*cp == '"')
674                                         quoted = !quoted;
675                         }
676                         /* Skip remaining whitespace. */
677                         for (; *cp == ' ' || *cp == '\t'; cp++)
678                                 ;
679                         if (key_read(found, &cp) == -1) {
680                                 debug2("user_key_allowed: advance: '%s'", cp);
681                                 /* still no key?  advance to next line*/
682                                 continue;
683                         }
684                 }
685                 if (key_equal(found, key) &&
686                     auth_parse_options(pw, options, linenum) == 1) {
687                         found_key = 1;
688                         debug("matching key found: file %s, line %ld",
689                             file, linenum);
690                         break;
691                 }
692         }
693         restore_uid();
694         fclose(f);
695         key_free(found);
696         return found_key;
697 }
This page took 0.089044 seconds and 5 git commands to generate.