1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert * Support for alternate authentication mapping.
3*bf6873c5SCy Schubert *
4*bf6873c5SCy Schubert * pam-krb5 supports a feature where the principal for authentication can be
5*bf6873c5SCy Schubert * set via a PAM option and possibly based on the authenticating user. This
6*bf6873c5SCy Schubert * can be used to, for example, require /root instances be used with sudo
7*bf6873c5SCy Schubert * while still using normal instances for other system authentications.
8*bf6873c5SCy Schubert *
9*bf6873c5SCy Schubert * This file collects all the pieces related to that support.
10*bf6873c5SCy Schubert *
11*bf6873c5SCy Schubert * Original support written by Booker Bense <bbense@slac.stanford.edu>
12*bf6873c5SCy Schubert * Further updates by Russ Allbery <eagle@eyrie.org>
13*bf6873c5SCy Schubert * Copyright 2020 Russ Allbery <eagle@eyrie.org>
14*bf6873c5SCy Schubert * Copyright 2008-2012
15*bf6873c5SCy Schubert * The Board of Trustees of the Leland Stanford Junior University
16*bf6873c5SCy Schubert *
17*bf6873c5SCy Schubert * SPDX-License-Identifier: BSD-3-clause or GPL-1+
18*bf6873c5SCy Schubert */
19*bf6873c5SCy Schubert
20*bf6873c5SCy Schubert #include <config.h>
21*bf6873c5SCy Schubert #include <portable/krb5.h>
22*bf6873c5SCy Schubert #include <portable/pam.h>
23*bf6873c5SCy Schubert #include <portable/system.h>
24*bf6873c5SCy Schubert
25*bf6873c5SCy Schubert #include <errno.h>
26*bf6873c5SCy Schubert
27*bf6873c5SCy Schubert #include <module/internal.h>
28*bf6873c5SCy Schubert #include <pam-util/args.h>
29*bf6873c5SCy Schubert #include <pam-util/logging.h>
30*bf6873c5SCy Schubert
31*bf6873c5SCy Schubert
32*bf6873c5SCy Schubert /*
33*bf6873c5SCy Schubert * Map the user to a Kerberos principal according to alt_auth_map. Returns 0
34*bf6873c5SCy Schubert * on success, storing the mapped principal name in newly allocated memory in
35*bf6873c5SCy Schubert * principal. The caller is responsible for freeing. Returns an errno value
36*bf6873c5SCy Schubert * on any error.
37*bf6873c5SCy Schubert */
38*bf6873c5SCy Schubert int
pamk5_map_principal(struct pam_args * args,const char * username,char ** principal)39*bf6873c5SCy Schubert pamk5_map_principal(struct pam_args *args, const char *username,
40*bf6873c5SCy Schubert char **principal)
41*bf6873c5SCy Schubert {
42*bf6873c5SCy Schubert char *realm;
43*bf6873c5SCy Schubert char *new_user = NULL;
44*bf6873c5SCy Schubert const char *user;
45*bf6873c5SCy Schubert const char *p;
46*bf6873c5SCy Schubert size_t needed, offset;
47*bf6873c5SCy Schubert int oerrno;
48*bf6873c5SCy Schubert
49*bf6873c5SCy Schubert /* Makes no sense if alt_auth_map isn't set. */
50*bf6873c5SCy Schubert if (args->config->alt_auth_map == NULL)
51*bf6873c5SCy Schubert return EINVAL;
52*bf6873c5SCy Schubert
53*bf6873c5SCy Schubert /* Need to split off the realm if it is present. */
54*bf6873c5SCy Schubert realm = strchr(username, '@');
55*bf6873c5SCy Schubert if (realm == NULL)
56*bf6873c5SCy Schubert user = username;
57*bf6873c5SCy Schubert else {
58*bf6873c5SCy Schubert new_user = strdup(username);
59*bf6873c5SCy Schubert if (new_user == NULL)
60*bf6873c5SCy Schubert return errno;
61*bf6873c5SCy Schubert realm = strchr(new_user, '@');
62*bf6873c5SCy Schubert if (realm == NULL)
63*bf6873c5SCy Schubert goto fail;
64*bf6873c5SCy Schubert *realm = '\0';
65*bf6873c5SCy Schubert realm++;
66*bf6873c5SCy Schubert user = new_user;
67*bf6873c5SCy Schubert }
68*bf6873c5SCy Schubert
69*bf6873c5SCy Schubert /* Now, allocate a string and build the principal. */
70*bf6873c5SCy Schubert needed = 0;
71*bf6873c5SCy Schubert for (p = args->config->alt_auth_map; *p != '\0'; p++) {
72*bf6873c5SCy Schubert if (p[0] == '%' && p[1] == 's') {
73*bf6873c5SCy Schubert needed += strlen(user);
74*bf6873c5SCy Schubert p++;
75*bf6873c5SCy Schubert } else {
76*bf6873c5SCy Schubert needed++;
77*bf6873c5SCy Schubert }
78*bf6873c5SCy Schubert }
79*bf6873c5SCy Schubert if (realm != NULL && strchr(args->config->alt_auth_map, '@') == NULL)
80*bf6873c5SCy Schubert needed += 1 + strlen(realm);
81*bf6873c5SCy Schubert needed++;
82*bf6873c5SCy Schubert *principal = malloc(needed);
83*bf6873c5SCy Schubert if (*principal == NULL)
84*bf6873c5SCy Schubert goto fail;
85*bf6873c5SCy Schubert offset = 0;
86*bf6873c5SCy Schubert for (p = args->config->alt_auth_map; *p != '\0'; p++) {
87*bf6873c5SCy Schubert if (p[0] == '%' && p[1] == 's') {
88*bf6873c5SCy Schubert memcpy(*principal + offset, user, strlen(user));
89*bf6873c5SCy Schubert offset += strlen(user);
90*bf6873c5SCy Schubert p++;
91*bf6873c5SCy Schubert } else {
92*bf6873c5SCy Schubert (*principal)[offset] = *p;
93*bf6873c5SCy Schubert offset++;
94*bf6873c5SCy Schubert }
95*bf6873c5SCy Schubert }
96*bf6873c5SCy Schubert if (realm != NULL && strchr(args->config->alt_auth_map, '@') == NULL) {
97*bf6873c5SCy Schubert (*principal)[offset] = '@';
98*bf6873c5SCy Schubert offset++;
99*bf6873c5SCy Schubert memcpy(*principal + offset, realm, strlen(realm));
100*bf6873c5SCy Schubert offset += strlen(realm);
101*bf6873c5SCy Schubert }
102*bf6873c5SCy Schubert (*principal)[offset] = '\0';
103*bf6873c5SCy Schubert free(new_user);
104*bf6873c5SCy Schubert return 0;
105*bf6873c5SCy Schubert
106*bf6873c5SCy Schubert fail:
107*bf6873c5SCy Schubert if (new_user != NULL) {
108*bf6873c5SCy Schubert oerrno = errno;
109*bf6873c5SCy Schubert free(new_user);
110*bf6873c5SCy Schubert errno = oerrno;
111*bf6873c5SCy Schubert }
112*bf6873c5SCy Schubert return errno;
113*bf6873c5SCy Schubert }
114*bf6873c5SCy Schubert
115*bf6873c5SCy Schubert
116*bf6873c5SCy Schubert /*
117*bf6873c5SCy Schubert * Authenticate using an alternate principal mapping.
118*bf6873c5SCy Schubert *
119*bf6873c5SCy Schubert * Create a principal based on the principal mapping and the user, and use the
120*bf6873c5SCy Schubert * provided password to try to authenticate as that user. If we succeed, fill
121*bf6873c5SCy Schubert * out creds, set princ to the successful principal in the context, and return
122*bf6873c5SCy Schubert * 0. Otherwise, return a Kerberos error code or an errno value.
123*bf6873c5SCy Schubert */
124*bf6873c5SCy Schubert krb5_error_code
pamk5_alt_auth(struct pam_args * args,const char * service,krb5_get_init_creds_opt * opts,const char * pass,krb5_creds * creds)125*bf6873c5SCy Schubert pamk5_alt_auth(struct pam_args *args, const char *service,
126*bf6873c5SCy Schubert krb5_get_init_creds_opt *opts, const char *pass,
127*bf6873c5SCy Schubert krb5_creds *creds)
128*bf6873c5SCy Schubert {
129*bf6873c5SCy Schubert struct context *ctx = args->config->ctx;
130*bf6873c5SCy Schubert char *kuser;
131*bf6873c5SCy Schubert krb5_principal princ;
132*bf6873c5SCy Schubert krb5_error_code retval;
133*bf6873c5SCy Schubert
134*bf6873c5SCy Schubert retval = pamk5_map_principal(args, ctx->name, &kuser);
135*bf6873c5SCy Schubert if (retval != 0)
136*bf6873c5SCy Schubert return retval;
137*bf6873c5SCy Schubert retval = krb5_parse_name(ctx->context, kuser, &princ);
138*bf6873c5SCy Schubert if (retval != 0) {
139*bf6873c5SCy Schubert free(kuser);
140*bf6873c5SCy Schubert return retval;
141*bf6873c5SCy Schubert }
142*bf6873c5SCy Schubert free(kuser);
143*bf6873c5SCy Schubert
144*bf6873c5SCy Schubert /* Log the principal we're attempting to authenticate as. */
145*bf6873c5SCy Schubert if (args->debug) {
146*bf6873c5SCy Schubert char *principal;
147*bf6873c5SCy Schubert
148*bf6873c5SCy Schubert retval = krb5_unparse_name(ctx->context, princ, &principal);
149*bf6873c5SCy Schubert if (retval != 0)
150*bf6873c5SCy Schubert putil_debug_krb5(args, retval, "krb5_unparse_name failed");
151*bf6873c5SCy Schubert else {
152*bf6873c5SCy Schubert putil_debug(args, "mapping %s to %s", ctx->name, principal);
153*bf6873c5SCy Schubert krb5_free_unparsed_name(ctx->context, principal);
154*bf6873c5SCy Schubert }
155*bf6873c5SCy Schubert }
156*bf6873c5SCy Schubert
157*bf6873c5SCy Schubert /*
158*bf6873c5SCy Schubert * Now, attempt to authenticate as that user. On success, save the
159*bf6873c5SCy Schubert * principal. Return the Kerberos status code.
160*bf6873c5SCy Schubert */
161*bf6873c5SCy Schubert retval = krb5_get_init_creds_password(ctx->context, creds, princ,
162*bf6873c5SCy Schubert (char *) pass, pamk5_prompter_krb5,
163*bf6873c5SCy Schubert args, 0, (char *) service, opts);
164*bf6873c5SCy Schubert if (retval != 0) {
165*bf6873c5SCy Schubert putil_debug_krb5(args, retval, "alternate authentication failed");
166*bf6873c5SCy Schubert krb5_free_principal(ctx->context, princ);
167*bf6873c5SCy Schubert return retval;
168*bf6873c5SCy Schubert } else {
169*bf6873c5SCy Schubert putil_debug(args, "alternate authentication successful");
170*bf6873c5SCy Schubert if (ctx->princ != NULL)
171*bf6873c5SCy Schubert krb5_free_principal(ctx->context, ctx->princ);
172*bf6873c5SCy Schubert ctx->princ = princ;
173*bf6873c5SCy Schubert return 0;
174*bf6873c5SCy Schubert }
175*bf6873c5SCy Schubert }
176*bf6873c5SCy Schubert
177*bf6873c5SCy Schubert
178*bf6873c5SCy Schubert /*
179*bf6873c5SCy Schubert * Verify an alternate authentication.
180*bf6873c5SCy Schubert *
181*bf6873c5SCy Schubert * Meant to be called from pamk5_authorized, this checks that the principal in
182*bf6873c5SCy Schubert * the context matches the alt_auth_map-derived identity of the user we're
183*bf6873c5SCy Schubert * authenticating. Returns PAM_SUCCESS if they match, PAM_AUTH_ERR if they
184*bf6873c5SCy Schubert * don't match, and PAM_SERVICE_ERR on an internal error.
185*bf6873c5SCy Schubert */
186*bf6873c5SCy Schubert int
pamk5_alt_auth_verify(struct pam_args * args)187*bf6873c5SCy Schubert pamk5_alt_auth_verify(struct pam_args *args)
188*bf6873c5SCy Schubert {
189*bf6873c5SCy Schubert struct context *ctx;
190*bf6873c5SCy Schubert char *name = NULL;
191*bf6873c5SCy Schubert char *mapped = NULL;
192*bf6873c5SCy Schubert char *authed = NULL;
193*bf6873c5SCy Schubert krb5_principal princ = NULL;
194*bf6873c5SCy Schubert krb5_error_code retval;
195*bf6873c5SCy Schubert int status = PAM_SERVICE_ERR;
196*bf6873c5SCy Schubert
197*bf6873c5SCy Schubert if (args == NULL || args->config == NULL || args->config->ctx == NULL)
198*bf6873c5SCy Schubert return PAM_SERVICE_ERR;
199*bf6873c5SCy Schubert ctx = args->config->ctx;
200*bf6873c5SCy Schubert if (ctx->context == NULL || ctx->name == NULL)
201*bf6873c5SCy Schubert return PAM_SERVICE_ERR;
202*bf6873c5SCy Schubert if (pamk5_map_principal(args, ctx->name, &name) != 0) {
203*bf6873c5SCy Schubert putil_err(args, "cannot map principal name");
204*bf6873c5SCy Schubert goto done;
205*bf6873c5SCy Schubert }
206*bf6873c5SCy Schubert retval = krb5_parse_name(ctx->context, name, &princ);
207*bf6873c5SCy Schubert if (retval != 0) {
208*bf6873c5SCy Schubert putil_err_krb5(args, retval, "cannot parse mapped principal name %s",
209*bf6873c5SCy Schubert mapped);
210*bf6873c5SCy Schubert goto done;
211*bf6873c5SCy Schubert }
212*bf6873c5SCy Schubert retval = krb5_unparse_name(ctx->context, princ, &mapped);
213*bf6873c5SCy Schubert if (retval != 0) {
214*bf6873c5SCy Schubert putil_err_krb5(args, retval,
215*bf6873c5SCy Schubert "krb5_unparse_name on mapped principal failed");
216*bf6873c5SCy Schubert goto done;
217*bf6873c5SCy Schubert }
218*bf6873c5SCy Schubert retval = krb5_unparse_name(ctx->context, ctx->princ, &authed);
219*bf6873c5SCy Schubert if (retval != 0) {
220*bf6873c5SCy Schubert putil_err_krb5(args, retval, "krb5_unparse_name failed");
221*bf6873c5SCy Schubert goto done;
222*bf6873c5SCy Schubert }
223*bf6873c5SCy Schubert if (strcmp(authed, mapped) == 0)
224*bf6873c5SCy Schubert status = PAM_SUCCESS;
225*bf6873c5SCy Schubert else {
226*bf6873c5SCy Schubert putil_debug(args, "mapped user %s does not match principal %s", mapped,
227*bf6873c5SCy Schubert authed);
228*bf6873c5SCy Schubert status = PAM_AUTH_ERR;
229*bf6873c5SCy Schubert }
230*bf6873c5SCy Schubert
231*bf6873c5SCy Schubert done:
232*bf6873c5SCy Schubert free(name);
233*bf6873c5SCy Schubert if (authed != NULL)
234*bf6873c5SCy Schubert krb5_free_unparsed_name(ctx->context, authed);
235*bf6873c5SCy Schubert if (mapped != NULL)
236*bf6873c5SCy Schubert krb5_free_unparsed_name(ctx->context, mapped);
237*bf6873c5SCy Schubert if (princ != NULL)
238*bf6873c5SCy Schubert krb5_free_principal(ctx->context, princ);
239*bf6873c5SCy Schubert return status;
240*bf6873c5SCy Schubert }
241