Line data Source code
1 : /*
2 : SSSD
3 :
4 : Kerberos 5 Backend Module -- tgt_req and changepw child
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009-2010 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <sys/types.h>
26 : #include <unistd.h>
27 : #include <sys/stat.h>
28 : #include <popt.h>
29 :
30 : #include <security/pam_modules.h>
31 :
32 : #include "util/util.h"
33 : #include "util/sss_krb5.h"
34 : #include "util/user_info_msg.h"
35 : #include "util/child_common.h"
36 : #include "util/find_uid.h"
37 : #include "src/util/util_errors.h"
38 : #include "providers/dp_backend.h"
39 : #include "providers/krb5/krb5_auth.h"
40 : #include "providers/krb5/krb5_utils.h"
41 : #include "sss_cli.h"
42 :
43 : #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw"
44 :
45 : enum k5c_fast_opt {
46 : K5C_FAST_NEVER,
47 : K5C_FAST_TRY,
48 : K5C_FAST_DEMAND,
49 : };
50 :
51 : struct krb5_req {
52 : krb5_context ctx;
53 : krb5_principal princ;
54 : char* name;
55 : krb5_creds *creds;
56 : bool otp;
57 : char *otp_vendor;
58 : char *otp_token_id;
59 : char *otp_challenge;
60 : krb5_get_init_creds_opt *options;
61 :
62 : struct pam_data *pd;
63 :
64 : char *realm;
65 : char *ccname;
66 : char *keytab;
67 : bool validate;
68 : bool send_pac;
69 : bool use_enterprise_princ;
70 : char *fast_ccname;
71 :
72 : const char *upn;
73 : uid_t uid;
74 : gid_t gid;
75 :
76 : char *old_ccname;
77 : bool old_cc_valid;
78 : bool old_cc_active;
79 : enum k5c_fast_opt fast_val;
80 :
81 : uid_t fast_uid;
82 : gid_t fast_gid;
83 : };
84 :
85 : static krb5_context krb5_error_ctx;
86 : #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
87 :
88 0 : static krb5_error_code set_lifetime_options(krb5_get_init_creds_opt *options)
89 : {
90 : char *lifetime_str;
91 : krb5_error_code kerr;
92 : krb5_deltat lifetime;
93 :
94 0 : lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
95 0 : if (lifetime_str == NULL) {
96 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
97 : SSSD_KRB5_RENEWABLE_LIFETIME);
98 :
99 : /* Unset option flag to make sure defaults from krb5.conf are used. */
100 0 : options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE);
101 : } else {
102 0 : kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
103 0 : if (kerr != 0) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE,
105 : "krb5_string_to_deltat failed for [%s].\n",
106 : lifetime_str);
107 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
108 0 : return kerr;
109 : }
110 0 : DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
111 : SSSD_KRB5_RENEWABLE_LIFETIME, lifetime_str);
112 0 : krb5_get_init_creds_opt_set_renew_life(options, lifetime);
113 : }
114 :
115 0 : lifetime_str = getenv(SSSD_KRB5_LIFETIME);
116 0 : if (lifetime_str == NULL) {
117 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Cannot read [%s] from environment.\n",
118 : SSSD_KRB5_LIFETIME);
119 :
120 : /* Unset option flag to make sure defaults from krb5.conf are used. */
121 0 : options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_TKT_LIFE);
122 : } else {
123 0 : kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
124 0 : if (kerr != 0) {
125 0 : DEBUG(SSSDBG_CRIT_FAILURE,
126 : "krb5_string_to_deltat failed for [%s].\n",
127 : lifetime_str);
128 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
129 0 : return kerr;
130 : }
131 0 : DEBUG(SSSDBG_CONF_SETTINGS,
132 : "%s is set to [%s]\n", SSSD_KRB5_LIFETIME, lifetime_str);
133 0 : krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
134 : }
135 :
136 0 : return 0;
137 : }
138 :
139 0 : static void set_canonicalize_option(krb5_get_init_creds_opt *opts)
140 : {
141 0 : int canonicalize = 0;
142 : char *tmp_str;
143 :
144 0 : tmp_str = getenv(SSSD_KRB5_CANONICALIZE);
145 0 : if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
146 0 : canonicalize = 1;
147 : }
148 0 : DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
149 : SSSD_KRB5_CANONICALIZE, tmp_str ? tmp_str : "not set");
150 0 : sss_krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
151 0 : }
152 :
153 0 : static void set_changepw_options(krb5_get_init_creds_opt *options)
154 : {
155 0 : sss_krb5_get_init_creds_opt_set_canonicalize(options, 0);
156 0 : krb5_get_init_creds_opt_set_forwardable(options, 0);
157 0 : krb5_get_init_creds_opt_set_proxiable(options, 0);
158 0 : krb5_get_init_creds_opt_set_renew_life(options, 0);
159 0 : krb5_get_init_creds_opt_set_tkt_life(options, 5*60);
160 0 : }
161 :
162 0 : static void revert_changepw_options(krb5_get_init_creds_opt *options)
163 : {
164 : krb5_error_code kerr;
165 :
166 0 : set_canonicalize_option(options);
167 :
168 : /* Currently we do not set forwardable and proxiable explicitly, the flags
169 : * must be removed so that libkrb5 can take the defaults from krb5.conf */
170 0 : options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_FORWARDABLE);
171 0 : options->flags &= ~(KRB5_GET_INIT_CREDS_OPT_PROXIABLE);
172 :
173 0 : kerr = set_lifetime_options(options);
174 0 : if (kerr != 0) {
175 0 : DEBUG(SSSDBG_OP_FAILURE, ("set_lifetime_options failed.\n"));
176 : }
177 0 : }
178 :
179 :
180 0 : static errno_t sss_send_pac(krb5_authdata **pac_authdata)
181 : {
182 : struct sss_cli_req_data sss_data;
183 : int ret;
184 : int errnop;
185 :
186 0 : sss_data.len = pac_authdata[0]->length;
187 0 : sss_data.data = pac_authdata[0]->contents;
188 :
189 0 : ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data,
190 : NULL, NULL, &errnop);
191 0 : if (ret != NSS_STATUS_SUCCESS || errnop != 0) {
192 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_pac_make_request failed [%d][%d].\n",
193 : ret, errnop);
194 0 : return EIO;
195 : }
196 :
197 0 : return EOK;
198 : }
199 :
200 0 : static void sss_krb5_expire_callback_func(krb5_context context, void *data,
201 : krb5_timestamp password_expiration,
202 : krb5_timestamp account_expiration,
203 : krb5_boolean is_last_req)
204 : {
205 : int ret;
206 : uint32_t *blob;
207 : long exp_time;
208 0 : struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
209 :
210 0 : if (password_expiration == 0) {
211 0 : return;
212 : }
213 :
214 0 : exp_time = password_expiration - time(NULL);
215 0 : if (exp_time < 0 || exp_time > UINT32_MAX) {
216 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Time to expire out of range.\n");
217 0 : return;
218 : }
219 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "exp_time: [%ld]\n", exp_time);
220 :
221 0 : blob = talloc_array(kr->pd, uint32_t, 2);
222 0 : if (blob == NULL) {
223 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
224 0 : return;
225 : }
226 :
227 0 : blob[0] = SSS_PAM_USER_INFO_EXPIRE_WARN;
228 0 : blob[1] = (uint32_t) exp_time;
229 :
230 0 : ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, 2 * sizeof(uint32_t),
231 : (uint8_t *) blob);
232 0 : if (ret != EOK) {
233 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
234 : }
235 :
236 0 : return;
237 : }
238 :
239 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
240 : /*
241 : * TODO: These features generally would requires a significant refactoring
242 : * of SSSD and MIT krb5 doesn't support them anyway. They are listed here
243 : * simply as a reminder of things that might become future feature potential.
244 : *
245 : * 1. tokeninfo selection
246 : * 2. challenge
247 : * 3. discreet token/pin prompting
248 : * 4. interactive otp format correction
249 : * 5. nextOTP
250 : *
251 : */
252 : typedef int (*checker)(int c);
253 :
254 0 : static inline checker pick_checker(int format)
255 : {
256 0 : switch (format) {
257 : case KRB5_RESPONDER_OTP_FORMAT_DECIMAL:
258 0 : return isdigit;
259 : case KRB5_RESPONDER_OTP_FORMAT_HEXADECIMAL:
260 0 : return isxdigit;
261 : case KRB5_RESPONDER_OTP_FORMAT_ALPHANUMERIC:
262 0 : return isalnum;
263 : }
264 :
265 0 : return NULL;
266 : }
267 :
268 0 : static int token_pin_destructor(char *mem)
269 : {
270 0 : safezero(mem, strlen(mem));
271 0 : return 0;
272 : }
273 :
274 0 : static krb5_error_code tokeninfo_matches_2fa(TALLOC_CTX *mem_ctx,
275 : const krb5_responder_otp_tokeninfo *ti,
276 : const char *fa1, size_t fa1_len,
277 : const char *fa2, size_t fa2_len,
278 : char **out_token, char **out_pin)
279 : {
280 0 : char *token = NULL, *pin = NULL;
281 0 : checker check = NULL;
282 : int i;
283 :
284 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) {
285 0 : return ENOTSUP;
286 : }
287 :
288 0 : if (ti->challenge != NULL) {
289 0 : return ENOTSUP;
290 : }
291 :
292 : /* This is a non-sensical value. */
293 0 : if (ti->length == 0) {
294 0 : return EPROTO;
295 : }
296 :
297 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
298 0 : if (ti->length > 0 && ti->length != fa2_len) {
299 0 : DEBUG(SSSDBG_CRIT_FAILURE,
300 : "Expected [%d] and given [%zu] token size "
301 : "do not match.\n", ti->length, fa2_len);
302 0 : return EMSGSIZE;
303 : }
304 :
305 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) {
306 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) {
307 :
308 0 : pin = talloc_strndup(mem_ctx, fa1, fa1_len);
309 0 : if (pin == NULL) {
310 0 : talloc_free(token);
311 0 : return ENOMEM;
312 : }
313 0 : talloc_set_destructor(pin, token_pin_destructor);
314 :
315 0 : token = talloc_strndup(mem_ctx, fa2, fa2_len);
316 0 : if (token == NULL) {
317 0 : return ENOMEM;
318 : }
319 0 : talloc_set_destructor(token, token_pin_destructor);
320 :
321 0 : check = pick_checker(ti->format);
322 : }
323 : } else {
324 0 : token = talloc_asprintf(mem_ctx, "%s%s", fa1, fa2);
325 0 : if (token == NULL) {
326 0 : return ENOMEM;
327 : }
328 0 : talloc_set_destructor(token, token_pin_destructor);
329 :
330 0 : check = pick_checker(ti->format);
331 : }
332 : } else {
333 : /* Assuming PIN only required */
334 0 : pin = talloc_strndup(mem_ctx, fa1, fa1_len);
335 0 : if (pin == NULL) {
336 0 : return ENOMEM;
337 : }
338 0 : talloc_set_destructor(pin, token_pin_destructor);
339 : }
340 :
341 : /* If check is set, we need to verify the contents of the token. */
342 0 : for (i = 0; check != NULL && token[i] != '\0'; i++) {
343 0 : if (!check(token[i])) {
344 0 : talloc_free(token);
345 0 : talloc_free(pin);
346 0 : return EBADMSG;
347 : }
348 : }
349 :
350 0 : *out_token = token;
351 0 : *out_pin = pin;
352 0 : return 0;
353 : }
354 0 : static krb5_error_code tokeninfo_matches_pwd(TALLOC_CTX *mem_ctx,
355 : const krb5_responder_otp_tokeninfo *ti,
356 : const char *pwd, size_t len,
357 : char **out_token, char **out_pin)
358 : {
359 0 : char *token = NULL, *pin = NULL;
360 0 : checker check = NULL;
361 : int i;
362 :
363 :
364 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) {
365 0 : return ENOTSUP;
366 : }
367 :
368 0 : if (ti->challenge != NULL) {
369 0 : return ENOTSUP;
370 : }
371 :
372 : /* This is a non-sensical value. */
373 0 : if (ti->length == 0) {
374 0 : return EPROTO;
375 : }
376 :
377 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
378 : /* ASSUMPTION: authtok has one of the following formats:
379 : * 1. TokenValue
380 : * 2. PIN+TokenValue
381 : */
382 0 : token = talloc_strndup(mem_ctx, pwd, len);
383 0 : if (token == NULL) {
384 0 : return ENOMEM;
385 : }
386 0 : talloc_set_destructor(token, token_pin_destructor);
387 :
388 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) {
389 : /* If the server desires a separate pin, we will split it.
390 : * ASSUMPTION: Format of authtok is PIN+TokenValue. */
391 0 : if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) {
392 0 : if (ti->length < 1) {
393 0 : talloc_free(token);
394 0 : return ENOTSUP;
395 : }
396 :
397 0 : if (ti->length >= len) {
398 0 : talloc_free(token);
399 0 : return EMSGSIZE;
400 : }
401 :
402 : /* Copy the PIN from the front of the value. */
403 0 : pin = talloc_strndup(NULL, pwd, len - ti->length);
404 0 : if (pin == NULL) {
405 0 : talloc_free(token);
406 0 : return ENOMEM;
407 : }
408 0 : talloc_set_destructor(pin, token_pin_destructor);
409 :
410 : /* Remove the PIN from the front of the token value. */
411 0 : memmove(token, token + len - ti->length, ti->length + 1);
412 :
413 0 : check = pick_checker(ti->format);
414 : } else {
415 0 : if (ti->length > 0 && ti->length > len) {
416 0 : talloc_free(token);
417 0 : return EMSGSIZE;
418 : }
419 : }
420 : } else {
421 0 : if (ti->length > 0 && ti->length != len) {
422 0 : talloc_free(token);
423 0 : return EMSGSIZE;
424 : }
425 :
426 0 : check = pick_checker(ti->format);
427 : }
428 : } else {
429 0 : pin = talloc_strndup(mem_ctx, pwd, len);
430 0 : if (pin == NULL) {
431 0 : return ENOMEM;
432 : }
433 0 : talloc_set_destructor(pin, token_pin_destructor);
434 : }
435 :
436 : /* If check is set, we need to verify the contents of the token. */
437 0 : for (i = 0; check != NULL && token[i] != '\0'; i++) {
438 0 : if (!check(token[i])) {
439 0 : talloc_free(token);
440 0 : talloc_free(pin);
441 0 : return EBADMSG;
442 : }
443 : }
444 :
445 0 : *out_token = token;
446 0 : *out_pin = pin;
447 0 : return 0;
448 : }
449 :
450 0 : static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
451 : const krb5_responder_otp_tokeninfo *ti,
452 : struct sss_auth_token *auth_tok,
453 : char **out_token, char **out_pin)
454 : {
455 : int ret;
456 : const char *pwd;
457 : size_t len;
458 : const char *fa2;
459 : size_t fa2_len;
460 :
461 0 : switch (sss_authtok_get_type(auth_tok)) {
462 : case SSS_AUTHTOK_TYPE_PASSWORD:
463 0 : ret = sss_authtok_get_password(auth_tok, &pwd, &len);
464 0 : if (ret != EOK) {
465 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_password failed.\n");
466 0 : return ret;
467 : }
468 :
469 0 : return tokeninfo_matches_pwd(mem_ctx, ti, pwd, len, out_token, out_pin);
470 : break;
471 : case SSS_AUTHTOK_TYPE_2FA:
472 0 : ret = sss_authtok_get_2fa(auth_tok, &pwd, &len, &fa2, &fa2_len);
473 0 : if (ret != EOK) {
474 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_2fa failed.\n");
475 0 : return ret;
476 : }
477 :
478 0 : return tokeninfo_matches_2fa(mem_ctx, ti, pwd, len, fa2, fa2_len,
479 : out_token, out_pin);
480 : break;
481 : default:
482 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type.\n");
483 : }
484 :
485 0 : return EINVAL;
486 : }
487 :
488 0 : static krb5_error_code answer_otp(krb5_context ctx,
489 : struct krb5_req *kr,
490 : krb5_responder_context rctx)
491 : {
492 : krb5_responder_otp_challenge *chl;
493 0 : char *token = NULL, *pin = NULL;
494 : krb5_error_code ret;
495 : size_t i;
496 :
497 0 : ret = krb5_responder_otp_get_challenge(ctx, rctx, &chl);
498 0 : if (ret != EOK || chl == NULL) {
499 : /* Either an error, or nothing to do. */
500 0 : return ret;
501 : }
502 :
503 0 : if (chl->tokeninfo == NULL || chl->tokeninfo[0] == NULL) {
504 : /* No tokeninfos? Absurd! */
505 0 : ret = EINVAL;
506 0 : goto done;
507 : }
508 :
509 0 : kr->otp = true;
510 :
511 0 : if (kr->pd->cmd == SSS_PAM_PREAUTH) {
512 0 : for (i = 0; chl->tokeninfo[i] != NULL; i++) {
513 0 : DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
514 : i, chl->tokeninfo[i]->vendor);
515 0 : DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n",
516 : i, chl->tokeninfo[i]->token_id);
517 0 : DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n",
518 : i, chl->tokeninfo[i]->challenge);
519 0 : DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n",
520 : i, chl->tokeninfo[i]->flags);
521 : }
522 :
523 0 : if (chl->tokeninfo[0]->vendor != NULL) {
524 0 : kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor);
525 : }
526 0 : if (chl->tokeninfo[0]->token_id != NULL) {
527 0 : kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id);
528 : }
529 0 : if (chl->tokeninfo[0]->challenge != NULL) {
530 0 : kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge);
531 : }
532 : /* Allocation errors are ignored on purpose */
533 :
534 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Exit answer_otp during pre-auth.\n");
535 0 : return EAGAIN;
536 : }
537 :
538 : /* Find the first supported tokeninfo which matches our authtoken. */
539 0 : for (i = 0; chl->tokeninfo[i] != NULL; i++) {
540 0 : ret = tokeninfo_matches(kr, chl->tokeninfo[i], kr->pd->authtok,
541 : &token, &pin);
542 0 : if (ret == EOK) {
543 0 : break;
544 : }
545 :
546 0 : switch (ret) {
547 : case EBADMSG:
548 : case EMSGSIZE:
549 : case ENOTSUP:
550 : case EPROTO:
551 0 : break;
552 : default:
553 0 : goto done;
554 : }
555 : }
556 0 : if (chl->tokeninfo[i] == NULL) {
557 0 : DEBUG(SSSDBG_CRIT_FAILURE,
558 : "No tokeninfos found which match our credentials.\n");
559 0 : ret = EOK;
560 0 : goto done;
561 : }
562 :
563 0 : if (chl->tokeninfo[i]->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) {
564 : /* Don't let SSSD cache the OTP authtok since it is single-use. */
565 0 : ret = pam_add_response(kr->pd, SSS_OTP, 0, NULL);
566 0 : if (ret != EOK) {
567 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
568 0 : goto done;
569 : }
570 : }
571 :
572 : /* Respond with the appropriate answer. */
573 0 : ret = krb5_responder_otp_set_answer(ctx, rctx, i, token, pin);
574 : done:
575 0 : talloc_free(token);
576 0 : talloc_free(pin);
577 0 : krb5_responder_otp_challenge_free(ctx, rctx, chl);
578 0 : return ret;
579 : }
580 :
581 0 : static krb5_error_code sss_krb5_responder(krb5_context ctx,
582 : void *data,
583 : krb5_responder_context rctx)
584 : {
585 0 : struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
586 :
587 0 : if (kr == NULL) {
588 0 : return EINVAL;
589 : }
590 :
591 0 : return answer_otp(ctx, kr, rctx);
592 : }
593 : #endif
594 :
595 0 : static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
596 : const char *name, const char *banner,
597 : int num_prompts, krb5_prompt prompts[])
598 : {
599 : int ret;
600 0 : struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
601 :
602 0 : if (num_prompts != 0) {
603 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
604 0 : return KRB5_LIBOS_CANTREADPWD;
605 : }
606 :
607 0 : if (banner == NULL || *banner == '\0') {
608 0 : DEBUG(SSSDBG_FUNC_DATA,
609 : "Prompter called with empty banner, nothing to do.\n");
610 0 : return EOK;
611 : }
612 :
613 0 : DEBUG(SSSDBG_FUNC_DATA, "Prompter called with [%s].\n", banner);
614 :
615 0 : ret = pam_add_response(kr->pd, SSS_PAM_TEXT_MSG, strlen(banner)+1,
616 : (const uint8_t *) banner);
617 0 : if (ret != EOK) {
618 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
619 : }
620 :
621 0 : return EOK;
622 : }
623 :
624 :
625 0 : static krb5_error_code create_empty_cred(krb5_context ctx, krb5_principal princ,
626 : krb5_creds **_cred)
627 : {
628 : krb5_error_code kerr;
629 0 : krb5_creds *cred = NULL;
630 : krb5_data *krb5_realm;
631 :
632 0 : cred = calloc(sizeof(krb5_creds), 1);
633 0 : if (cred == NULL) {
634 0 : DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
635 0 : return ENOMEM;
636 : }
637 :
638 0 : kerr = krb5_copy_principal(ctx, princ, &cred->client);
639 0 : if (kerr != 0) {
640 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
641 0 : goto done;
642 : }
643 :
644 0 : krb5_realm = krb5_princ_realm(ctx, princ);
645 :
646 0 : kerr = krb5_build_principal_ext(ctx, &cred->server,
647 0 : krb5_realm->length, krb5_realm->data,
648 : KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
649 : krb5_realm->length, krb5_realm->data, 0);
650 0 : if (kerr != 0) {
651 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_build_principal_ext failed.\n");
652 0 : goto done;
653 : }
654 :
655 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Created empty krb5_creds.\n");
656 :
657 : done:
658 0 : if (kerr != 0) {
659 0 : krb5_free_cred_contents(ctx, cred);
660 0 : free(cred);
661 : } else {
662 0 : *_cred = cred;
663 : }
664 :
665 0 : return kerr;
666 : }
667 :
668 :
669 0 : static errno_t handle_randomized(char *in)
670 : {
671 : size_t ccname_len;
672 0 : char *ccname = NULL;
673 : int ret;
674 :
675 : /* We only treat the FILE type case in a special way due to the history
676 : * of storing FILE type ccache in /tmp and associated security issues */
677 0 : if (in[0] == '/') {
678 0 : ccname = in;
679 0 : } else if (strncmp(in, "FILE:", 5) == 0) {
680 0 : ccname = in + 5;
681 : } else {
682 0 : return EOK;
683 : }
684 :
685 0 : ccname_len = strlen(ccname);
686 0 : if (ccname_len >= 6 && strcmp(ccname + (ccname_len - 6), "XXXXXX") == 0) {
687 : /* NOTE: this call is only used to create a unique name, as later
688 : * krb5_cc_initialize() will unlink and recreate the file.
689 : * This is ok because this part of the code is called with
690 : * privileges already dropped when handling user ccache, or the ccache
691 : * is stored in a private directory. So we do not have huge issues if
692 : * something races, we mostly care only about not accidentally use
693 : * an existing name and thus failing in the process of saving the
694 : * cache. Malicious races can only be avoided by libkrb5 itself. */
695 0 : ret = sss_unique_filename(NULL, ccname);
696 0 : if (ret != EOK) {
697 0 : DEBUG(SSSDBG_CRIT_FAILURE,
698 : "mkstemp(\"%s\") failed [%d]: %s!\n",
699 : ccname, ret, strerror(ret));
700 0 : return ret;
701 : }
702 : }
703 :
704 0 : return EOK;
705 : }
706 :
707 : /* NOTE: callers rely on 'name' being *changed* if it needs to be randomized,
708 : * as they will then send the name back to the new name via the return call
709 : * k5c_attach_ccname_msg(). Callers will send in a copy of the name if they
710 : * do not care for changes. */
711 0 : static krb5_error_code create_ccache(char *ccname, krb5_creds *creds)
712 : {
713 0 : krb5_context kctx = NULL;
714 0 : krb5_ccache kcc = NULL;
715 : const char *type;
716 : krb5_error_code kerr;
717 : #ifdef HAVE_KRB5_CC_COLLECTION
718 : krb5_ccache cckcc;
719 0 : bool switch_to_cc = false;
720 : #endif
721 :
722 : /* Set a restrictive umask, just in case we end up creating any file */
723 0 : umask(SSS_DFL_X_UMASK);
724 :
725 : /* we create a new context here as the main process one may have been
726 : * opened as root and contain possibly references (even open handles ?)
727 : * to resources we do not have or do not want to have access to */
728 0 : kerr = krb5_init_context(&kctx);
729 0 : if (kerr) {
730 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
731 0 : return ERR_INTERNAL;
732 : }
733 :
734 0 : kerr = handle_randomized(ccname);
735 0 : if (kerr) {
736 0 : DEBUG(SSSDBG_CRIT_FAILURE, "handle_randomized failed: %d\n", kerr);
737 0 : goto done;
738 : }
739 :
740 0 : kerr = krb5_cc_resolve(kctx, ccname, &kcc);
741 0 : if (kerr) {
742 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
743 0 : goto done;
744 : }
745 :
746 0 : type = krb5_cc_get_type(kctx, kcc);
747 0 : DEBUG(SSSDBG_TRACE_ALL, "Initializing ccache of type [%s]\n", type);
748 :
749 : #ifdef HAVE_KRB5_CC_COLLECTION
750 0 : if (krb5_cc_support_switch(kctx, type)) {
751 0 : DEBUG(SSSDBG_TRACE_ALL, "CC supports switch\n");
752 0 : kerr = krb5_cc_set_default_name(kctx, ccname);
753 0 : if (kerr) {
754 0 : DEBUG(SSSDBG_TRACE_ALL, "Cannot set default name!\n");
755 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
756 0 : goto done;
757 : }
758 :
759 0 : kerr = krb5_cc_cache_match(kctx, creds->client, &cckcc);
760 0 : if (kerr == KRB5_CC_NOTFOUND) {
761 0 : DEBUG(SSSDBG_TRACE_ALL, "Match not found\n");
762 0 : kerr = krb5_cc_new_unique(kctx, type, NULL, &cckcc);
763 0 : switch_to_cc = true;
764 : }
765 0 : if (kerr) {
766 0 : DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_cache_match failed\n");
767 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
768 0 : goto done;
769 : }
770 0 : krb5_cc_close(kctx, kcc);
771 0 : kcc = cckcc;
772 : }
773 : #endif
774 :
775 0 : kerr = krb5_cc_initialize(kctx, kcc, creds->client);
776 0 : if (kerr) {
777 0 : DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_initialize failed\n");
778 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
779 0 : goto done;
780 : }
781 :
782 0 : kerr = krb5_cc_store_cred(kctx, kcc, creds);
783 0 : if (kerr) {
784 0 : DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_store_cred failed\n");
785 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
786 0 : goto done;
787 : }
788 :
789 : #ifdef HAVE_KRB5_CC_COLLECTION
790 0 : if (switch_to_cc) {
791 0 : DEBUG(SSSDBG_TRACE_ALL, "switch_to_cc\n");
792 0 : kerr = krb5_cc_switch(kctx, kcc);
793 0 : if (kerr) {
794 0 : DEBUG(SSSDBG_TRACE_ALL, "krb5_cc_switch\n");
795 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
796 0 : goto done;
797 : }
798 : }
799 : #endif
800 :
801 0 : DEBUG(SSSDBG_TRACE_ALL, "returning: %d\n", kerr);
802 : done:
803 0 : if (kcc) {
804 : /* FIXME: should we krb5_cc_destroy in case of error ? */
805 0 : krb5_cc_close(kctx, kcc);
806 : }
807 0 : return kerr;
808 : }
809 :
810 0 : static errno_t pack_response_packet(TALLOC_CTX *mem_ctx, errno_t error,
811 : struct response_data *resp_list,
812 : uint8_t **_buf, size_t *_len)
813 : {
814 : uint8_t *buf;
815 0 : size_t size = 0;
816 0 : size_t p = 0;
817 : struct response_data *pdr;
818 :
819 : /* A buffer with the following structure must be created:
820 : * int32_t status of the request (required)
821 : * message (zero or more)
822 : *
823 : * A message consists of:
824 : * int32_t type of the message
825 : * int32_t length of the following data
826 : * uint8_t[len] data
827 : */
828 :
829 0 : size = sizeof(int32_t);
830 :
831 0 : for (pdr = resp_list; pdr != NULL; pdr = pdr->next) {
832 0 : size += 2*sizeof(int32_t) + pdr->len;
833 : }
834 :
835 0 : buf = talloc_array(mem_ctx, uint8_t, size);
836 0 : if (!buf) {
837 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Insufficient memory to create message.\n");
838 0 : return ENOMEM;
839 : }
840 :
841 0 : SAFEALIGN_SET_INT32(&buf[p], error, &p);
842 :
843 0 : for (pdr = resp_list; pdr != NULL; pdr = pdr->next) {
844 0 : SAFEALIGN_SET_INT32(&buf[p], pdr->type, &p);
845 0 : SAFEALIGN_SET_INT32(&buf[p], pdr->len, &p);
846 0 : safealign_memcpy(&buf[p], pdr->data, pdr->len, &p);
847 : }
848 :
849 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "response packet size: [%zu]\n", p);
850 :
851 0 : *_buf = buf;
852 0 : *_len = p;
853 0 : return EOK;
854 : }
855 :
856 0 : static errno_t k5c_attach_otp_info_msg(struct krb5_req *kr)
857 : {
858 0 : uint8_t *msg = NULL;
859 : size_t msg_len;
860 : int ret;
861 0 : size_t vendor_len = 0;
862 0 : size_t token_id_len = 0;
863 0 : size_t challenge_len = 0;
864 0 : size_t idx = 0;
865 :
866 0 : msg_len = 3;
867 0 : if (kr->otp_vendor != NULL) {
868 0 : vendor_len = strlen(kr->otp_vendor);
869 0 : msg_len += vendor_len;
870 : }
871 :
872 0 : if (kr->otp_token_id != NULL) {
873 0 : token_id_len = strlen(kr->otp_token_id);
874 0 : msg_len += token_id_len;
875 : }
876 :
877 0 : if (kr->otp_challenge != NULL) {
878 0 : challenge_len = strlen(kr->otp_challenge);
879 0 : msg_len += challenge_len;
880 : }
881 :
882 0 : msg = talloc_zero_size(kr, msg_len);
883 0 : if (msg == NULL) {
884 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
885 0 : return ENOMEM;
886 : }
887 :
888 0 : if (kr->otp_vendor != NULL) {
889 0 : memcpy(msg, kr->otp_vendor, vendor_len);
890 : }
891 0 : idx += vendor_len +1;
892 :
893 0 : if (kr->otp_token_id != NULL) {
894 0 : memcpy(msg + idx, kr->otp_token_id, token_id_len);
895 : }
896 0 : idx += token_id_len +1;
897 :
898 0 : if (kr->otp_challenge != NULL) {
899 0 : memcpy(msg + idx, kr->otp_challenge, challenge_len);
900 : }
901 :
902 0 : ret = pam_add_response(kr->pd, SSS_PAM_OTP_INFO, msg_len, msg);
903 0 : talloc_zfree(msg);
904 :
905 0 : return ret;
906 : }
907 :
908 0 : static errno_t k5c_attach_ccname_msg(struct krb5_req *kr)
909 : {
910 0 : char *msg = NULL;
911 : int ret;
912 :
913 0 : if (kr->ccname == NULL) {
914 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error obtaining ccname.\n");
915 0 : return ERR_INTERNAL;
916 : }
917 :
918 0 : msg = talloc_asprintf(kr, "%s=%s",CCACHE_ENV_NAME, kr->ccname);
919 0 : if (msg == NULL) {
920 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
921 0 : return ENOMEM;
922 : }
923 :
924 0 : ret = pam_add_response(kr->pd, SSS_PAM_ENV_ITEM,
925 0 : strlen(msg) + 1, (uint8_t *)msg);
926 0 : talloc_zfree(msg);
927 :
928 0 : return ret;
929 : }
930 :
931 0 : static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error)
932 : {
933 : ssize_t written;
934 : uint8_t *buf;
935 : size_t len;
936 : int ret;
937 :
938 0 : DEBUG(SSSDBG_FUNC_DATA, "Received error code %d\n", error);
939 :
940 0 : ret = pack_response_packet(kr, error, kr->pd->resp_list, &buf, &len);
941 0 : if (ret != EOK) {
942 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
943 0 : return ret;
944 : }
945 :
946 0 : errno = 0;
947 0 : written = sss_atomic_write_s(fd, buf, len);
948 0 : if (written == -1) {
949 0 : ret = errno;
950 0 : DEBUG(SSSDBG_CRIT_FAILURE,
951 : "write failed [%d][%s].\n", ret, strerror(ret));
952 0 : return ret;
953 : }
954 :
955 0 : if (written != len) {
956 0 : DEBUG(SSSDBG_CRIT_FAILURE,
957 : "Write error, wrote [%zu] bytes, expected [%zu]\n",
958 : written, len);
959 0 : return EOK;
960 : }
961 :
962 0 : DEBUG(SSSDBG_TRACE_ALL, "Response sent.\n");
963 :
964 0 : return EOK;
965 : }
966 :
967 0 : static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr)
968 : {
969 : int ret;
970 : int64_t t[4];
971 : krb5_error_code kerr;
972 0 : char *upn = NULL;
973 0 : unsigned int upn_len = 0;
974 :
975 0 : t[0] = (int64_t) kr->creds->times.authtime;
976 0 : t[1] = (int64_t) kr->creds->times.starttime;
977 0 : t[2] = (int64_t) kr->creds->times.endtime;
978 0 : t[3] = (int64_t) kr->creds->times.renew_till;
979 :
980 0 : ret = pam_add_response(kr->pd, SSS_KRB5_INFO_TGT_LIFETIME,
981 : 4*sizeof(int64_t), (uint8_t *) t);
982 0 : if (ret != EOK) {
983 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
984 0 : goto done;
985 : }
986 :
987 0 : kerr = krb5_unparse_name_ext(kr->ctx, kr->creds->client, &upn, &upn_len);
988 0 : if (kerr != 0) {
989 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_unparse_name failed.\n");
990 0 : goto done;
991 : }
992 :
993 0 : ret = pam_add_response(kr->pd, SSS_KRB5_INFO_UPN, upn_len,
994 : (uint8_t *) upn);
995 0 : krb5_free_unparsed_name(kr->ctx, upn);
996 0 : if (ret != EOK) {
997 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pack_response_packet failed.\n");
998 0 : goto done;
999 : }
1000 :
1001 : done:
1002 0 : return ret;
1003 : }
1004 :
1005 0 : static krb5_error_code validate_tgt(struct krb5_req *kr)
1006 : {
1007 : krb5_error_code kerr;
1008 : krb5_error_code kt_err;
1009 0 : char *principal = NULL;
1010 : krb5_keytab keytab;
1011 : krb5_kt_cursor cursor;
1012 : krb5_keytab_entry entry;
1013 : krb5_verify_init_creds_opt opt;
1014 0 : krb5_principal validation_princ = NULL;
1015 0 : bool realm_entry_found = false;
1016 0 : krb5_ccache validation_ccache = NULL;
1017 0 : krb5_authdata **pac_authdata = NULL;
1018 :
1019 0 : memset(&keytab, 0, sizeof(keytab));
1020 0 : kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab);
1021 0 : if (kerr != 0) {
1022 0 : DEBUG(SSSDBG_CRIT_FAILURE, "error resolving keytab [%s], " \
1023 : "not verifying TGT.\n", kr->keytab);
1024 0 : return kerr;
1025 : }
1026 :
1027 0 : memset(&cursor, 0, sizeof(cursor));
1028 0 : kerr = krb5_kt_start_seq_get(kr->ctx, keytab, &cursor);
1029 0 : if (kerr != 0) {
1030 0 : DEBUG(SSSDBG_CRIT_FAILURE, "error reading keytab [%s], " \
1031 : "not verifying TGT.\n", kr->keytab);
1032 0 : return kerr;
1033 : }
1034 :
1035 : /* We look for the first entry from our realm or take the last one */
1036 0 : memset(&entry, 0, sizeof(entry));
1037 0 : while ((kt_err = krb5_kt_next_entry(kr->ctx, keytab, &entry, &cursor)) == 0) {
1038 0 : if (validation_princ != NULL) {
1039 0 : krb5_free_principal(kr->ctx, validation_princ);
1040 0 : validation_princ = NULL;
1041 : }
1042 0 : kerr = krb5_copy_principal(kr->ctx, entry.principal,
1043 : &validation_princ);
1044 0 : if (kerr != 0) {
1045 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
1046 0 : goto done;
1047 : }
1048 :
1049 0 : kerr = sss_krb5_free_keytab_entry_contents(kr->ctx, &entry);
1050 0 : if (kerr != 0) {
1051 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Failed to free keytab entry.\n");
1052 : }
1053 0 : memset(&entry, 0, sizeof(entry));
1054 :
1055 0 : if (krb5_realm_compare(kr->ctx, validation_princ, kr->creds->client)) {
1056 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1057 : "Found keytab entry with the realm of the credential.\n");
1058 0 : realm_entry_found = true;
1059 0 : break;
1060 : }
1061 : }
1062 :
1063 0 : if (!realm_entry_found) {
1064 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1065 : "Keytab entry with the realm of the credential not found "
1066 : "in keytab. Using the last entry.\n");
1067 : }
1068 :
1069 : /* Close the keytab here. Even though we're using cursors, the file
1070 : * handle is stored in the krb5_keytab structure, and it gets
1071 : * overwritten when the verify_init_creds() call below creates its own
1072 : * cursor, creating a leak. */
1073 0 : kerr = krb5_kt_end_seq_get(kr->ctx, keytab, &cursor);
1074 0 : if (kerr != 0) {
1075 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_end_seq_get failed, " \
1076 : "not verifying TGT.\n");
1077 0 : goto done;
1078 : }
1079 :
1080 : /* check if we got any errors from krb5_kt_next_entry */
1081 0 : if (kt_err != 0 && kt_err != KRB5_KT_END) {
1082 0 : DEBUG(SSSDBG_CRIT_FAILURE, "error reading keytab [%s], " \
1083 : "not verifying TGT.\n", kr->keytab);
1084 0 : goto done;
1085 : }
1086 :
1087 : /* Get the principal to which the key belongs, for logging purposes. */
1088 0 : principal = NULL;
1089 0 : kerr = krb5_unparse_name(kr->ctx, validation_princ, &principal);
1090 0 : if (kerr != 0) {
1091 0 : DEBUG(SSSDBG_CRIT_FAILURE, "internal error parsing principal name, "
1092 : "not verifying TGT.\n");
1093 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1094 0 : goto done;
1095 : }
1096 :
1097 :
1098 0 : krb5_verify_init_creds_opt_init(&opt);
1099 0 : kerr = krb5_verify_init_creds(kr->ctx, kr->creds, validation_princ, keytab,
1100 : &validation_ccache, &opt);
1101 :
1102 0 : if (kerr == 0) {
1103 0 : DEBUG(SSSDBG_TRACE_FUNC, "TGT verified using key for [%s].\n",
1104 : principal);
1105 : } else {
1106 0 : DEBUG(SSSDBG_CRIT_FAILURE ,"TGT failed verification using key " \
1107 : "for [%s].\n", principal);
1108 0 : goto done;
1109 : }
1110 :
1111 : /* Try to find and send the PAC to the PAC responder.
1112 : * Failures are not critical. */
1113 0 : if (kr->send_pac) {
1114 0 : kerr = sss_extract_pac(kr->ctx, validation_ccache, validation_princ,
1115 0 : kr->creds->client, keytab, &pac_authdata);
1116 0 : if (kerr != 0) {
1117 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_extract_and_send_pac failed, group " \
1118 : "membership for user with principal [%s] " \
1119 : "might not be correct.\n", kr->name);
1120 0 : kerr = 0;
1121 0 : goto done;
1122 : }
1123 :
1124 0 : kerr = sss_send_pac(pac_authdata);
1125 0 : krb5_free_authdata(kr->ctx, pac_authdata);
1126 0 : if (kerr != 0) {
1127 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_send_pac failed, group " \
1128 : "membership for user with principal [%s] " \
1129 : "might not be correct.\n", kr->name);
1130 0 : kerr = 0;
1131 : }
1132 : }
1133 :
1134 : done:
1135 0 : if (validation_ccache != NULL) {
1136 0 : krb5_cc_destroy(kr->ctx, validation_ccache);
1137 : }
1138 :
1139 0 : if (krb5_kt_close(kr->ctx, keytab) != 0) {
1140 0 : DEBUG(SSSDBG_MINOR_FAILURE, "krb5_kt_close failed\n");
1141 : }
1142 0 : if (validation_princ != NULL) {
1143 0 : krb5_free_principal(kr->ctx, validation_princ);
1144 : }
1145 0 : if (principal != NULL) {
1146 0 : sss_krb5_free_unparsed_name(kr->ctx, principal);
1147 : }
1148 :
1149 0 : return kerr;
1150 :
1151 : }
1152 :
1153 0 : static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
1154 : krb5_principal princ,
1155 : krb5_keytab keytab,
1156 : char *ccname)
1157 : {
1158 0 : krb5_error_code kerr = 0;
1159 : krb5_creds creds;
1160 : krb5_get_init_creds_opt options;
1161 :
1162 0 : memset(&creds, 0, sizeof(creds));
1163 0 : memset(&options, 0, sizeof(options));
1164 :
1165 0 : krb5_get_init_creds_opt_set_address_list(&options, NULL);
1166 0 : krb5_get_init_creds_opt_set_forwardable(&options, 0);
1167 0 : krb5_get_init_creds_opt_set_proxiable(&options, 0);
1168 0 : set_canonicalize_option(&options);
1169 :
1170 0 : kerr = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL,
1171 : &options);
1172 0 : if (kerr != 0) {
1173 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1174 0 : return kerr;
1175 : }
1176 :
1177 : /* Use the updated principal in the creds in case canonicalized */
1178 0 : kerr = create_ccache(ccname, &creds);
1179 0 : if (kerr != 0) {
1180 0 : goto done;
1181 : }
1182 0 : kerr = 0;
1183 :
1184 : done:
1185 0 : krb5_free_cred_contents(ctx, &creds);
1186 :
1187 0 : return kerr;
1188 :
1189 : }
1190 :
1191 0 : static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
1192 : const char *password)
1193 : {
1194 : const char *realm_name;
1195 : int realm_length;
1196 : krb5_error_code kerr;
1197 : char *cc_name;
1198 :
1199 0 : kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options,
1200 : sss_krb5_expire_callback_func,
1201 : kr);
1202 0 : if (kerr != 0) {
1203 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1204 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1205 : "Failed to set expire callback, continue without.\n");
1206 : }
1207 :
1208 0 : sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length);
1209 0 : if (realm_length == 0) {
1210 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
1211 0 : return KRB5KRB_ERR_GENERIC;
1212 : }
1213 :
1214 0 : DEBUG(SSSDBG_TRACE_FUNC,
1215 : "Attempting kinit for realm [%s]\n",realm_name);
1216 0 : kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
1217 : discard_const(password),
1218 : sss_krb5_prompter, kr, 0,
1219 : NULL, kr->options);
1220 0 : if (kr->pd->cmd == SSS_PAM_PREAUTH) {
1221 : /* Any errors are ignored during pre-auth, only data is collected to
1222 : * be send back to the client.*/
1223 0 : DEBUG(SSSDBG_TRACE_FUNC,
1224 : "krb5_get_init_creds_password returned [%d} during pre-auth.\n",
1225 : kerr);
1226 0 : return 0;
1227 : } else {
1228 0 : if (kerr != 0) {
1229 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1230 0 : return kerr;
1231 : }
1232 : }
1233 :
1234 0 : if (kr->validate) {
1235 0 : kerr = validate_tgt(kr);
1236 0 : if (kerr != 0) {
1237 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1238 0 : return kerr;
1239 : }
1240 :
1241 : } else {
1242 0 : DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n");
1243 : }
1244 :
1245 : /* If kr->ccname is cache collection (DIR:/...), we want to work
1246 : * directly with file ccache (DIR::/...), but cache collection
1247 : * should be returned back to back end.
1248 : */
1249 0 : cc_name = sss_get_ccache_name_for_principal(kr->pd, kr->ctx,
1250 0 : kr->creds->client,
1251 0 : kr->ccname);
1252 0 : if (cc_name == NULL) {
1253 0 : cc_name = kr->ccname;
1254 : }
1255 :
1256 : /* Use the updated principal in the creds in case canonicalized */
1257 0 : kerr = create_ccache(cc_name, kr->creds);
1258 0 : if (kerr != 0) {
1259 0 : goto done;
1260 : }
1261 :
1262 : /* Successfull authentication! Check if ccache contains the
1263 : * right principal...
1264 : */
1265 0 : kerr = sss_krb5_check_ccache_princ(kr->ctx, kr->ccname, kr->creds->client);
1266 0 : if (kerr) {
1267 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1268 : "No ccache for %s in %s?\n", kr->upn, kr->ccname);
1269 0 : goto done;
1270 : }
1271 :
1272 0 : kerr = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname,
1273 : kr->uid, kr->gid);
1274 0 : if (kerr != EOK) {
1275 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1276 : "Failed to remove old ccache file [%s], "
1277 : "please remove it manually.\n", kr->old_ccname);
1278 : }
1279 :
1280 0 : kerr = add_ticket_times_and_upn_to_response(kr);
1281 0 : if (kerr != 0) {
1282 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1283 : "add_ticket_times_and_upn_to_response failed.\n");
1284 : }
1285 :
1286 0 : kerr = 0;
1287 :
1288 : done:
1289 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1290 :
1291 0 : return kerr;
1292 :
1293 : }
1294 :
1295 0 : static errno_t map_krb5_error(krb5_error_code kerr)
1296 : {
1297 0 : if (kerr != 0) {
1298 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1299 : }
1300 :
1301 0 : switch (kerr) {
1302 : case 0:
1303 0 : return ERR_OK;
1304 :
1305 : case KRB5_LIBOS_CANTREADPWD:
1306 0 : return ERR_NO_CREDS;
1307 :
1308 : case KRB5_KDCREP_SKEW:
1309 : case KRB5KRB_AP_ERR_SKEW:
1310 : case KRB5_KDC_UNREACH:
1311 : case KRB5_REALM_CANT_RESOLVE:
1312 0 : return ERR_NETWORK_IO;
1313 :
1314 : case KRB5KDC_ERR_CLIENT_REVOKED:
1315 0 : return ERR_ACCOUNT_EXPIRED;
1316 :
1317 : case KRB5KDC_ERR_KEY_EXP:
1318 0 : return ERR_CREDS_EXPIRED;
1319 :
1320 : case KRB5KRB_AP_ERR_BAD_INTEGRITY:
1321 0 : return ERR_AUTH_FAILED;
1322 :
1323 : /* ERR_CREDS_INVALID is used to indicate to the IPA provider that trying
1324 : * password migration would make sense. All Kerberos error codes which can
1325 : * be seen while migrating LDAP users to IPA should be added here. */
1326 : case KRB5_PROG_ETYPE_NOSUPP:
1327 : case KRB5_PREAUTH_FAILED:
1328 : case KRB5KDC_ERR_PREAUTH_FAILED:
1329 0 : return ERR_CREDS_INVALID;
1330 :
1331 : /* Please do not remove KRB5KRB_ERR_GENERIC here, it is a _generic_ error
1332 : * code and we cannot make any assumptions about the reason for the error.
1333 : * As a consequence we cannot return a different error code than a generic
1334 : * one which unfortunately might result in a unspecific system error
1335 : * message to the user.
1336 : *
1337 : * If there are cases where libkrb5 calls return KRB5KRB_ERR_GENERIC where
1338 : * SSSD should behave differently this has to be detected by different
1339 : * means, e.g. by evaluation error messages, and then the error code
1340 : * should be changed to a more suitable KRB5* error code or immediately to
1341 : * a SSSD ERR_* error code to avoid the default handling here. */
1342 : case KRB5KRB_ERR_GENERIC:
1343 : default:
1344 0 : return ERR_INTERNAL;
1345 : }
1346 : }
1347 :
1348 0 : static errno_t changepw_child(struct krb5_req *kr, bool prelim)
1349 : {
1350 : int ret;
1351 0 : krb5_error_code kerr = 0;
1352 0 : const char *password = NULL;
1353 0 : const char *newpassword = NULL;
1354 0 : int result_code = -1;
1355 : krb5_data result_code_string;
1356 : krb5_data result_string;
1357 0 : char *user_error_message = NULL;
1358 : size_t user_resp_len;
1359 : uint8_t *user_resp;
1360 0 : krb5_prompter_fct prompter = NULL;
1361 : const char *realm_name;
1362 : int realm_length;
1363 : size_t msg_len;
1364 : uint8_t *msg;
1365 : uint32_t user_info_type;
1366 :
1367 0 : DEBUG(SSSDBG_TRACE_LIBS, "Password change operation\n");
1368 :
1369 0 : ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
1370 0 : if (ret != EOK) {
1371 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1372 : "Failed to fetch current password [%d] %s.\n",
1373 : ret, strerror(ret));
1374 0 : return ERR_NO_CREDS;
1375 : }
1376 :
1377 0 : if (!prelim) {
1378 : /* We do not need a password expiration warning here. */
1379 0 : prompter = sss_krb5_prompter;
1380 : }
1381 :
1382 0 : set_changepw_options(kr->options);
1383 0 : sss_krb5_princ_realm(kr->ctx, kr->princ, &realm_name, &realm_length);
1384 0 : if (realm_length == 0) {
1385 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
1386 0 : return ERR_INTERNAL;
1387 : }
1388 :
1389 0 : DEBUG(SSSDBG_TRACE_FUNC,
1390 : "Attempting kinit for realm [%s]\n",realm_name);
1391 0 : kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
1392 : discard_const(password),
1393 : prompter, kr, 0,
1394 : SSSD_KRB5_CHANGEPW_PRINCIPAL,
1395 : kr->options);
1396 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1397 : "chpass is%s using OTP\n", kr->otp ? "" : " not");
1398 0 : if (kerr != 0) {
1399 0 : ret = pack_user_info_chpass_error(kr->pd, "Old password not accepted.",
1400 : &msg_len, &msg);
1401 0 : if (ret != EOK) {
1402 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1403 : "pack_user_info_chpass_error failed.\n");
1404 : } else {
1405 0 : ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, msg_len,
1406 : msg);
1407 0 : if (ret != EOK) {
1408 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1409 : "pam_add_response failed.\n");
1410 : }
1411 : }
1412 0 : return kerr;
1413 : }
1414 :
1415 0 : sss_authtok_set_empty(kr->pd->authtok);
1416 :
1417 0 : if (prelim) {
1418 0 : DEBUG(SSSDBG_TRACE_LIBS,
1419 : "Initial authentication for change password operation "
1420 : "successful.\n");
1421 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1422 0 : return EOK;
1423 : }
1424 :
1425 0 : ret = sss_authtok_get_password(kr->pd->newauthtok, &newpassword, NULL);
1426 0 : if (ret != EOK) {
1427 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to fetch new password [%d] %s.\n",
1428 : ret, strerror(ret));
1429 0 : return ERR_NO_CREDS;
1430 : }
1431 :
1432 0 : memset(&result_code_string, 0, sizeof(krb5_data));
1433 0 : memset(&result_string, 0, sizeof(krb5_data));
1434 0 : kerr = krb5_change_password(kr->ctx, kr->creds,
1435 : discard_const(newpassword), &result_code,
1436 : &result_code_string, &result_string);
1437 :
1438 0 : if (kerr == KRB5_KDC_UNREACH) {
1439 0 : return ERR_NETWORK_IO;
1440 : }
1441 :
1442 0 : if (kerr != 0 || result_code != 0) {
1443 0 : if (kerr != 0) {
1444 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1445 : }
1446 :
1447 0 : if (result_code_string.length > 0) {
1448 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1449 : "krb5_change_password failed [%d][%.*s].\n", result_code,
1450 : result_code_string.length, result_code_string.data);
1451 0 : user_error_message = talloc_strndup(kr->pd, result_code_string.data,
1452 0 : result_code_string.length);
1453 0 : if (user_error_message == NULL) {
1454 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
1455 : }
1456 : }
1457 :
1458 0 : if (result_string.length > 0 && result_string.data[0] != '\0') {
1459 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1460 : "krb5_change_password failed [%d][%.*s].\n", result_code,
1461 : result_string.length, result_string.data);
1462 0 : talloc_free(user_error_message);
1463 0 : user_error_message = talloc_strndup(kr->pd, result_string.data,
1464 0 : result_string.length);
1465 0 : if (user_error_message == NULL) {
1466 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
1467 : }
1468 0 : } else if (result_code == KRB5_KPASSWD_SOFTERROR) {
1469 0 : user_error_message = talloc_strdup(kr->pd, "Please make sure the "
1470 : "password meets the complexity constraints.");
1471 0 : if (user_error_message == NULL) {
1472 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
1473 : }
1474 : }
1475 :
1476 0 : if (user_error_message != NULL) {
1477 0 : ret = pack_user_info_chpass_error(kr->pd, user_error_message,
1478 : &user_resp_len, &user_resp);
1479 0 : if (ret != EOK) {
1480 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1481 : "pack_user_info_chpass_error failed.\n");
1482 : } else {
1483 0 : ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, user_resp_len,
1484 : user_resp);
1485 0 : if (ret != EOK) {
1486 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1487 : "pack_response_packet failed.\n");
1488 : }
1489 : }
1490 : }
1491 :
1492 0 : return ERR_CHPASS_FAILED;
1493 : }
1494 :
1495 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1496 :
1497 0 : if (kr->otp == true) {
1498 0 : user_info_type = SSS_PAM_USER_INFO_OTP_CHPASS;
1499 0 : ret = pam_add_response(kr->pd, SSS_PAM_USER_INFO, sizeof(uint32_t),
1500 : (const uint8_t *) &user_info_type);
1501 0 : if (ret != EOK) {
1502 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
1503 : /* Not fatal */
1504 : }
1505 :
1506 0 : sss_authtok_set_empty(kr->pd->newauthtok);
1507 0 : return map_krb5_error(kerr);
1508 : }
1509 :
1510 : /* We changed some of the gic options for the password change, now we have
1511 : * to change them back to get a fresh TGT. */
1512 0 : revert_changepw_options(kr->options);
1513 :
1514 0 : kerr = get_and_save_tgt(kr, newpassword);
1515 :
1516 0 : sss_authtok_set_empty(kr->pd->newauthtok);
1517 :
1518 0 : if (kerr == 0) {
1519 0 : kerr = k5c_attach_ccname_msg(kr);
1520 : }
1521 0 : return map_krb5_error(kerr);
1522 : }
1523 :
1524 0 : static errno_t tgt_req_child(struct krb5_req *kr)
1525 : {
1526 0 : const char *password = NULL;
1527 : krb5_error_code kerr;
1528 : int ret;
1529 :
1530 0 : DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n");
1531 :
1532 : /* No password is needed for pre-auth, or if we have 2FA */
1533 0 : if (kr->pd->cmd != SSS_PAM_PREAUTH
1534 0 : && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) {
1535 0 : ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
1536 0 : switch (ret) {
1537 : case EOK:
1538 0 : break;
1539 :
1540 : case EACCES:
1541 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid authtok type\n");
1542 0 : return ERR_INVALID_CRED_TYPE;
1543 : break;
1544 :
1545 : default:
1546 0 : DEBUG(SSSDBG_OP_FAILURE, "No credentials available\n");
1547 0 : return ERR_NO_CREDS;
1548 : break;
1549 : }
1550 : }
1551 :
1552 0 : kerr = get_and_save_tgt(kr, password);
1553 :
1554 0 : if (kerr != KRB5KDC_ERR_KEY_EXP) {
1555 0 : if (kr->pd->cmd == SSS_PAM_PREAUTH) {
1556 : /* add OTP tokeninfo messge if available */
1557 0 : if (kr->otp) {
1558 0 : kerr = k5c_attach_otp_info_msg(kr);
1559 : }
1560 : } else {
1561 0 : if (kerr == 0) {
1562 0 : kerr = k5c_attach_ccname_msg(kr);
1563 : }
1564 : }
1565 0 : ret = map_krb5_error(kerr);
1566 0 : goto done;
1567 : }
1568 :
1569 : /* If the password is expired the KDC will always return
1570 : KRB5KDC_ERR_KEY_EXP regardless if the supplied password is correct or
1571 : not. In general the password can still be used to get a changepw ticket.
1572 : So we validate the password by trying to get a changepw ticket. */
1573 0 : DEBUG(SSSDBG_TRACE_LIBS, "Password was expired\n");
1574 0 : kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx,
1575 : kr->options,
1576 : NULL, NULL);
1577 0 : if (kerr != 0) {
1578 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1579 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1580 : "Failed to unset expire callback, continue ...\n");
1581 : }
1582 :
1583 0 : set_changepw_options(kr->options);
1584 0 : kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
1585 : discard_const(password),
1586 : sss_krb5_prompter, kr, 0,
1587 : SSSD_KRB5_CHANGEPW_PRINCIPAL,
1588 : kr->options);
1589 :
1590 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1591 0 : if (kerr == 0) {
1592 0 : ret = ERR_CREDS_EXPIRED;
1593 :
1594 : /* If the password is expired we can safely remove the ccache from the
1595 : * cache and disk if it is not actively used anymore. This will allow
1596 : * to create a new random ccache if sshd with privilege separation is
1597 : * used. */
1598 0 : if (kr->old_cc_active == false && kr->old_ccname) {
1599 0 : ret = safe_remove_old_ccache_file(kr->old_ccname, NULL,
1600 : kr->uid, kr->gid);
1601 0 : if (ret != EOK) {
1602 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1603 : "Failed to remove old ccache file [%s], "
1604 : "please remove it manually.\n", kr->old_ccname);
1605 : }
1606 0 : ret = ERR_CREDS_EXPIRED_CCACHE;
1607 : }
1608 : } else {
1609 0 : ret = map_krb5_error(kerr);
1610 : }
1611 :
1612 : done:
1613 0 : sss_authtok_set_empty(kr->pd->authtok);
1614 0 : return ret;
1615 : }
1616 :
1617 0 : static errno_t kuserok_child(struct krb5_req *kr)
1618 : {
1619 : krb5_boolean access_allowed;
1620 : krb5_error_code kerr;
1621 :
1622 0 : DEBUG(SSSDBG_TRACE_LIBS, "Verifying if principal can log in as user\n");
1623 :
1624 : /* krb5_kuserok tries to verify that kr->pd->user is a locally known
1625 : * account, so we have to unset _SSS_LOOPS to make getpwnam() work. */
1626 0 : if (unsetenv("_SSS_LOOPS") != 0) {
1627 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unset _SSS_LOOPS, "
1628 : "krb5_kuserok will most certainly fail.\n");
1629 : }
1630 :
1631 0 : kerr = krb5_set_default_realm(kr->ctx, kr->realm);
1632 0 : if (kerr != 0) {
1633 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_set_default_realm failed, "
1634 : "krb5_kuserok may fail.\n");
1635 : }
1636 :
1637 0 : access_allowed = krb5_kuserok(kr->ctx, kr->princ, kr->pd->user);
1638 0 : DEBUG(SSSDBG_TRACE_LIBS,
1639 : "Access was %s\n", access_allowed ? "allowed" : "denied");
1640 :
1641 0 : if (access_allowed) {
1642 0 : return EOK;
1643 : }
1644 :
1645 0 : return ERR_AUTH_DENIED;
1646 : }
1647 :
1648 0 : static errno_t renew_tgt_child(struct krb5_req *kr)
1649 : {
1650 : const char *ccname;
1651 0 : krb5_ccache ccache = NULL;
1652 : krb5_error_code kerr;
1653 : int ret;
1654 :
1655 0 : DEBUG(SSSDBG_TRACE_LIBS, "Renewing a ticket\n");
1656 :
1657 0 : ret = sss_authtok_get_ccfile(kr->pd->authtok, &ccname, NULL);
1658 0 : if (ret != EOK) {
1659 0 : DEBUG(SSSDBG_OP_FAILURE,
1660 : "Unsupported authtok type for TGT renewal [%d].\n",
1661 : sss_authtok_get_type(kr->pd->authtok));
1662 0 : return ERR_INVALID_CRED_TYPE;
1663 : }
1664 :
1665 0 : kerr = krb5_cc_resolve(kr->ctx, ccname, &ccache);
1666 0 : if (kerr != 0) {
1667 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1668 0 : goto done;
1669 : }
1670 :
1671 0 : kerr = krb5_get_renewed_creds(kr->ctx, kr->creds, kr->princ, ccache, NULL);
1672 0 : if (kerr != 0) {
1673 0 : goto done;
1674 : }
1675 :
1676 0 : if (kr->validate) {
1677 0 : kerr = validate_tgt(kr);
1678 0 : if (kerr != 0) {
1679 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1680 0 : goto done;
1681 : }
1682 :
1683 : } else {
1684 0 : DEBUG(SSSDBG_CONF_SETTINGS, "TGT validation is disabled.\n");
1685 : }
1686 :
1687 0 : kerr = krb5_cc_initialize(kr->ctx, ccache, kr->princ);
1688 0 : if (kerr != 0) {
1689 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1690 0 : goto done;
1691 : }
1692 :
1693 0 : kerr = krb5_cc_store_cred(kr->ctx, ccache, kr->creds);
1694 0 : if (kerr != 0) {
1695 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
1696 0 : goto done;
1697 : }
1698 :
1699 0 : kerr = add_ticket_times_and_upn_to_response(kr);
1700 0 : if (kerr != 0) {
1701 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1702 : "add_ticket_times_and_upn_to_response failed.\n");
1703 : }
1704 :
1705 0 : kerr = k5c_attach_ccname_msg(kr);
1706 :
1707 : done:
1708 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1709 :
1710 0 : if (ccache != NULL) {
1711 0 : krb5_cc_close(kr->ctx, ccache);
1712 : }
1713 :
1714 0 : return map_krb5_error(kerr);
1715 : }
1716 :
1717 0 : static errno_t create_empty_ccache(struct krb5_req *kr)
1718 : {
1719 0 : krb5_creds *creds = NULL;
1720 : krb5_error_code kerr;
1721 :
1722 0 : if (kr->old_cc_valid == false) {
1723 0 : DEBUG(SSSDBG_TRACE_LIBS, "Creating empty ccache\n");
1724 0 : kerr = create_empty_cred(kr->ctx, kr->princ, &creds);
1725 0 : if (kerr == 0) {
1726 0 : kerr = create_ccache(kr->ccname, creds);
1727 : }
1728 : } else {
1729 0 : DEBUG(SSSDBG_TRACE_LIBS, "Existing ccache still valid, reusing\n");
1730 0 : kerr = 0;
1731 : }
1732 :
1733 0 : if (kerr == 0) {
1734 0 : kerr = k5c_attach_ccname_msg(kr);
1735 : }
1736 :
1737 0 : krb5_free_creds(kr->ctx, creds);
1738 :
1739 0 : return map_krb5_error(kerr);
1740 : }
1741 :
1742 0 : static errno_t unpack_authtok(struct sss_auth_token *tok,
1743 : uint8_t *buf, size_t size, size_t *p)
1744 : {
1745 : uint32_t auth_token_type;
1746 : uint32_t auth_token_length;
1747 0 : errno_t ret = EOK;
1748 :
1749 0 : SAFEALIGN_COPY_UINT32_CHECK(&auth_token_type, buf + *p, size, p);
1750 0 : SAFEALIGN_COPY_UINT32_CHECK(&auth_token_length, buf + *p, size, p);
1751 0 : if ((*p + auth_token_length) > size) {
1752 0 : return EINVAL;
1753 : }
1754 0 : switch (auth_token_type) {
1755 : case SSS_AUTHTOK_TYPE_EMPTY:
1756 0 : sss_authtok_set_empty(tok);
1757 0 : break;
1758 : case SSS_AUTHTOK_TYPE_PASSWORD:
1759 0 : ret = sss_authtok_set_password(tok, (char *)(buf + *p), 0);
1760 0 : break;
1761 : case SSS_AUTHTOK_TYPE_CCFILE:
1762 0 : ret = sss_authtok_set_ccfile(tok, (char *)(buf + *p), 0);
1763 0 : break;
1764 : case SSS_AUTHTOK_TYPE_2FA:
1765 0 : ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, (buf + *p),
1766 : auth_token_length);
1767 0 : break;
1768 : default:
1769 0 : return EINVAL;
1770 : }
1771 :
1772 0 : if (ret == EOK) {
1773 0 : *p += auth_token_length;
1774 : }
1775 0 : return ret;
1776 : }
1777 :
1778 0 : static errno_t unpack_buffer(uint8_t *buf, size_t size,
1779 : struct krb5_req *kr, uint32_t *offline)
1780 : {
1781 0 : size_t p = 0;
1782 : uint32_t len;
1783 : uint32_t validate;
1784 : uint32_t send_pac;
1785 : uint32_t use_enterprise_princ;
1786 : struct pam_data *pd;
1787 : errno_t ret;
1788 :
1789 0 : DEBUG(SSSDBG_TRACE_LIBS, "total buffer size: [%zu]\n", size);
1790 :
1791 0 : if (!offline || !kr) return EINVAL;
1792 :
1793 0 : pd = create_pam_data(kr);
1794 0 : if (pd == NULL) {
1795 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
1796 0 : return ENOMEM;
1797 : }
1798 0 : kr->pd = pd;
1799 :
1800 0 : SAFEALIGN_COPY_UINT32_CHECK(&pd->cmd, buf + p, size, &p);
1801 0 : SAFEALIGN_COPY_UINT32_CHECK(&kr->uid, buf + p, size, &p);
1802 0 : SAFEALIGN_COPY_UINT32_CHECK(&kr->gid, buf + p, size, &p);
1803 0 : SAFEALIGN_COPY_UINT32_CHECK(&validate, buf + p, size, &p);
1804 0 : kr->validate = (validate == 0) ? false : true;
1805 0 : SAFEALIGN_COPY_UINT32_CHECK(offline, buf + p, size, &p);
1806 0 : SAFEALIGN_COPY_UINT32_CHECK(&send_pac, buf + p, size, &p);
1807 0 : kr->send_pac = (send_pac == 0) ? false : true;
1808 0 : SAFEALIGN_COPY_UINT32_CHECK(&use_enterprise_princ, buf + p, size, &p);
1809 0 : kr->use_enterprise_princ = (use_enterprise_princ == 0) ? false : true;
1810 0 : SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
1811 0 : if (len > size - p) return EINVAL;
1812 0 : kr->upn = talloc_strndup(pd, (char *)(buf + p), len);
1813 0 : if (kr->upn == NULL) return ENOMEM;
1814 0 : p += len;
1815 :
1816 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1817 : "cmd [%d] uid [%llu] gid [%llu] validate [%s] "
1818 : "enterprise principal [%s] offline [%s] UPN [%s]\n",
1819 : pd->cmd, (unsigned long long) kr->uid,
1820 : (unsigned long long) kr->gid, kr->validate ? "true" : "false",
1821 : kr->use_enterprise_princ ? "true" : "false",
1822 : *offline ? "true" : "false", kr->upn ? kr->upn : "none");
1823 :
1824 0 : if (pd->cmd == SSS_PAM_AUTHENTICATE ||
1825 0 : pd->cmd == SSS_CMD_RENEW ||
1826 0 : pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) {
1827 0 : SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
1828 0 : if (len > size - p) return EINVAL;
1829 0 : kr->ccname = talloc_strndup(pd, (char *)(buf + p), len);
1830 0 : if (kr->ccname == NULL) return ENOMEM;
1831 0 : p += len;
1832 :
1833 0 : SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
1834 0 : if (len > size - p) return EINVAL;
1835 :
1836 0 : if (len > 0) {
1837 0 : kr->old_ccname = talloc_strndup(pd, (char *)(buf + p), len);
1838 0 : if (kr->old_ccname == NULL) return ENOMEM;
1839 0 : p += len;
1840 : } else {
1841 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "No old ccache\n");
1842 : }
1843 :
1844 0 : SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
1845 0 : if (len > size - p) return EINVAL;
1846 0 : kr->keytab = talloc_strndup(pd, (char *)(buf + p), len);
1847 0 : if (kr->keytab == NULL) return ENOMEM;
1848 0 : p += len;
1849 :
1850 0 : ret = unpack_authtok(pd->authtok, buf, size, &p);
1851 0 : if (ret) {
1852 0 : return ret;
1853 : }
1854 :
1855 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1856 : "ccname: [%s] old_ccname: [%s] keytab: [%s]\n",
1857 : kr->ccname,
1858 : kr->old_ccname ? kr->old_ccname : "not set",
1859 : kr->keytab);
1860 : } else {
1861 0 : kr->ccname = NULL;
1862 0 : kr->old_ccname = NULL;
1863 0 : kr->keytab = NULL;
1864 0 : sss_authtok_set_empty(pd->authtok);
1865 : }
1866 :
1867 0 : if (pd->cmd == SSS_PAM_CHAUTHTOK) {
1868 0 : ret = unpack_authtok(pd->newauthtok, buf, size, &p);
1869 0 : if (ret) {
1870 0 : return ret;
1871 : }
1872 : } else {
1873 0 : sss_authtok_set_empty(pd->newauthtok);
1874 : }
1875 :
1876 0 : if (pd->cmd == SSS_PAM_ACCT_MGMT) {
1877 0 : SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
1878 0 : if (len > size - p) return EINVAL;
1879 0 : pd->user = talloc_strndup(pd, (char *)(buf + p), len);
1880 0 : if (pd->user == NULL) return ENOMEM;
1881 0 : p += len;
1882 0 : DEBUG(SSSDBG_CONF_SETTINGS, "user: [%s]\n", pd->user);
1883 : } else {
1884 0 : pd->user = NULL;
1885 : }
1886 :
1887 0 : return EOK;
1888 : }
1889 :
1890 0 : static int krb5_cleanup(struct krb5_req *kr)
1891 : {
1892 0 : if (kr == NULL) return EOK;
1893 :
1894 0 : if (kr->options != NULL) {
1895 0 : sss_krb5_get_init_creds_opt_free(kr->ctx, kr->options);
1896 : }
1897 :
1898 0 : if (kr->creds != NULL) {
1899 0 : krb5_free_cred_contents(kr->ctx, kr->creds);
1900 0 : krb5_free_creds(kr->ctx, kr->creds);
1901 : }
1902 0 : if (kr->name != NULL)
1903 0 : sss_krb5_free_unparsed_name(kr->ctx, kr->name);
1904 0 : if (kr->princ != NULL)
1905 0 : krb5_free_principal(kr->ctx, kr->princ);
1906 0 : if (kr->ctx != NULL)
1907 0 : krb5_free_context(kr->ctx);
1908 :
1909 0 : memset(kr, 0, sizeof(struct krb5_req));
1910 :
1911 0 : return EOK;
1912 : }
1913 :
1914 0 : static krb5_error_code get_tgt_times(krb5_context ctx, const char *ccname,
1915 : krb5_principal server_principal,
1916 : krb5_principal client_principal,
1917 : sss_krb5_ticket_times *tgtt)
1918 : {
1919 : krb5_error_code krberr;
1920 0 : krb5_ccache ccache = NULL;
1921 : krb5_creds mcred;
1922 : krb5_creds cred;
1923 :
1924 0 : krberr = krb5_cc_resolve(ctx, ccname, &ccache);
1925 0 : if (krberr != 0) {
1926 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_resolve failed.\n");
1927 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, krberr);
1928 0 : goto done;
1929 : }
1930 :
1931 0 : memset(&mcred, 0, sizeof(mcred));
1932 0 : memset(&cred, 0, sizeof(mcred));
1933 :
1934 0 : mcred.server = server_principal;
1935 0 : mcred.client = client_principal;
1936 :
1937 0 : krberr = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcred, &cred);
1938 0 : if (krberr == KRB5_FCC_NOFILE) {
1939 0 : DEBUG(SSSDBG_TRACE_LIBS, "FAST ccache must be recreated\n");
1940 0 : } else if (krberr != 0) {
1941 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_cc_retrieve_cred failed\n");
1942 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, krberr);
1943 0 : krberr = 0;
1944 0 : goto done;
1945 : }
1946 :
1947 0 : tgtt->authtime = cred.times.authtime;
1948 0 : tgtt->starttime = cred.times.starttime;
1949 0 : tgtt->endtime = cred.times.endtime;
1950 0 : tgtt->renew_till = cred.times.renew_till;
1951 :
1952 0 : krb5_free_cred_contents(ctx, &cred);
1953 :
1954 0 : krberr = 0;
1955 :
1956 : done:
1957 0 : if (ccache != NULL) {
1958 0 : krb5_cc_close(ctx, ccache);
1959 : }
1960 :
1961 0 : return krberr;
1962 : }
1963 :
1964 0 : static krb5_error_code check_fast_ccache(TALLOC_CTX *mem_ctx,
1965 : krb5_context ctx,
1966 : uid_t fast_uid,
1967 : gid_t fast_gid,
1968 : const char *primary,
1969 : const char *realm,
1970 : const char *keytab_name,
1971 : char **fast_ccname)
1972 : {
1973 0 : TALLOC_CTX *tmp_ctx = NULL;
1974 : krb5_error_code kerr;
1975 : char *ccname;
1976 : char *server_name;
1977 : sss_krb5_ticket_times tgtt;
1978 0 : krb5_keytab keytab = NULL;
1979 0 : krb5_principal client_princ = NULL;
1980 0 : krb5_principal server_princ = NULL;
1981 : pid_t fchild_pid;
1982 : int status;
1983 :
1984 0 : tmp_ctx = talloc_new(NULL);
1985 0 : if (tmp_ctx == NULL) {
1986 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
1987 0 : return ENOMEM;
1988 : }
1989 :
1990 0 : ccname = talloc_asprintf(tmp_ctx, "FILE:%s/fast_ccache_%s", DB_PATH, realm);
1991 0 : if (ccname == NULL) {
1992 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
1993 0 : kerr = ENOMEM;
1994 0 : goto done;
1995 : }
1996 :
1997 0 : if (keytab_name != NULL) {
1998 0 : kerr = krb5_kt_resolve(ctx, keytab_name, &keytab);
1999 : } else {
2000 0 : kerr = krb5_kt_default(ctx, &keytab);
2001 : }
2002 0 : if (kerr) {
2003 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2004 : "Failed to read keytab file [%s]: %s\n",
2005 : KEYTAB_CLEAN_NAME,
2006 : sss_krb5_get_error_message(ctx, kerr));
2007 0 : goto done;
2008 : }
2009 :
2010 0 : kerr = find_principal_in_keytab(ctx, keytab, primary, realm, &client_princ);
2011 0 : if (kerr != 0) {
2012 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2013 : "find_principal_in_keytab failed for principal %s@%s.\n",
2014 : primary, realm);
2015 0 : goto done;
2016 : }
2017 :
2018 0 : server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
2019 0 : if (server_name == NULL) {
2020 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
2021 0 : kerr = ENOMEM;
2022 0 : goto done;
2023 : }
2024 :
2025 0 : kerr = krb5_parse_name(ctx, server_name, &server_princ);
2026 0 : if (kerr != 0) {
2027 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
2028 0 : goto done;
2029 : }
2030 :
2031 0 : memset(&tgtt, 0, sizeof(tgtt));
2032 0 : kerr = get_tgt_times(ctx, ccname, server_princ, client_princ, &tgtt);
2033 0 : if (kerr == 0) {
2034 0 : if (tgtt.endtime > time(NULL)) {
2035 0 : DEBUG(SSSDBG_FUNC_DATA, "FAST TGT is still valid.\n");
2036 0 : goto done;
2037 : }
2038 : }
2039 :
2040 : /* Need to recreate the FAST ccache */
2041 0 : fchild_pid = fork();
2042 0 : switch (fchild_pid) {
2043 : case -1:
2044 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fork failed\n");
2045 0 : kerr = EIO;
2046 0 : goto done;
2047 : case 0:
2048 : /* Child */
2049 0 : debug_prg_name = talloc_asprintf(NULL, "[sssd[krb5_child[%d]]]", getpid());
2050 0 : if (debug_prg_name == NULL) {
2051 0 : debug_prg_name = "[sssd[krb5_child]]";
2052 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
2053 : /* Try to carry on */
2054 : }
2055 :
2056 0 : kerr = become_user(fast_uid, fast_gid);
2057 0 : if (kerr != 0) {
2058 0 : DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed: %d\n", kerr);
2059 0 : exit(1);
2060 : }
2061 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
2062 : "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
2063 :
2064 0 : kerr = get_and_save_tgt_with_keytab(ctx, client_princ,
2065 : keytab, ccname);
2066 0 : if (kerr != 0) {
2067 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2068 : "get_and_save_tgt_with_keytab failed: %d\n", kerr);
2069 0 : exit(2);
2070 : }
2071 0 : exit(0);
2072 : default:
2073 : /* Parent */
2074 : do {
2075 0 : errno = 0;
2076 0 : kerr = waitpid(fchild_pid, &status, 0);
2077 0 : } while (kerr == -1 && errno == EINTR);
2078 :
2079 0 : if (kerr > 0) {
2080 0 : if (WIFEXITED(status)) {
2081 0 : kerr = WEXITSTATUS(status);
2082 : /* Don't blindly fail if the child fails, but check
2083 : * the ccache again */
2084 0 : if (kerr != 0) {
2085 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2086 : "Creating FAST ccache failed, krb5_child will "
2087 : "likely fail!\n");
2088 : }
2089 : } else {
2090 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2091 : "krb5_child subprocess %d terminated unexpectedly\n",
2092 : fchild_pid);
2093 : }
2094 : } else {
2095 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2096 : "Failed to wait for child %d\n", fchild_pid);
2097 : /* Let the code re-check the TGT times and fail if we
2098 : * can't find the updated principal */
2099 : }
2100 : }
2101 :
2102 : /* Check the ccache times again. Should be updated ... */
2103 0 : memset(&tgtt, 0, sizeof(tgtt));
2104 0 : kerr = get_tgt_times(ctx, ccname, server_princ, client_princ, &tgtt);
2105 0 : if (kerr != 0) {
2106 0 : DEBUG(SSSDBG_OP_FAILURE, "get_tgt_times() failed\n");
2107 0 : goto done;
2108 : }
2109 :
2110 0 : if (tgtt.endtime < time(NULL)) {
2111 0 : DEBUG(SSSDBG_OP_FAILURE,
2112 : "FAST TGT was renewed but is already expired, please check that "
2113 : "time is synchronized with server.\n");
2114 0 : kerr = ERR_CREDS_EXPIRED;
2115 0 : goto done;
2116 : }
2117 0 : DEBUG(SSSDBG_FUNC_DATA, "FAST TGT was successfully recreated!\n");
2118 :
2119 : done:
2120 0 : if (client_princ != NULL) {
2121 0 : krb5_free_principal(ctx, client_princ);
2122 : }
2123 0 : if (server_princ != NULL) {
2124 0 : krb5_free_principal(ctx, server_princ);
2125 : }
2126 :
2127 0 : if (kerr == 0) {
2128 0 : *fast_ccname = talloc_steal(mem_ctx, ccname);
2129 : }
2130 0 : talloc_free(tmp_ctx);
2131 :
2132 0 : if (keytab != NULL) {
2133 0 : krb5_kt_close(ctx, keytab);
2134 : }
2135 :
2136 0 : return kerr;
2137 : }
2138 :
2139 0 : static errno_t k5c_recv_data(struct krb5_req *kr, int fd, uint32_t *offline)
2140 : {
2141 : uint8_t buf[IN_BUF_SIZE];
2142 : ssize_t len;
2143 : errno_t ret;
2144 :
2145 0 : errno = 0;
2146 0 : len = sss_atomic_read_s(fd, buf, IN_BUF_SIZE);
2147 0 : if (len == -1) {
2148 0 : ret = errno;
2149 0 : ret = (ret == 0) ? EINVAL: ret;
2150 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2151 : "read failed [%d][%s].\n", ret, strerror(ret));
2152 0 : return ret;
2153 : }
2154 :
2155 0 : ret = unpack_buffer(buf, len, kr, offline);
2156 0 : if (ret != EOK) {
2157 0 : DEBUG(SSSDBG_CRIT_FAILURE, "unpack_buffer failed.\n");
2158 : }
2159 :
2160 0 : return ret;
2161 : }
2162 :
2163 0 : static int k5c_setup_fast(struct krb5_req *kr, bool demand)
2164 : {
2165 : krb5_principal fast_princ_struct;
2166 : krb5_data *realm_data;
2167 : char *fast_principal_realm;
2168 : char *fast_principal;
2169 : krb5_error_code kerr;
2170 : char *tmp_str;
2171 : char *new_ccname;
2172 :
2173 0 : tmp_str = getenv(SSSD_KRB5_FAST_PRINCIPAL);
2174 0 : if (tmp_str) {
2175 0 : DEBUG(SSSDBG_CONF_SETTINGS, "%s is set to [%s]\n",
2176 : SSSD_KRB5_FAST_PRINCIPAL, tmp_str);
2177 0 : kerr = krb5_parse_name(kr->ctx, tmp_str, &fast_princ_struct);
2178 0 : if (kerr) {
2179 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_parse_name failed.\n");
2180 0 : return kerr;
2181 : }
2182 0 : kerr = sss_krb5_unparse_name_flags(kr->ctx, fast_princ_struct,
2183 : KRB5_PRINCIPAL_UNPARSE_NO_REALM,
2184 : &tmp_str);
2185 0 : if (kerr) {
2186 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_unparse_name_flags failed.\n");
2187 0 : return kerr;
2188 : }
2189 0 : fast_principal = talloc_strdup(kr, tmp_str);
2190 0 : if (!fast_principal) {
2191 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
2192 0 : return KRB5KRB_ERR_GENERIC;
2193 : }
2194 0 : free(tmp_str);
2195 0 : realm_data = krb5_princ_realm(kr->ctx, fast_princ_struct);
2196 0 : fast_principal_realm = talloc_asprintf(kr, "%.*s", realm_data->length, realm_data->data);
2197 0 : if (!fast_principal_realm) {
2198 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
2199 0 : return ENOMEM;
2200 : }
2201 : } else {
2202 0 : fast_principal_realm = kr->realm;
2203 0 : fast_principal = NULL;
2204 : }
2205 :
2206 0 : kerr = check_fast_ccache(kr, kr->ctx, kr->fast_uid, kr->fast_gid,
2207 : fast_principal, fast_principal_realm,
2208 0 : kr->keytab, &kr->fast_ccname);
2209 0 : if (kerr != 0) {
2210 0 : DEBUG(SSSDBG_CRIT_FAILURE, "check_fast_ccache failed.\n");
2211 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2212 0 : return kerr;
2213 : }
2214 :
2215 0 : kerr = copy_ccache_into_memory(kr, kr->ctx, kr->fast_ccname, &new_ccname);
2216 0 : if (kerr != 0) {
2217 0 : DEBUG(SSSDBG_CRIT_FAILURE, "copy_ccache_into_memory failed.\n");
2218 0 : return kerr;
2219 : }
2220 :
2221 0 : talloc_free(kr->fast_ccname);
2222 0 : kr->fast_ccname = new_ccname;
2223 :
2224 0 : kerr = sss_krb5_get_init_creds_opt_set_fast_ccache_name(kr->ctx,
2225 : kr->options,
2226 0 : kr->fast_ccname);
2227 0 : if (kerr != 0) {
2228 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2229 : "sss_krb5_get_init_creds_opt_set_fast_ccache_name "
2230 : "failed.\n");
2231 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2232 0 : return kerr;
2233 : }
2234 :
2235 0 : if (demand) {
2236 0 : kerr = sss_krb5_get_init_creds_opt_set_fast_flags(kr->ctx,
2237 : kr->options,
2238 : SSS_KRB5_FAST_REQUIRED);
2239 0 : if (kerr != 0) {
2240 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2241 : "sss_krb5_get_init_creds_opt_set_fast_flags "
2242 : "failed.\n");
2243 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2244 0 : return kerr;
2245 : }
2246 : }
2247 :
2248 0 : return EOK;
2249 : }
2250 :
2251 0 : static errno_t check_use_fast(enum k5c_fast_opt *_fast_val)
2252 : {
2253 : char *use_fast_str;
2254 : enum k5c_fast_opt fast_val;
2255 :
2256 0 : use_fast_str = getenv(SSSD_KRB5_USE_FAST);
2257 0 : if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
2258 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Not using FAST.\n");
2259 0 : fast_val = K5C_FAST_NEVER;
2260 0 : } else if (strcasecmp(use_fast_str, "try") == 0) {
2261 0 : fast_val = K5C_FAST_TRY;
2262 0 : } else if (strcasecmp(use_fast_str, "demand") == 0) {
2263 0 : fast_val = K5C_FAST_DEMAND;
2264 : } else {
2265 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2266 : "Unsupported value [%s] for krb5_use_fast.\n",
2267 : use_fast_str);
2268 0 : return EINVAL;
2269 : }
2270 :
2271 0 : *_fast_val = fast_val;
2272 0 : return EOK;
2273 : }
2274 :
2275 0 : static errno_t old_ccache_valid(struct krb5_req *kr, bool *_valid)
2276 : {
2277 : errno_t ret;
2278 : bool valid;
2279 :
2280 0 : valid = false;
2281 :
2282 0 : ret = sss_krb5_cc_verify_ccache(kr->old_ccname,
2283 : kr->uid, kr->gid,
2284 0 : kr->realm, kr->upn);
2285 0 : switch (ret) {
2286 : case ERR_NOT_FOUND:
2287 : case ENOENT:
2288 0 : DEBUG(SSSDBG_TRACE_FUNC,
2289 : "Saved ccache %s doesn't exist, ignoring\n", kr->old_ccname);
2290 0 : break;
2291 : case EINVAL:
2292 : /* cache found but no tgt or expired */
2293 : case EOK:
2294 0 : valid = true;
2295 0 : break;
2296 : default:
2297 0 : DEBUG(SSSDBG_OP_FAILURE,
2298 : "Cannot check if saved ccache %s is valid\n",
2299 : kr->old_ccname);
2300 0 : return ret;
2301 : }
2302 :
2303 0 : *_valid = valid;
2304 0 : return EOK;
2305 : }
2306 :
2307 0 : static int k5c_check_old_ccache(struct krb5_req *kr)
2308 : {
2309 : errno_t ret;
2310 :
2311 0 : if (kr->old_ccname) {
2312 0 : ret = old_ccache_valid(kr, &kr->old_cc_valid);
2313 0 : if (ret != EOK) {
2314 0 : DEBUG(SSSDBG_OP_FAILURE, "old_ccache_valid failed.\n");
2315 0 : return ret;
2316 : }
2317 :
2318 0 : ret = check_if_uid_is_active(kr->uid, &kr->old_cc_active);
2319 0 : if (ret != EOK) {
2320 0 : DEBUG(SSSDBG_OP_FAILURE, "check_if_uid_is_active failed.\n");
2321 0 : return ret;
2322 : }
2323 :
2324 0 : DEBUG(SSSDBG_TRACE_ALL,
2325 : "Ccache_file is [%s] and is %s active and TGT is %s valid.\n",
2326 : kr->old_ccname ? kr->old_ccname : "not set",
2327 : kr->old_cc_active ? "" : "not",
2328 : kr->old_cc_valid ? "" : "not");
2329 : }
2330 :
2331 0 : return EOK;
2332 : }
2333 :
2334 0 : static int k5c_precreate_ccache(struct krb5_req *kr, uint32_t offline)
2335 : {
2336 : errno_t ret;
2337 :
2338 : /* The ccache file should be (re)created if one of the following conditions
2339 : * is true:
2340 : * - it doesn't exist (kr->old_ccname == NULL)
2341 : * - the backend is online and the current ccache file is not used, i.e
2342 : * the related user is currently not logged in and it is not a renewal
2343 : * request
2344 : * (offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)
2345 : * - the backend is offline and the current cache file not used and
2346 : * it does not contain a valid tgt
2347 : * (offline && !kr->old_cc_active && !kr->valid_tgt)
2348 : */
2349 0 : if (kr->old_ccname == NULL ||
2350 0 : (offline && !kr->old_cc_active && !kr->old_cc_valid) ||
2351 0 : (!offline && !kr->old_cc_active && kr->pd->cmd != SSS_CMD_RENEW)) {
2352 0 : DEBUG(SSSDBG_TRACE_ALL, "Recreating ccache\n");
2353 :
2354 0 : ret = sss_krb5_precreate_ccache(kr->ccname, kr->uid, kr->gid);
2355 0 : if (ret != EOK) {
2356 0 : DEBUG(SSSDBG_OP_FAILURE, "ccache creation failed.\n");
2357 0 : return ret;
2358 : }
2359 : } else {
2360 : /* We can reuse the old ccache */
2361 0 : kr->ccname = kr->old_ccname;
2362 : }
2363 :
2364 0 : return EOK;
2365 : }
2366 :
2367 0 : static int k5c_ccache_setup(struct krb5_req *kr, uint32_t offline)
2368 : {
2369 : errno_t ret;
2370 :
2371 0 : if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
2372 0 : return EOK;
2373 : }
2374 :
2375 0 : ret = k5c_check_old_ccache(kr);
2376 0 : if (ret != 0) {
2377 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot check old ccache [%s]: [%d][%s]. " \
2378 : "Assuming old cache is invalid " \
2379 : "and not used.\n",
2380 : kr->old_ccname, ret, sss_strerror(ret));
2381 : }
2382 :
2383 : /* Pre-creating the ccache must be done as root, otherwise we can't mkdir
2384 : * some of the DIR: cache components. One example is /run/user/$UID because
2385 : * logind doesn't create the directory until the session phase, whereas
2386 : * we need the directory during the auth phase already
2387 : */
2388 0 : ret = k5c_precreate_ccache(kr, offline);
2389 0 : if (ret != 0) {
2390 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot precreate ccache\n");
2391 0 : return ret;
2392 : }
2393 :
2394 0 : return EOK;
2395 : }
2396 :
2397 0 : static int k5c_setup(struct krb5_req *kr, uint32_t offline)
2398 : {
2399 : krb5_error_code kerr;
2400 : int parse_flags;
2401 :
2402 0 : if (offline || (kr->fast_val == K5C_FAST_NEVER && kr->validate == false)) {
2403 : /* If krb5_child was started as setuid, but we don't need to
2404 : * perform either validation or FAST, just drop privileges to
2405 : * the user who is logging in. The same applies to the offline case
2406 : * the user who is logging in. The same applies to the offline case.
2407 : */
2408 0 : kerr = become_user(kr->uid, kr->gid);
2409 0 : if (kerr != 0) {
2410 0 : DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
2411 0 : return kerr;
2412 : }
2413 : }
2414 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
2415 : "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
2416 :
2417 : /* Set the global error context */
2418 0 : krb5_error_ctx = kr->ctx;
2419 :
2420 0 : if (debug_level & SSSDBG_TRACE_ALL) {
2421 0 : kerr = sss_child_set_krb5_tracing(kr->ctx);
2422 0 : if (kerr != 0) {
2423 0 : KRB5_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr);
2424 0 : return EIO;
2425 : }
2426 : }
2427 :
2428 : /* Enterprise principals require that a default realm is available. To
2429 : * make SSSD more robust in the case that the default realm option is
2430 : * missing in krb5.conf or to allow SSSD to work with multiple unconnected
2431 : * realms (e.g. AD domains without trust between them) the default realm
2432 : * will be set explicitly. */
2433 0 : if (kr->use_enterprise_princ) {
2434 0 : kerr = krb5_set_default_realm(kr->ctx, kr->realm);
2435 0 : if (kerr != 0) {
2436 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_set_default_realm failed.\n");
2437 : }
2438 : }
2439 :
2440 0 : parse_flags = kr->use_enterprise_princ ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
2441 0 : kerr = sss_krb5_parse_name_flags(kr->ctx, kr->upn, parse_flags, &kr->princ);
2442 0 : if (kerr != 0) {
2443 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2444 0 : return kerr;
2445 : }
2446 :
2447 0 : kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
2448 0 : if (kerr != 0) {
2449 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2450 0 : return kerr;
2451 : }
2452 :
2453 0 : kr->creds = calloc(1, sizeof(krb5_creds));
2454 0 : if (kr->creds == NULL) {
2455 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
2456 0 : return ENOMEM;
2457 : }
2458 :
2459 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
2460 0 : kerr = krb5_get_init_creds_opt_set_responder(kr->ctx, kr->options,
2461 : sss_krb5_responder, kr);
2462 0 : if (kerr != 0) {
2463 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2464 0 : return kerr;
2465 : }
2466 : #endif
2467 :
2468 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT
2469 : /* A prompter is used to catch messages about when a password will
2470 : * expired. The library shall not use the prompter to ask for a new password
2471 : * but shall return KRB5KDC_ERR_KEY_EXP. */
2472 0 : krb5_get_init_creds_opt_set_change_password_prompt(kr->options, 0);
2473 : #endif
2474 :
2475 0 : kerr = set_lifetime_options(kr->options);
2476 0 : if (kerr != 0) {
2477 0 : DEBUG(SSSDBG_OP_FAILURE, "set_lifetime_options failed.\n");
2478 0 : return kerr;
2479 : }
2480 :
2481 0 : if (!offline) {
2482 0 : set_canonicalize_option(kr->options);
2483 : }
2484 :
2485 : /* TODO: set options, e.g.
2486 : * krb5_get_init_creds_opt_set_forwardable
2487 : * krb5_get_init_creds_opt_set_proxiable
2488 : * krb5_get_init_creds_opt_set_etype_list
2489 : * krb5_get_init_creds_opt_set_address_list
2490 : * krb5_get_init_creds_opt_set_preauth_list
2491 : * krb5_get_init_creds_opt_set_salt
2492 : * krb5_get_init_creds_opt_set_change_password_prompt
2493 : * krb5_get_init_creds_opt_set_pa
2494 : */
2495 :
2496 0 : return kerr;
2497 : }
2498 :
2499 0 : static krb5_error_code privileged_krb5_setup(struct krb5_req *kr,
2500 : uint32_t offline)
2501 : {
2502 : krb5_error_code kerr;
2503 : int ret;
2504 : char *mem_keytab;
2505 :
2506 0 : kr->realm = getenv(SSSD_KRB5_REALM);
2507 0 : if (kr->realm == NULL) {
2508 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2509 : "Cannot read [%s] from environment.\n", SSSD_KRB5_REALM);
2510 : }
2511 :
2512 0 : kerr = krb5_init_context(&kr->ctx);
2513 0 : if (kerr != 0) {
2514 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2515 0 : return kerr;
2516 : }
2517 :
2518 0 : kerr = sss_krb5_get_init_creds_opt_alloc(kr->ctx, &kr->options);
2519 0 : if (kerr != 0) {
2520 0 : KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
2521 0 : return kerr;
2522 : }
2523 :
2524 0 : ret = check_use_fast(&kr->fast_val);
2525 0 : if (ret != EOK) {
2526 0 : DEBUG(SSSDBG_CRIT_FAILURE, "check_use_fast failed.\n");
2527 0 : return ret;;
2528 : }
2529 :
2530 : /* For ccache types FILE: and DIR: we might need to create some directory
2531 : * components as root. Cache files are not needed during preauth. */
2532 0 : if (kr->pd->cmd != SSS_PAM_PREAUTH) {
2533 0 : ret = k5c_ccache_setup(kr, offline);
2534 0 : if (ret != EOK) {
2535 0 : DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup failed.\n");
2536 0 : return ret;
2537 : }
2538 : }
2539 :
2540 0 : if (!(offline ||
2541 0 : (kr->fast_val == K5C_FAST_NEVER && kr->validate == false))) {
2542 0 : kerr = copy_keytab_into_memory(kr, kr->ctx, kr->keytab, &mem_keytab,
2543 : NULL);
2544 0 : if (kerr != 0) {
2545 0 : DEBUG(SSSDBG_OP_FAILURE, "copy_keytab_into_memory failed.\n");
2546 0 : return kerr;
2547 : }
2548 :
2549 0 : talloc_free(kr->keytab);
2550 0 : kr->keytab = mem_keytab;
2551 :
2552 0 : if (kr->fast_val != K5C_FAST_NEVER) {
2553 0 : kerr = k5c_setup_fast(kr, kr->fast_val == K5C_FAST_DEMAND);
2554 0 : if (kerr != EOK) {
2555 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot set up FAST\n");
2556 0 : return kerr;
2557 : }
2558 : }
2559 : }
2560 :
2561 0 : if (kr->send_pac) {
2562 0 : ret = sss_pac_check_and_open();
2563 0 : if (ret != EOK) {
2564 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot open the PAC responder socket\n");
2565 : /* Not fatal */
2566 : }
2567 : }
2568 :
2569 0 : return 0;
2570 : }
2571 :
2572 0 : int main(int argc, const char *argv[])
2573 : {
2574 0 : struct krb5_req *kr = NULL;
2575 : uint32_t offline;
2576 : int opt;
2577 : poptContext pc;
2578 0 : int debug_fd = -1;
2579 : errno_t ret;
2580 : krb5_error_code kerr;
2581 : uid_t fast_uid;
2582 : gid_t fast_gid;
2583 :
2584 0 : struct poptOption long_options[] = {
2585 : POPT_AUTOHELP
2586 : {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
2587 0 : _("Debug level"), NULL},
2588 : {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
2589 0 : _("Add debug timestamps"), NULL},
2590 : {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0,
2591 0 : _("Show timestamps with microseconds"), NULL},
2592 : {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
2593 0 : _("An open file descriptor for the debug logs"), NULL},
2594 : {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
2595 : &debug_to_stderr, 0,
2596 0 : _("Send the debug output to stderr directly."), NULL },
2597 : {"fast-ccache-uid", 0, POPT_ARG_INT, &fast_uid, 0,
2598 0 : _("The user to create FAST ccache as"), NULL},
2599 : {"fast-ccache-gid", 0, POPT_ARG_INT, &fast_gid, 0,
2600 0 : _("The group to create FAST ccache as"), NULL},
2601 : POPT_TABLEEND
2602 : };
2603 :
2604 : /* Set debug level to invalid value so we can decide if -d 0 was used. */
2605 0 : debug_level = SSSDBG_INVALID;
2606 :
2607 0 : pc = poptGetContext(argv[0], argc, argv, long_options, 0);
2608 0 : while((opt = poptGetNextOpt(pc)) != -1) {
2609 : switch(opt) {
2610 : default:
2611 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
2612 : poptBadOption(pc, 0), poptStrerror(opt));
2613 0 : poptPrintUsage(pc, stderr, 0);
2614 0 : _exit(-1);
2615 : }
2616 : }
2617 :
2618 0 : poptFreeContext(pc);
2619 :
2620 0 : DEBUG_INIT(debug_level);
2621 :
2622 0 : debug_prg_name = talloc_asprintf(NULL, "[sssd[krb5_child[%d]]]", getpid());
2623 0 : if (!debug_prg_name) {
2624 0 : debug_prg_name = "[sssd[krb5_child]]";
2625 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
2626 0 : ret = ENOMEM;
2627 0 : goto done;
2628 : }
2629 :
2630 0 : if (debug_fd != -1) {
2631 0 : ret = set_debug_file_from_fd(debug_fd);
2632 0 : if (ret != EOK) {
2633 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
2634 : }
2635 : }
2636 :
2637 0 : DEBUG(SSSDBG_TRACE_FUNC, "krb5_child started.\n");
2638 :
2639 0 : kr = talloc_zero(NULL, struct krb5_req);
2640 0 : if (kr == NULL) {
2641 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
2642 0 : ret = ENOMEM;
2643 0 : goto done;
2644 : }
2645 0 : talloc_steal(kr, debug_prg_name);
2646 :
2647 0 : kr->fast_uid = fast_uid;
2648 0 : kr->fast_gid = fast_gid;
2649 :
2650 0 : ret = k5c_recv_data(kr, STDIN_FILENO, &offline);
2651 0 : if (ret != EOK) {
2652 0 : goto done;
2653 : }
2654 :
2655 0 : close(STDIN_FILENO);
2656 :
2657 0 : kerr = privileged_krb5_setup(kr, offline);
2658 0 : if (kerr != 0) {
2659 0 : DEBUG(SSSDBG_CRIT_FAILURE, "privileged_krb5_setup failed.\n");
2660 0 : ret = EFAULT;
2661 0 : goto done;
2662 : }
2663 :
2664 0 : kerr = become_user(kr->uid, kr->gid);
2665 0 : if (kerr != 0) {
2666 0 : DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
2667 0 : ret = EFAULT;
2668 0 : goto done;
2669 : }
2670 :
2671 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
2672 : "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
2673 :
2674 0 : ret = k5c_setup(kr, offline);
2675 0 : if (ret != EOK) {
2676 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_child_setup failed.\n");
2677 0 : goto done;
2678 : }
2679 :
2680 0 : switch(kr->pd->cmd) {
2681 : case SSS_PAM_AUTHENTICATE:
2682 : /* If we are offline, we need to create an empty ccache file */
2683 0 : if (offline) {
2684 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform offline auth\n");
2685 0 : ret = create_empty_ccache(kr);
2686 : } else {
2687 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform online auth\n");
2688 0 : ret = tgt_req_child(kr);
2689 : }
2690 0 : break;
2691 : case SSS_PAM_CHAUTHTOK:
2692 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform password change\n");
2693 0 : ret = changepw_child(kr, false);
2694 0 : break;
2695 : case SSS_PAM_CHAUTHTOK_PRELIM:
2696 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform password change checks\n");
2697 0 : ret = changepw_child(kr, true);
2698 0 : break;
2699 : case SSS_PAM_ACCT_MGMT:
2700 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform account management\n");
2701 0 : ret = kuserok_child(kr);
2702 0 : break;
2703 : case SSS_CMD_RENEW:
2704 0 : if (offline) {
2705 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot renew TGT while offline\n");
2706 0 : ret = KRB5_KDC_UNREACH;
2707 0 : goto done;
2708 : }
2709 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform ticket renewal\n");
2710 0 : ret = renew_tgt_child(kr);
2711 0 : break;
2712 : case SSS_PAM_PREAUTH:
2713 0 : DEBUG(SSSDBG_TRACE_FUNC, "Will perform pre-auth\n");
2714 0 : ret = tgt_req_child(kr);
2715 0 : break;
2716 : default:
2717 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2718 : "PAM command [%d] not supported.\n", kr->pd->cmd);
2719 0 : ret = EINVAL;
2720 0 : goto done;
2721 : }
2722 :
2723 0 : ret = k5c_send_data(kr, STDOUT_FILENO, ret);
2724 0 : if (ret != EOK) {
2725 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to send reply\n");
2726 : }
2727 :
2728 : done:
2729 0 : if (ret == EOK) {
2730 0 : DEBUG(SSSDBG_TRACE_FUNC, "krb5_child completed successfully\n");
2731 0 : ret = 0;
2732 : } else {
2733 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_child failed!\n");
2734 0 : ret = -1;
2735 : }
2736 0 : krb5_cleanup(kr);
2737 0 : talloc_free(kr);
2738 0 : exit(ret);
2739 : }
|