source: trunk/third/openssh/auth.c @ 18763

Revision 18763, 14.1 KB checked in by zacheiss, 22 years ago (diff)
Merge openssh 3.5p1.
Line 
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"
26RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $");
27
28#ifdef HAVE_LOGIN_H
29#include <login.h>
30#endif
31#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
32#include <shadow.h>
33#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */
34
35#ifdef HAVE_LIBGEN_H
36#include <libgen.h>
37#endif
38
39#include <al.h>
40char *session_username;
41int is_local_acct;
42
43#include "xmalloc.h"
44#include "match.h"
45#include "groupaccess.h"
46#include "log.h"
47#include "servconf.h"
48#include "auth.h"
49#include "auth-options.h"
50#include "canohost.h"
51#include "buffer.h"
52#include "bufaux.h"
53#include "uidswap.h"
54#include "tildexpand.h"
55#include "misc.h"
56#include "bufaux.h"
57#include "packet.h"
58
59/* import */
60extern ServerOptions options;
61
62/* Debugging messages */
63Buffer auth_debug;
64int auth_debug_init;
65
66/*
67 * Check if the user is allowed to log in via ssh. If user is listed
68 * in DenyUsers or one of user's groups is listed in DenyGroups, false
69 * will be returned. If AllowUsers isn't empty and user isn't listed
70 * there, or if AllowGroups isn't empty and one of user's groups isn't
71 * listed there, false will be returned.
72 * If the user's shell is not executable, false will be returned.
73 * Otherwise true is returned.
74 */
75int
76allowed_user(struct passwd * pw)
77{
78        struct stat st;
79        const char *hostname = NULL, *ipaddr = NULL;
80        char *shell;
81        int i;
82#ifdef WITH_AIXAUTHENTICATE
83        char *loginmsg;
84#endif /* WITH_AIXAUTHENTICATE */
85#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
86        !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
87        struct spwd *spw;
88
89        /* Shouldn't be called if pw is NULL, but better safe than sorry... */
90        if (!pw || !pw->pw_name)
91                return 0;
92
93#define DAY             (24L * 60 * 60) /* 1 day in seconds */
94        spw = getspnam(pw->pw_name);
95        if (spw != NULL) {
96                time_t today = time(NULL) / DAY;
97                debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
98                    " sp_max %d", (int)today, (int)spw->sp_expire,
99                    (int)spw->sp_lstchg, (int)spw->sp_max);
100
101                /*
102                 * We assume account and password expiration occurs the
103                 * day after the day specified.
104                 */
105                if (spw->sp_expire != -1 && today > spw->sp_expire) {
106                        log("Account %.100s has expired", pw->pw_name);
107                        return 0;
108                }
109
110                if (spw->sp_lstchg == 0) {
111                        log("User %.100s password has expired (root forced)",
112                            pw->pw_name);
113                        return 0;
114                }
115
116                if (spw->sp_max != -1 &&
117                    today > spw->sp_lstchg + spw->sp_max) {
118                        log("User %.100s password has expired (password aged)",
119                            pw->pw_name);
120                        return 0;
121                }
122        }
123#else
124        /* Shouldn't be called if pw is NULL, but better safe than sorry... */
125        if (!pw || !pw->pw_name)
126                return 0;
127#endif
128
129        /*
130         * Get the shell from the password data.  An empty shell field is
131         * legal, and means /bin/sh.
132         */
133        shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
134
135        /* deny if shell does not exists or is not executable */
136        if (stat(shell, &st) != 0) {
137                log("User %.100s not allowed because shell %.100s does not exist",
138                    pw->pw_name, shell);
139                return 0;
140        }
141        if (S_ISREG(st.st_mode) == 0 ||
142            (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
143                log("User %.100s not allowed because shell %.100s is not executable",
144                    pw->pw_name, shell);
145                return 0;
146        }
147
148        if (options.num_deny_users > 0 || options.num_allow_users > 0) {
149                hostname = get_canonical_hostname(options.verify_reverse_mapping);
150                ipaddr = get_remote_ipaddr();
151        }
152
153        /* Return false if user is listed in DenyUsers */
154        if (options.num_deny_users > 0) {
155                for (i = 0; i < options.num_deny_users; i++)
156                        if (match_user(pw->pw_name, hostname, ipaddr,
157                            options.deny_users[i])) {
158                                log("User %.100s not allowed because listed in DenyUsers",
159                                    pw->pw_name);
160                                return 0;
161                        }
162        }
163        /* Return false if AllowUsers isn't empty and user isn't listed there */
164        if (options.num_allow_users > 0) {
165                for (i = 0; i < options.num_allow_users; i++)
166                        if (match_user(pw->pw_name, hostname, ipaddr,
167                            options.allow_users[i]))
168                                break;
169                /* i < options.num_allow_users iff we break for loop */
170                if (i >= options.num_allow_users) {
171                        log("User %.100s not allowed because not listed in AllowUsers",
172                            pw->pw_name);
173                        return 0;
174                }
175        }
176        if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
177                /* Get the user's group access list (primary and supplementary) */
178                if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
179                        log("User %.100s not allowed because not in any group",
180                            pw->pw_name);
181                        return 0;
182                }
183
184                /* Return false if one of user's groups is listed in DenyGroups */
185                if (options.num_deny_groups > 0)
186                        if (ga_match(options.deny_groups,
187                            options.num_deny_groups)) {
188                                ga_free();
189                                log("User %.100s not allowed because a group is listed in DenyGroups",
190                                    pw->pw_name);
191                                return 0;
192                        }
193                /*
194                 * Return false if AllowGroups isn't empty and one of user's groups
195                 * isn't listed there
196                 */
197                if (options.num_allow_groups > 0)
198                        if (!ga_match(options.allow_groups,
199                            options.num_allow_groups)) {
200                                ga_free();
201                                log("User %.100s not allowed because none of user's groups are listed in AllowGroups",
202                                    pw->pw_name);
203                                return 0;
204                        }
205                ga_free();
206        }
207
208#ifdef WITH_AIXAUTHENTICATE
209        if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) {
210                if (loginmsg && *loginmsg) {
211                        /* Remove embedded newlines (if any) */
212                        char *p;
213                        for (p = loginmsg; *p; p++) {
214                                if (*p == '\n')
215                                        *p = ' ';
216                        }
217                        /* Remove trailing newline */
218                        *--p = '\0';
219                        log("Login restricted for %s: %.100s", pw->pw_name, loginmsg);
220                }
221                return 0;
222        }
223#endif /* WITH_AIXAUTHENTICATE */
224
225        /* We found no reason not to let this user try to log on... */
226        return 1;
227}
228
229Authctxt *
230authctxt_new(void)
231{
232        Authctxt *authctxt = xmalloc(sizeof(*authctxt));
233        memset(authctxt, 0, sizeof(*authctxt));
234        return authctxt;
235}
236
237void
238auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
239{
240        void (*authlog) (const char *fmt,...) = verbose;
241        char *authmsg;
242
243        /* Raise logging level */
244        if (authenticated == 1 ||
245            !authctxt->valid ||
246            authctxt->failures >= AUTH_FAIL_LOG ||
247            strcmp(method, "password") == 0)
248                authlog = log;
249
250        if (authctxt->postponed)
251                authmsg = "Postponed";
252        else
253                authmsg = authenticated ? "Accepted" : "Failed";
254
255        authlog("%s %s for %s%.100s from %.200s port %d%s",
256            authmsg,
257            method,
258            authctxt->valid ? "" : "illegal user ",
259            authctxt->user,
260            get_remote_ipaddr(),
261            get_remote_port(),
262            info);
263
264#ifdef WITH_AIXAUTHENTICATE
265        if (authenticated == 0 && strcmp(method, "password") == 0)
266            loginfailed(authctxt->user,
267                get_canonical_hostname(options.verify_reverse_mapping),
268                "ssh");
269#endif /* WITH_AIXAUTHENTICATE */
270
271}
272
273/*
274 * Check whether root logins are disallowed.
275 */
276int
277auth_root_allowed(char *method)
278{
279        switch (options.permit_root_login) {
280        case PERMIT_YES:
281                return 1;
282                break;
283        case PERMIT_NO_PASSWD:
284                if (strcmp(method, "password") != 0)
285                        return 1;
286                break;
287        case PERMIT_FORCED_ONLY:
288                if (forced_command) {
289                        log("Root login accepted for forced command.");
290                        return 1;
291                }
292                break;
293        }
294        log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
295        return 0;
296}
297
298
299/*
300 * Given a template and a passwd structure, build a filename
301 * by substituting % tokenised options. Currently, %% becomes '%',
302 * %h becomes the home directory and %u the username.
303 *
304 * This returns a buffer allocated by xmalloc.
305 */
306char *
307expand_filename(const char *filename, struct passwd *pw)
308{
309        Buffer buffer;
310        char *file;
311        const char *cp;
312
313        /*
314         * Build the filename string in the buffer by making the appropriate
315         * substitutions to the given file name.
316         */
317        buffer_init(&buffer);
318        for (cp = filename; *cp; cp++) {
319                if (cp[0] == '%' && cp[1] == '%') {
320                        buffer_append(&buffer, "%", 1);
321                        cp++;
322                        continue;
323                }
324                if (cp[0] == '%' && cp[1] == 'h') {
325                        buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir));
326                        cp++;
327                        continue;
328                }
329                if (cp[0] == '%' && cp[1] == 'u') {
330                        buffer_append(&buffer, pw->pw_name,
331                            strlen(pw->pw_name));
332                        cp++;
333                        continue;
334                }
335                buffer_append(&buffer, cp, 1);
336        }
337        buffer_append(&buffer, "\0", 1);
338
339        /*
340         * Ensure that filename starts anchored. If not, be backward
341         * compatible and prepend the '%h/'
342         */
343        file = xmalloc(MAXPATHLEN);
344        cp = buffer_ptr(&buffer);
345        if (*cp != '/')
346                snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp);
347        else
348                strlcpy(file, cp, MAXPATHLEN);
349
350        buffer_free(&buffer);
351        return file;
352}
353
354char *
355authorized_keys_file(struct passwd *pw)
356{
357        return expand_filename(options.authorized_keys_file, pw);
358}
359
360char *
361authorized_keys_file2(struct passwd *pw)
362{
363        return expand_filename(options.authorized_keys_file2, pw);
364}
365
366/* return ok if key exists in sysfile or userfile */
367HostStatus
368check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
369    const char *sysfile, const char *userfile)
370{
371        Key *found;
372        char *user_hostfile;
373        struct stat st;
374        HostStatus host_status;
375
376        /* Check if we know the host and its host key. */
377        found = key_new(key->type);
378        host_status = check_host_in_hostfile(sysfile, host, key, found, NULL);
379
380        if (host_status != HOST_OK && userfile != NULL) {
381                user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
382                if (options.strict_modes &&
383                    (stat(user_hostfile, &st) == 0) &&
384                    ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
385                    (st.st_mode & 022) != 0)) {
386                        log("Authentication refused for %.100s: "
387                            "bad owner or modes for %.200s",
388                            pw->pw_name, user_hostfile);
389                } else {
390                        temporarily_use_uid(pw);
391                        host_status = check_host_in_hostfile(user_hostfile,
392                            host, key, found, NULL);
393                        restore_uid();
394                }
395                xfree(user_hostfile);
396        }
397        key_free(found);
398
399        debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
400            "ok" : "not found", host);
401        return host_status;
402}
403
404
405/*
406 * Check a given file for security. This is defined as all components
407 * of the path to the file must be owned by either the owner of
408 * of the file or root and no directories must be group or world writable.
409 *
410 * XXX Should any specific check be done for sym links ?
411 *
412 * Takes an open file descriptor, the file name, a uid and and
413 * error buffer plus max size as arguments.
414 *
415 * Returns 0 on success and -1 on failure
416 */
417int
418secure_filename(FILE *f, const char *file, struct passwd *pw,
419    char *err, size_t errlen)
420{
421        uid_t uid = pw->pw_uid;
422        char buf[MAXPATHLEN], homedir[MAXPATHLEN];
423        char *cp;
424        struct stat st;
425
426        if (realpath(file, buf) == NULL) {
427                snprintf(err, errlen, "realpath %s failed: %s", file,
428                    strerror(errno));
429                return -1;
430        }
431        if (realpath(pw->pw_dir, homedir) == NULL) {
432                snprintf(err, errlen, "realpath %s failed: %s", pw->pw_dir,
433                    strerror(errno));
434                return -1;
435        }
436
437        /* check the open file to avoid races */
438        if (fstat(fileno(f), &st) < 0 ||
439            (st.st_uid != 0 && st.st_uid != uid) ||
440            (st.st_mode & 022) != 0) {
441                snprintf(err, errlen, "bad ownership or modes for file %s",
442                    buf);
443                return -1;
444        }
445
446        /* for each component of the canonical path, walking upwards */
447        for (;;) {
448                if ((cp = dirname(buf)) == NULL) {
449                        snprintf(err, errlen, "dirname() failed");
450                        return -1;
451                }
452                strlcpy(buf, cp, sizeof(buf));
453
454                debug3("secure_filename: checking '%s'", buf);
455                if (stat(buf, &st) < 0 ||
456                    (st.st_uid != 0 && st.st_uid != uid) ||
457                    (st.st_mode & 022) != 0) {
458                        snprintf(err, errlen,
459                            "bad ownership or modes for directory %s", buf);
460                        return -1;
461                }
462
463                /* If are passed the homedir then we can stop */
464                if (strcmp(homedir, buf) == 0) {
465                        debug3("secure_filename: terminating check at '%s'",
466                            buf);
467                        break;
468                }
469                /*
470                 * dirname should always complete with a "/" path,
471                 * but we can be paranoid and check for "." too
472                 */
473                if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
474                        break;
475        }
476        return 0;
477}
478
479void session_cleanup(void)
480{
481  al_acct_revert(session_username, getpid());
482}
483
484struct passwd *
485getpwnamallow(const char *user)
486{
487#ifdef HAVE_LOGIN_CAP
488        extern login_cap_t *lc;
489#ifdef BSD_AUTH
490        auth_session_t *as;
491#endif
492#endif
493        struct passwd *pw;
494
495        pw = getpwnam(user);
496        if (pw == NULL) {
497                log("Illegal user %.100s from %.100s",
498                    user, get_remote_ipaddr());
499                return (NULL);
500        }
501        if (!allowed_user(pw))
502                return (NULL);
503#ifdef HAVE_LOGIN_CAP
504        if ((lc = login_getclass(pw->pw_class)) == NULL) {
505                debug("unable to get login class: %s", user);
506                return (NULL);
507        }
508#ifdef BSD_AUTH
509        if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
510            auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
511                debug("Approval failure for %s", user);
512                pw = NULL;
513        }
514        if (as != NULL)
515                auth_close(as);
516#endif
517#endif
518        if (pw != NULL)
519                return (pwcopy(pw));
520        return (NULL);
521}
522
523void
524auth_debug_add(const char *fmt,...)
525{
526        char buf[1024];
527        va_list args;
528
529        if (!auth_debug_init)
530                return;
531
532        va_start(args, fmt);
533        vsnprintf(buf, sizeof(buf), fmt, args);
534        va_end(args);
535        buffer_put_cstring(&auth_debug, buf);
536}
537
538void
539auth_debug_send(void)
540{
541        char *msg;
542
543        if (!auth_debug_init)
544                return;
545        while (buffer_len(&auth_debug)) {
546                msg = buffer_get_string(&auth_debug, NULL);
547                packet_send_debug("%s", msg);
548                xfree(msg);
549        }
550}
551
552void
553auth_debug_reset(void)
554{
555        if (auth_debug_init)
556                buffer_clear(&auth_debug);
557        else {
558                buffer_init(&auth_debug);
559                auth_debug_init = 1;
560        }
561}
Note: See TracBrowser for help on using the repository browser.