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
pamk5_should_ignore(struct pam_args * args,PAM_CONST char * username)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
pamk5_authorized(struct pam_args * args)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