xref: /freebsd/contrib/pam-krb5/module/support.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
1 /*
2  * Support functions for pam-krb5.
3  *
4  * Some general utility functions used by multiple PAM groups that aren't
5  * associated with any particular chunk of functionality.
6  *
7  * Copyright 2005-2007, 2009, 2020 Russ Allbery <eagle@eyrie.org>
8  * Copyright 2011-2012
9  *     The Board of Trustees of the Leland Stanford Junior University
10  * Copyright 2005 Andres Salomon <dilinger@debian.org>
11  * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
12  *
13  * SPDX-License-Identifier: BSD-3-clause or GPL-1+
14  */
15 
16 #include <config.h>
17 #include <portable/krb5.h>
18 #include <portable/pam.h>
19 #include <portable/system.h>
20 
21 #include <errno.h>
22 #include <pwd.h>
23 
24 #include <module/internal.h>
25 #include <pam-util/args.h>
26 #include <pam-util/logging.h>
27 
28 
29 /*
30  * Given the PAM arguments and the user we're authenticating, see if we should
31  * ignore that user because they're root or have a low-numbered UID and we
32  * were configured to ignore such users.  Returns true if we should ignore
33  * them, false otherwise.  Ignores any fully-qualified principal names.
34  */
35 int
36 pamk5_should_ignore(struct pam_args *args, PAM_CONST char *username)
37 {
38     struct passwd *pwd;
39 
40     if (args->config->ignore_root && strcmp("root", username) == 0) {
41         putil_debug(args, "ignoring root user");
42         return 1;
43     }
44     if (args->config->minimum_uid > 0 && strchr(username, '@') == NULL) {
45         pwd = pam_modutil_getpwnam(args->pamh, username);
46         if (pwd != NULL && pwd->pw_uid < (uid_t) args->config->minimum_uid) {
47             putil_debug(args, "ignoring low-UID user (%lu < %ld)",
48                         (unsigned long) pwd->pw_uid,
49                         args->config->minimum_uid);
50             return 1;
51         }
52     }
53     return 0;
54 }
55 
56 
57 /*
58  * Verify the user authorization.  Call krb5_kuserok if this is a local
59  * account, or do the krb5_aname_to_localname verification if ignore_k5login
60  * was requested.  For non-local accounts, the principal must match the
61  * authentication identity.
62  */
63 int
64 pamk5_authorized(struct pam_args *args)
65 {
66     struct context *ctx;
67     krb5_context c;
68     krb5_error_code retval;
69     int status;
70     struct passwd *pwd;
71     char kuser[65]; /* MAX_USERNAME == 65 (MIT Kerberos 1.4.1). */
72 
73     if (args == NULL || args->config == NULL || args->config->ctx == NULL
74         || args->config->ctx->context == NULL)
75         return PAM_SERVICE_ERR;
76     ctx = args->config->ctx;
77     if (ctx->name == NULL)
78         return PAM_SERVICE_ERR;
79     c = ctx->context;
80 
81     /*
82      * If alt_auth_map was set, authorize the user if the authenticated
83      * principal matches the mapped principal.  alt_auth_map essentially
84      * serves as a supplemental .k5login.  PAM_SERVICE_ERR indicates fatal
85      * errors that should abort remaining processing; PAM_AUTH_ERR indicates
86      * that it just didn't match, in which case we continue to try other
87      * authorization methods.
88      */
89     if (args->config->alt_auth_map != NULL) {
90         status = pamk5_alt_auth_verify(args);
91         if (status == PAM_SUCCESS || status == PAM_SERVICE_ERR)
92             return status;
93     }
94 
95     /*
96      * If the name to which we're authenticating contains @ (is fully
97      * qualified), it must match the principal exactly.
98      */
99     if (strchr(ctx->name, '@') != NULL) {
100         char *principal;
101 
102         retval = krb5_unparse_name(c, ctx->princ, &principal);
103         if (retval != 0) {
104             putil_err_krb5(args, retval, "krb5_unparse_name failed");
105             return PAM_SERVICE_ERR;
106         }
107         if (strcmp(principal, ctx->name) != 0) {
108             putil_err(args, "user %s does not match principal %s", ctx->name,
109                       principal);
110             krb5_free_unparsed_name(c, principal);
111             return PAM_AUTH_ERR;
112         }
113         krb5_free_unparsed_name(c, principal);
114         return PAM_SUCCESS;
115     }
116 
117     /*
118      * Otherwise, apply either krb5_aname_to_localname or krb5_kuserok
119      * depending on the situation.
120      */
121     pwd = pam_modutil_getpwnam(args->pamh, ctx->name);
122     if (args->config->ignore_k5login || pwd == NULL) {
123         retval = krb5_aname_to_localname(c, ctx->princ, sizeof(kuser), kuser);
124         if (retval != 0) {
125             putil_err_krb5(args, retval, "cannot convert principal to user");
126             return PAM_AUTH_ERR;
127         }
128         if (strcmp(kuser, ctx->name) != 0) {
129             putil_err(args, "user %s does not match local name %s", ctx->name,
130                       kuser);
131             return PAM_AUTH_ERR;
132         }
133     } else {
134         if (!krb5_kuserok(c, ctx->princ, ctx->name)) {
135             putil_err(args, "krb5_kuserok for user %s failed", ctx->name);
136             return PAM_AUTH_ERR;
137         }
138     }
139 
140     return PAM_SUCCESS;
141 }
142