17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5a0709436Smp * Common Development and Distribution License (the "License"). 6a0709436Smp * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 215ad42b1bSSurya Prakki 227c478bd9Sstevel@tonic-gate /* 23488060a6SWill Fiveash * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <security/pam_appl.h> 277c478bd9Sstevel@tonic-gate #include <security/pam_modules.h> 287c478bd9Sstevel@tonic-gate #include <security/pam_impl.h> 297c478bd9Sstevel@tonic-gate #include <string.h> 307c478bd9Sstevel@tonic-gate #include <stdio.h> 317c478bd9Sstevel@tonic-gate #include <stdlib.h> 327c478bd9Sstevel@tonic-gate #include <sys/types.h> 337c478bd9Sstevel@tonic-gate #include <sys/stat.h> 347c478bd9Sstevel@tonic-gate #include <pwd.h> 357c478bd9Sstevel@tonic-gate #include <syslog.h> 367c478bd9Sstevel@tonic-gate #include <libintl.h> 377c478bd9Sstevel@tonic-gate #include <k5-int.h> 387c478bd9Sstevel@tonic-gate #include "profile/prof_int.h" 397c478bd9Sstevel@tonic-gate #include <netdb.h> 407c478bd9Sstevel@tonic-gate #include <ctype.h> 417c478bd9Sstevel@tonic-gate #include "utils.h" 427c478bd9Sstevel@tonic-gate #include "krb5_repository.h" 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate #define KRB5_DEFAULT_OPTIONS 0 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate int forwardable_flag = 0; 477c478bd9Sstevel@tonic-gate int renewable_flag = 0; 487c478bd9Sstevel@tonic-gate int proxiable_flag = 0; 497c478bd9Sstevel@tonic-gate int no_address_flag = 0; 507c478bd9Sstevel@tonic-gate profile_options_boolean config_option[] = { 517c478bd9Sstevel@tonic-gate { "forwardable", &forwardable_flag, 0 }, 527c478bd9Sstevel@tonic-gate { "renewable", &renewable_flag, 0 }, 537c478bd9Sstevel@tonic-gate { "proxiable", &proxiable_flag, 0 }, 547c478bd9Sstevel@tonic-gate { "no_addresses", &no_address_flag, 0 }, 557c478bd9Sstevel@tonic-gate { NULL, NULL, 0 } 567c478bd9Sstevel@tonic-gate }; 577c478bd9Sstevel@tonic-gate char *renew_timeval; 587c478bd9Sstevel@tonic-gate char *life_timeval; 597c478bd9Sstevel@tonic-gate profile_option_strings config_times[] = { 607c478bd9Sstevel@tonic-gate { "max_life", &life_timeval, 0 }, 617c478bd9Sstevel@tonic-gate { "max_renewable_life", &renew_timeval, 0 }, 627c478bd9Sstevel@tonic-gate { NULL, NULL, 0 } 637c478bd9Sstevel@tonic-gate }; 647c478bd9Sstevel@tonic-gate char *realmdef[] = { "realms", NULL, NULL, NULL }; 657c478bd9Sstevel@tonic-gate char *appdef[] = { "appdefaults", "kinit", NULL }; 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate #define krb_realm (*(realmdef + 1)) 687c478bd9Sstevel@tonic-gate 6970f41fc1SWill Fiveash int attempt_krb5_auth(pam_handle_t *, krb5_module_data_t *, char *, 7070f41fc1SWill Fiveash char **, boolean_t); 717c478bd9Sstevel@tonic-gate void krb5_cleanup(pam_handle_t *, void *, int); 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_boolean(); 747c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_string(); 752278144aSsemery extern int krb5_verifypw(char *, char *, int); 767c478bd9Sstevel@tonic-gate extern krb5_error_code krb5_verify_init_creds(krb5_context, 777c478bd9Sstevel@tonic-gate krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *, 787c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt *); 793125ebfcSsemery extern krb5_error_code __krb5_get_init_creds_password(krb5_context, 803125ebfcSsemery krb5_creds *, krb5_principal, char *, krb5_prompter_fct, void *, 813125ebfcSsemery krb5_deltat, char *, krb5_get_init_creds_opt *, 823125ebfcSsemery krb5_kdc_rep **); 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 857c478bd9Sstevel@tonic-gate * pam_sm_authenticate - Authenticate user 867c478bd9Sstevel@tonic-gate */ 877c478bd9Sstevel@tonic-gate int 887c478bd9Sstevel@tonic-gate pam_sm_authenticate( 897c478bd9Sstevel@tonic-gate pam_handle_t *pamh, 907c478bd9Sstevel@tonic-gate int flags, 917c478bd9Sstevel@tonic-gate int argc, 927c478bd9Sstevel@tonic-gate const char **argv) 937c478bd9Sstevel@tonic-gate { 940be37caaSsemery char *user = NULL; 957c478bd9Sstevel@tonic-gate int err; 967c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR; 977c478bd9Sstevel@tonic-gate /* pam.conf options */ 987c478bd9Sstevel@tonic-gate int debug = 0; 997c478bd9Sstevel@tonic-gate int warn = 1; 1007c478bd9Sstevel@tonic-gate /* return an error on password expire */ 1017c478bd9Sstevel@tonic-gate int err_on_exp = 0; 1027c478bd9Sstevel@tonic-gate int i; 1037c478bd9Sstevel@tonic-gate char *password = NULL; 1047c478bd9Sstevel@tonic-gate uid_t pw_uid; 1057c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = NULL; 1067c478bd9Sstevel@tonic-gate krb5_repository_data_t *krb5_data = NULL; 1077c478bd9Sstevel@tonic-gate pam_repository_t *rep_data = NULL; 10870f41fc1SWill Fiveash boolean_t do_pkinit = FALSE; 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 1117c478bd9Sstevel@tonic-gate if (strcmp(argv[i], "debug") == 0) { 1127c478bd9Sstevel@tonic-gate debug = 1; 1137c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "nowarn") == 0) { 1147c478bd9Sstevel@tonic-gate warn = 0; 1157c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "err_on_exp") == 0) { 1167c478bd9Sstevel@tonic-gate err_on_exp = 1; 11770f41fc1SWill Fiveash } else if (strcmp(argv[i], "pkinit") == 0) { 11870f41fc1SWill Fiveash do_pkinit = TRUE; 1197c478bd9Sstevel@tonic-gate } else { 1203bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 12170f41fc1SWill Fiveash "PAM-KRB5 (auth) unrecognized option %s", argv[i]); 1227c478bd9Sstevel@tonic-gate } 1237c478bd9Sstevel@tonic-gate } 1247c478bd9Sstevel@tonic-gate if (flags & PAM_SILENT) warn = 0; 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate if (debug) 1273bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 1287c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_authenticate flags=%d", 1297c478bd9Sstevel@tonic-gate flags); 1307c478bd9Sstevel@tonic-gate 13170f41fc1SWill Fiveash /* 13270f41fc1SWill Fiveash * pam_get_data could fail if we are being called for the first time 13370f41fc1SWill Fiveash * or if the module is not found, PAM_NO_MODULE_DATA is not an error 13470f41fc1SWill Fiveash */ 13570f41fc1SWill Fiveash err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd); 13670f41fc1SWill Fiveash if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA)) 13770f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 13870f41fc1SWill Fiveash 13970f41fc1SWill Fiveash /* 14070f41fc1SWill Fiveash * If pam_krb5 was stacked higher in the auth stack and did PKINIT 14170f41fc1SWill Fiveash * preauth sucessfully then this instance is a fallback to password 14270f41fc1SWill Fiveash * based preauth and should just return PAM_IGNORE. 14370f41fc1SWill Fiveash * 14470f41fc1SWill Fiveash * The else clause is handled further down. 14570f41fc1SWill Fiveash */ 14670f41fc1SWill Fiveash if (kmd != NULL) { 14770f41fc1SWill Fiveash if (++(kmd->auth_calls) > 2) { 14870f41fc1SWill Fiveash /* 14970f41fc1SWill Fiveash * pam_krb5 has been stacked > 2 times in the auth 15070f41fc1SWill Fiveash * stack. Clear out the current kmd and proceed as if 15170f41fc1SWill Fiveash * this is the first time pam_krb5 auth has been called. 15270f41fc1SWill Fiveash */ 15370f41fc1SWill Fiveash if (debug) { 15470f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 15570f41fc1SWill Fiveash "PAM-KRB5 (auth): stacked more than" 15670f41fc1SWill Fiveash " two times, clearing kmd"); 15770f41fc1SWill Fiveash } 15870f41fc1SWill Fiveash /* clear out/free current kmd */ 15970f41fc1SWill Fiveash err = pam_set_data(pamh, KRB5_DATA, NULL, NULL); 16070f41fc1SWill Fiveash if (err != PAM_SUCCESS) { 16170f41fc1SWill Fiveash krb5_cleanup(pamh, kmd, err); 16270f41fc1SWill Fiveash result = err; 16370f41fc1SWill Fiveash goto out; 16470f41fc1SWill Fiveash } 16570f41fc1SWill Fiveash kmd = NULL; 16670f41fc1SWill Fiveash } else if (kmd->auth_calls == 2 && 16770f41fc1SWill Fiveash kmd->auth_status == PAM_SUCCESS) { 16870f41fc1SWill Fiveash /* 16970f41fc1SWill Fiveash * The previous instance of pam_krb5 succeeded and this 17070f41fc1SWill Fiveash * instance was a fall back in case it didn't succeed so 17170f41fc1SWill Fiveash * return ignore. 17270f41fc1SWill Fiveash */ 17370f41fc1SWill Fiveash if (debug) { 17470f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 17570f41fc1SWill Fiveash "PAM-KRB5 (auth): PKINIT succeeded " 17670f41fc1SWill Fiveash "earlier so returning PAM_IGNORE"); 17770f41fc1SWill Fiveash } 17870f41fc1SWill Fiveash return (PAM_IGNORE); 17970f41fc1SWill Fiveash } 18070f41fc1SWill Fiveash } 18170f41fc1SWill Fiveash 1820be37caaSsemery (void) pam_get_item(pamh, PAM_USER, (void**) &user); 1837c478bd9Sstevel@tonic-gate 184a0e56b0eSsemery if (user == NULL || *user == '\0') { 18570f41fc1SWill Fiveash if (do_pkinit) { 18670f41fc1SWill Fiveash /* 18770f41fc1SWill Fiveash * If doing PKINIT it is okay to prompt for the user 18870f41fc1SWill Fiveash * name. 18970f41fc1SWill Fiveash */ 19070f41fc1SWill Fiveash if ((err = pam_get_user(pamh, &user, NULL)) != 19170f41fc1SWill Fiveash PAM_SUCCESS) { 19270f41fc1SWill Fiveash if (debug) { 19370f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 19470f41fc1SWill Fiveash "PAM-KRB5 (auth): get user failed: " 19570f41fc1SWill Fiveash "%s", pam_strerror(pamh, err)); 19670f41fc1SWill Fiveash } 19770f41fc1SWill Fiveash return (err); 19870f41fc1SWill Fiveash } 19970f41fc1SWill Fiveash } else { 20070f41fc1SWill Fiveash if (debug) 20170f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 20270f41fc1SWill Fiveash "PAM-KRB5 (auth): user empty or null"); 20370f41fc1SWill Fiveash return (PAM_USER_UNKNOWN); 20470f41fc1SWill Fiveash } 2057c478bd9Sstevel@tonic-gate } 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* make sure a password entry exists for this user */ 2080be37caaSsemery if (!get_pw_uid(user, &pw_uid)) 2090be37caaSsemery return (PAM_USER_UNKNOWN); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate if (kmd == NULL) { 2127c478bd9Sstevel@tonic-gate kmd = calloc(1, sizeof (krb5_module_data_t)); 2137c478bd9Sstevel@tonic-gate if (kmd == NULL) { 2147c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 2157c478bd9Sstevel@tonic-gate goto out; 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup); 2197c478bd9Sstevel@tonic-gate if (err != PAM_SUCCESS) { 2207c478bd9Sstevel@tonic-gate free(kmd); 2217c478bd9Sstevel@tonic-gate result = err; 2227c478bd9Sstevel@tonic-gate goto out; 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate if (!kmd->env) { 2277c478bd9Sstevel@tonic-gate char buffer[512]; 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate if (snprintf(buffer, sizeof (buffer), 230b0c1f5b7SWill Fiveash "%s=FILE:/tmp/krb5cc_%d", 231b0c1f5b7SWill Fiveash KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) { 2327c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 2337c478bd9Sstevel@tonic-gate goto out; 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate /* we MUST copy this to the heap for the putenv to work! */ 2377c478bd9Sstevel@tonic-gate kmd->env = strdup(buffer); 2387c478bd9Sstevel@tonic-gate if (!kmd->env) { 2397c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 2407c478bd9Sstevel@tonic-gate goto out; 2417c478bd9Sstevel@tonic-gate } else { 2427c478bd9Sstevel@tonic-gate if (putenv(kmd->env)) { 2437c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 2447c478bd9Sstevel@tonic-gate goto out; 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 24967c90040Ssemery if (kmd->user != NULL) 25067c90040Ssemery free(kmd->user); 2513bfb48feSsemery if ((kmd->user = strdup(user)) == NULL) { 2523bfb48feSsemery result = PAM_BUF_ERR; 2533bfb48feSsemery goto out; 2543bfb48feSsemery } 2553bfb48feSsemery 2567c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_AUTH_ERR; 2577c478bd9Sstevel@tonic-gate kmd->debug = debug; 2587c478bd9Sstevel@tonic-gate kmd->warn = warn; 2597c478bd9Sstevel@tonic-gate kmd->err_on_exp = err_on_exp; 2607c478bd9Sstevel@tonic-gate kmd->ccache = NULL; 2617c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 2627c478bd9Sstevel@tonic-gate kmd->password = NULL; 2637c478bd9Sstevel@tonic-gate kmd->age_status = PAM_SUCCESS; 2647c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 26570f41fc1SWill Fiveash kmd->auth_calls = 1; 26670f41fc1SWill Fiveash kmd->preauth_type = do_pkinit ? KRB_PKINIT : KRB_PASSWD; 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate /* 2697c478bd9Sstevel@tonic-gate * For apps that already did krb5 auth exchange... 2707c478bd9Sstevel@tonic-gate * Now that we've created the kmd structure, we can 2717c478bd9Sstevel@tonic-gate * return SUCCESS. 'kmd' may be needed later by other 2727c478bd9Sstevel@tonic-gate * PAM functions, thats why we wait until this point to 2737c478bd9Sstevel@tonic-gate * return. 2747c478bd9Sstevel@tonic-gate */ 2750be37caaSsemery (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data); 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate if (rep_data != NULL) { 2787c478bd9Sstevel@tonic-gate if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) { 2797c478bd9Sstevel@tonic-gate if (debug) 2803bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 281b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): wrong" 282b0c1f5b7SWill Fiveash "repository found (%s), returning " 283b0c1f5b7SWill Fiveash "PAM_IGNORE", rep_data->type); 2847c478bd9Sstevel@tonic-gate return (PAM_IGNORE); 2857c478bd9Sstevel@tonic-gate } 2867c478bd9Sstevel@tonic-gate if (rep_data->scope_len == sizeof (krb5_repository_data_t)) { 2877c478bd9Sstevel@tonic-gate krb5_data = (krb5_repository_data_t *)rep_data->scope; 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate if (krb5_data->flags == 290b0c1f5b7SWill Fiveash SUNW_PAM_KRB5_ALREADY_AUTHENTICATED && 291b0c1f5b7SWill Fiveash krb5_data->principal != NULL && 292b0c1f5b7SWill Fiveash strlen(krb5_data->principal)) { 2937c478bd9Sstevel@tonic-gate if (debug) 2943bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 295b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Principal " 296b0c1f5b7SWill Fiveash "%s already authenticated", 297b0c1f5b7SWill Fiveash krb5_data->principal); 2987c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_SUCCESS; 2997c478bd9Sstevel@tonic-gate return (PAM_SUCCESS); 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate } 3027c478bd9Sstevel@tonic-gate } 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate /* 3057c478bd9Sstevel@tonic-gate * if root key exists in the keytab, it's a random key so no 3067c478bd9Sstevel@tonic-gate * need to prompt for pw and we just return IGNORE. 3077c478bd9Sstevel@tonic-gate * 3087c478bd9Sstevel@tonic-gate * note we don't need to force a prompt for pw as authtok_get 3097c478bd9Sstevel@tonic-gate * is required to be stacked above this module. 3107c478bd9Sstevel@tonic-gate */ 3117c478bd9Sstevel@tonic-gate if ((strcmp(user, ROOT_UNAME) == 0) && 3127c478bd9Sstevel@tonic-gate key_in_keytab(user, debug)) { 3137c478bd9Sstevel@tonic-gate if (debug) 3143bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3157c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): " 3167c478bd9Sstevel@tonic-gate "key for '%s' in keytab, returning IGNORE", user); 3177c478bd9Sstevel@tonic-gate result = PAM_IGNORE; 3187c478bd9Sstevel@tonic-gate goto out; 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate 3210be37caaSsemery (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 3227c478bd9Sstevel@tonic-gate 32370f41fc1SWill Fiveash result = attempt_krb5_auth(pamh, kmd, user, &password, 1); 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate out: 3267c478bd9Sstevel@tonic-gate if (kmd) { 3277c478bd9Sstevel@tonic-gate if (debug) 3283bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3297c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_auth finalize" 3307c478bd9Sstevel@tonic-gate " ccname env, result =%d, env ='%s'," 3317c478bd9Sstevel@tonic-gate " age = %d, status = %d", 3327c478bd9Sstevel@tonic-gate result, kmd->env ? kmd->env : "<null>", 3337c478bd9Sstevel@tonic-gate kmd->age_status, kmd->auth_status); 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate if (kmd->env && 3367c478bd9Sstevel@tonic-gate !(kmd->age_status == PAM_NEW_AUTHTOK_REQD && 337b0c1f5b7SWill Fiveash kmd->auth_status == PAM_SUCCESS)) { 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate if (result == PAM_SUCCESS) { 3417c478bd9Sstevel@tonic-gate /* 3427c478bd9Sstevel@tonic-gate * Put ccname into the pamh so that login 3437c478bd9Sstevel@tonic-gate * apps can pick this up when they run 3447c478bd9Sstevel@tonic-gate * pam_getenvlist(). 3457c478bd9Sstevel@tonic-gate */ 3467c478bd9Sstevel@tonic-gate if ((result = pam_putenv(pamh, kmd->env)) 3477c478bd9Sstevel@tonic-gate != PAM_SUCCESS) { 3487c478bd9Sstevel@tonic-gate /* should not happen but... */ 3493bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 3507c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth):" 3513bfb48feSsemery " pam_putenv failed: result: %d", 3527c478bd9Sstevel@tonic-gate result); 3537c478bd9Sstevel@tonic-gate goto cleanupccname; 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate } else { 3567c478bd9Sstevel@tonic-gate cleanupccname: 3577c478bd9Sstevel@tonic-gate /* for lack of a Solaris unputenv() */ 3587c478bd9Sstevel@tonic-gate krb5_unsetenv(KRB5_ENV_CCNAME); 3597c478bd9Sstevel@tonic-gate free(kmd->env); 3607c478bd9Sstevel@tonic-gate kmd->env = NULL; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate } 3637c478bd9Sstevel@tonic-gate kmd->auth_status = result; 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate if (debug) 3673bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3687c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result)); 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate return (result); 3717c478bd9Sstevel@tonic-gate } 3727c478bd9Sstevel@tonic-gate 37370f41fc1SWill Fiveash static krb5_error_code 37470f41fc1SWill Fiveash pam_krb5_prompter( 37570f41fc1SWill Fiveash krb5_context ctx, 37670f41fc1SWill Fiveash void *data, 37770f41fc1SWill Fiveash /* ARGSUSED1 */ 37870f41fc1SWill Fiveash const char *name, 37970f41fc1SWill Fiveash const char *banner, 38070f41fc1SWill Fiveash int num_prompts, 38170f41fc1SWill Fiveash krb5_prompt prompts[]) 38270f41fc1SWill Fiveash { 38370f41fc1SWill Fiveash krb5_error_code rc; 38470f41fc1SWill Fiveash pam_handle_t *pamh = (pam_handle_t *)data; 38570f41fc1SWill Fiveash struct pam_conv *pam_convp; 38670f41fc1SWill Fiveash struct pam_message *msgs; 38770f41fc1SWill Fiveash struct pam_response *ret_respp; 38870f41fc1SWill Fiveash int i; 38970f41fc1SWill Fiveash krb5_prompt_type *prompt_type = krb5_get_prompt_types(ctx); 39070f41fc1SWill Fiveash char tmpbuf[PAM_MAX_MSG_SIZE]; 39170f41fc1SWill Fiveash 39270f41fc1SWill Fiveash /* 39370f41fc1SWill Fiveash * Because this function should never be used for password prompts, 39470f41fc1SWill Fiveash * disallow password prompts. 39570f41fc1SWill Fiveash */ 39670f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 397*a1219d13SWill Fiveash switch (prompt_type[i]) { 398*a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_PASSWORD: 399*a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_NEW_PASSWORD: 400*a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN: 40170f41fc1SWill Fiveash return (KRB5_LIBOS_CANTREADPWD); 402*a1219d13SWill Fiveash } 40370f41fc1SWill Fiveash } 40470f41fc1SWill Fiveash 40570f41fc1SWill Fiveash if (num_prompts == 0) { 40670f41fc1SWill Fiveash if (prompts) { 40770f41fc1SWill Fiveash /* This shouldn't happen */ 40870f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 40970f41fc1SWill Fiveash } else { 41070f41fc1SWill Fiveash /* no prompts so return */ 41170f41fc1SWill Fiveash return (0); 41270f41fc1SWill Fiveash } 41370f41fc1SWill Fiveash } 41470f41fc1SWill Fiveash if ((rc = pam_get_item(pamh, PAM_CONV, (void **)&pam_convp)) 41570f41fc1SWill Fiveash != PAM_SUCCESS) { 41670f41fc1SWill Fiveash return (rc); 41770f41fc1SWill Fiveash } 41870f41fc1SWill Fiveash if (pam_convp == NULL) { 41970f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 42070f41fc1SWill Fiveash } 42170f41fc1SWill Fiveash 42270f41fc1SWill Fiveash msgs = (struct pam_message *)calloc(num_prompts, 42370f41fc1SWill Fiveash sizeof (struct pam_message)); 42470f41fc1SWill Fiveash if (msgs == NULL) { 42570f41fc1SWill Fiveash return (PAM_BUF_ERR); 42670f41fc1SWill Fiveash } 42770f41fc1SWill Fiveash (void) memset(msgs, 0, sizeof (struct pam_message) * num_prompts); 42870f41fc1SWill Fiveash 42970f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 43070f41fc1SWill Fiveash /* convert krb prompt style to PAM style */ 43170f41fc1SWill Fiveash if (prompts[i].hidden) { 43270f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_OFF; 43370f41fc1SWill Fiveash } else { 43470f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_ON; 43570f41fc1SWill Fiveash } 43670f41fc1SWill Fiveash /* 43770f41fc1SWill Fiveash * krb expects the prompting function to append ": " to the 43870f41fc1SWill Fiveash * prompt string. 43970f41fc1SWill Fiveash */ 44070f41fc1SWill Fiveash if (snprintf(tmpbuf, sizeof (tmpbuf), "%s: ", 44170f41fc1SWill Fiveash prompts[i].prompt) < 0) { 44270f41fc1SWill Fiveash rc = PAM_BUF_ERR; 44370f41fc1SWill Fiveash goto cleanup; 44470f41fc1SWill Fiveash } 44570f41fc1SWill Fiveash msgs[i].msg = strdup(tmpbuf); 44670f41fc1SWill Fiveash if (msgs[i].msg == NULL) { 44770f41fc1SWill Fiveash rc = PAM_BUF_ERR; 44870f41fc1SWill Fiveash goto cleanup; 44970f41fc1SWill Fiveash } 45070f41fc1SWill Fiveash } 45170f41fc1SWill Fiveash 45270f41fc1SWill Fiveash /* 45370f41fc1SWill Fiveash * Call PAM conv function to display the prompt. 45470f41fc1SWill Fiveash */ 45570f41fc1SWill Fiveash rc = (pam_convp->conv)(num_prompts, &msgs, &ret_respp, 45670f41fc1SWill Fiveash pam_convp->appdata_ptr); 45770f41fc1SWill Fiveash 45870f41fc1SWill Fiveash if (rc == PAM_SUCCESS) { 45970f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 46070f41fc1SWill Fiveash /* convert PAM response to krb prompt reply format */ 46170f41fc1SWill Fiveash prompts[i].reply->length = strlen(ret_respp[i].resp) + 46270f41fc1SWill Fiveash 1; /* adding 1 for NULL terminator */ 46370f41fc1SWill Fiveash prompts[i].reply->data = ret_respp[i].resp; 46470f41fc1SWill Fiveash } 46570f41fc1SWill Fiveash /* 46670f41fc1SWill Fiveash * Note, just free ret_respp, not the resp data since that is 46770f41fc1SWill Fiveash * being referenced by the krb prompt reply data pointer. 46870f41fc1SWill Fiveash */ 46970f41fc1SWill Fiveash free(ret_respp); 47070f41fc1SWill Fiveash } 47170f41fc1SWill Fiveash 47270f41fc1SWill Fiveash cleanup: 47370f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 47470f41fc1SWill Fiveash if (msgs[i].msg) { 47570f41fc1SWill Fiveash free(msgs[i].msg); 47670f41fc1SWill Fiveash } 47770f41fc1SWill Fiveash } 47870f41fc1SWill Fiveash free(msgs); 47970f41fc1SWill Fiveash return (rc); 48070f41fc1SWill Fiveash } 48170f41fc1SWill Fiveash 4827c478bd9Sstevel@tonic-gate int 4837c478bd9Sstevel@tonic-gate attempt_krb5_auth( 48470f41fc1SWill Fiveash pam_handle_t *pamh, 4857c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd, 4867c478bd9Sstevel@tonic-gate char *user, 4877c478bd9Sstevel@tonic-gate char **krb5_pass, 4880be37caaSsemery boolean_t verify_tik) 4897c478bd9Sstevel@tonic-gate { 49067c90040Ssemery krb5_principal me = NULL, clientp = NULL; 49167c90040Ssemery krb5_principal server = NULL, serverp = NULL; 4927c478bd9Sstevel@tonic-gate krb5_creds *my_creds; 4937c478bd9Sstevel@tonic-gate krb5_timestamp now; 4947c478bd9Sstevel@tonic-gate krb5_error_code code = 0; 4957c478bd9Sstevel@tonic-gate char kuser[2*MAXHOSTNAMELEN]; 4967c478bd9Sstevel@tonic-gate krb5_deltat lifetime; 4977c478bd9Sstevel@tonic-gate krb5_deltat rlife; 4987c478bd9Sstevel@tonic-gate krb5_deltat krb5_max_duration; 4997c478bd9Sstevel@tonic-gate int options = KRB5_DEFAULT_OPTIONS; 5007c478bd9Sstevel@tonic-gate krb5_data tgtname = { 5017c478bd9Sstevel@tonic-gate 0, 5027c478bd9Sstevel@tonic-gate KRB5_TGS_NAME_SIZE, 5037c478bd9Sstevel@tonic-gate KRB5_TGS_NAME 5047c478bd9Sstevel@tonic-gate }; 505488060a6SWill Fiveash krb5_get_init_creds_opt *opts = NULL; 5063125ebfcSsemery krb5_kdc_rep *as_reply = NULL; 5077c478bd9Sstevel@tonic-gate /* 5087c478bd9Sstevel@tonic-gate * "result" should not be assigned PAM_SUCCESS unless 5097c478bd9Sstevel@tonic-gate * authentication has succeeded and there are no other errors. 5107c478bd9Sstevel@tonic-gate * 5117c478bd9Sstevel@tonic-gate * "code" is sometimes used for PAM codes, sometimes for krb5 5127c478bd9Sstevel@tonic-gate * codes. Be careful. 5137c478bd9Sstevel@tonic-gate */ 5147c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR; 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate if (kmd->debug) 5173bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 5187c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'", 5197c478bd9Sstevel@tonic-gate user ? user : "<null>"); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate /* need to free context with krb5_free_context */ 5228ce3ffdfSPeter Shoults if (code = krb5_init_secure_context(&kmd->kcontext)) { 5233bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 524b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Error initializing " 525b0c1f5b7SWill Fiveash "krb5: %s", 526b0c1f5b7SWill Fiveash error_message(code)); 5277c478bd9Sstevel@tonic-gate return (PAM_SYSTEM_ERR); 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser, 531b0c1f5b7SWill Fiveash 2*MAXHOSTNAMELEN)) != 0) { 5327c478bd9Sstevel@tonic-gate /* get_kmd_kuser returns proper PAM error statuses */ 5337c478bd9Sstevel@tonic-gate return (code); 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) { 5377c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext); 5387c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 5393bfb48feSsemery return (PAM_SYSTEM_ERR); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate /* call krb5_free_cred_contents() on error */ 5437c478bd9Sstevel@tonic-gate my_creds = &kmd->initcreds; 5447c478bd9Sstevel@tonic-gate 5453bfb48feSsemery if ((code = 5463bfb48feSsemery krb5_copy_principal(kmd->kcontext, me, &my_creds->client))) { 5473bfb48feSsemery result = PAM_SYSTEM_ERR; 5483bfb48feSsemery goto out_err; 5493bfb48feSsemery } 55067c90040Ssemery clientp = my_creds->client; 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate if (code = krb5_build_principal_ext(kmd->kcontext, &server, 553b0c1f5b7SWill Fiveash krb5_princ_realm(kmd->kcontext, me)->length, 554b0c1f5b7SWill Fiveash krb5_princ_realm(kmd->kcontext, me)->data, 555b0c1f5b7SWill Fiveash tgtname.length, tgtname.data, 556b0c1f5b7SWill Fiveash krb5_princ_realm(kmd->kcontext, me)->length, 557b0c1f5b7SWill Fiveash krb5_princ_realm(kmd->kcontext, me)->data, 0)) { 5583bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 559b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): attempt_krb5_auth: " 560b0c1f5b7SWill Fiveash "krb5_build_princ_ext failed: %s", 561b0c1f5b7SWill Fiveash error_message(code)); 5623bfb48feSsemery result = PAM_SYSTEM_ERR; 5637c478bd9Sstevel@tonic-gate goto out; 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate if (code = krb5_copy_principal(kmd->kcontext, server, 567b0c1f5b7SWill Fiveash &my_creds->server)) { 5683bfb48feSsemery result = PAM_SYSTEM_ERR; 5697c478bd9Sstevel@tonic-gate goto out_err; 5707c478bd9Sstevel@tonic-gate } 57167c90040Ssemery serverp = my_creds->server; 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate if (code = krb5_timeofday(kmd->kcontext, &now)) { 5743bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 575b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): attempt_krb5_auth: " 576b0c1f5b7SWill Fiveash "krb5_timeofday failed: %s", 577b0c1f5b7SWill Fiveash error_message(code)); 5783bfb48feSsemery result = PAM_SYSTEM_ERR; 5797c478bd9Sstevel@tonic-gate goto out; 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate 5827c478bd9Sstevel@tonic-gate /* 5837c478bd9Sstevel@tonic-gate * set the values for lifetime and rlife to be the maximum 5847c478bd9Sstevel@tonic-gate * possible 5857c478bd9Sstevel@tonic-gate */ 5867c478bd9Sstevel@tonic-gate krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 5877c478bd9Sstevel@tonic-gate lifetime = krb5_max_duration; 5887c478bd9Sstevel@tonic-gate rlife = krb5_max_duration; 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* 5917c478bd9Sstevel@tonic-gate * Let us get the values for various options 5927c478bd9Sstevel@tonic-gate * from Kerberos configuration file 5937c478bd9Sstevel@tonic-gate */ 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate krb_realm = krb5_princ_realm(kmd->kcontext, me)->data; 5967c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile, 597b0c1f5b7SWill Fiveash realmdef, config_option); 5987c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile, 599b0c1f5b7SWill Fiveash appdef, config_option); 6007c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile, 601b0c1f5b7SWill Fiveash realmdef, config_times); 6027c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile, 603b0c1f5b7SWill Fiveash appdef, config_times); 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate if (renew_timeval) { 6067c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(renew_timeval, &rlife); 6077c478bd9Sstevel@tonic-gate if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 6083bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 609b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Bad max_renewable_life " 610b0c1f5b7SWill Fiveash " value '%s' in Kerberos config file", 6117c478bd9Sstevel@tonic-gate renew_timeval); 6127c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 6137c478bd9Sstevel@tonic-gate goto out; 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate if (life_timeval) { 6177c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(life_timeval, &lifetime); 6187c478bd9Sstevel@tonic-gate if (code != 0 || lifetime == 0 || 6197c478bd9Sstevel@tonic-gate lifetime > krb5_max_duration) { 6203bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 621b0c1f5b7SWill Fiveash "lifetime value '%s' in Kerberos config file", 6227c478bd9Sstevel@tonic-gate life_timeval); 6237c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 6247c478bd9Sstevel@tonic-gate goto out; 6257c478bd9Sstevel@tonic-gate } 6267c478bd9Sstevel@tonic-gate } 6277c478bd9Sstevel@tonic-gate /* start timer when request gets to KDC */ 6287c478bd9Sstevel@tonic-gate my_creds->times.starttime = 0; 6297c478bd9Sstevel@tonic-gate my_creds->times.endtime = now + lifetime; 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate if (options & KDC_OPT_RENEWABLE) { 6327c478bd9Sstevel@tonic-gate my_creds->times.renew_till = now + rlife; 6337c478bd9Sstevel@tonic-gate } else 6347c478bd9Sstevel@tonic-gate my_creds->times.renew_till = 0; 6357c478bd9Sstevel@tonic-gate 636488060a6SWill Fiveash code = krb5_get_init_creds_opt_alloc(kmd->kcontext, &opts); 637488060a6SWill Fiveash if (code != 0) { 638488060a6SWill Fiveash __pam_log(LOG_AUTH | LOG_ERR, 639488060a6SWill Fiveash "Error allocating gic opts: %s", 640488060a6SWill Fiveash error_message(code)); 641488060a6SWill Fiveash result = PAM_SYSTEM_ERR; 642488060a6SWill Fiveash goto out; 643488060a6SWill Fiveash } 644488060a6SWill Fiveash 645488060a6SWill Fiveash krb5_get_init_creds_opt_set_tkt_life(opts, lifetime); 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate if (proxiable_flag) { /* Set in config file */ 6487c478bd9Sstevel@tonic-gate if (kmd->debug) 6493bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 650b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Proxiable tickets " 651b0c1f5b7SWill Fiveash "requested"); 652488060a6SWill Fiveash krb5_get_init_creds_opt_set_proxiable(opts, TRUE); 6537c478bd9Sstevel@tonic-gate } 6547c478bd9Sstevel@tonic-gate if (forwardable_flag) { 6557c478bd9Sstevel@tonic-gate if (kmd->debug) 6563bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 657b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Forwardable tickets " 658b0c1f5b7SWill Fiveash "requested"); 659488060a6SWill Fiveash krb5_get_init_creds_opt_set_forwardable(opts, TRUE); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate if (renewable_flag) { 6627c478bd9Sstevel@tonic-gate if (kmd->debug) 6633bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 664b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Renewable tickets " 665b0c1f5b7SWill Fiveash "requested"); 666488060a6SWill Fiveash krb5_get_init_creds_opt_set_renew_life(opts, rlife); 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate if (no_address_flag) { 6697c478bd9Sstevel@tonic-gate if (kmd->debug) 6703bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 671b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): Addressless tickets " 672b0c1f5b7SWill Fiveash "requested"); 673488060a6SWill Fiveash krb5_get_init_creds_opt_set_address_list(opts, NULL); 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate /* 67770f41fc1SWill Fiveash * mech_krb5 interprets empty passwords as NULL passwords and tries to 67870f41fc1SWill Fiveash * read a password from stdin. Since we are in pam this is bad and 67970f41fc1SWill Fiveash * should not be allowed. 68070f41fc1SWill Fiveash * 68170f41fc1SWill Fiveash * Note, the logic now is that if the preauth_type is PKINIT then 68270f41fc1SWill Fiveash * provide a proper PAMcentric prompt function that the underlying 68370f41fc1SWill Fiveash * PKINIT preauth plugin will use to prompt for the PIN. 6847c478bd9Sstevel@tonic-gate */ 68570f41fc1SWill Fiveash if (kmd->preauth_type == KRB_PKINIT) { 68670f41fc1SWill Fiveash /* 68770f41fc1SWill Fiveash * Do PKINIT preauth 68870f41fc1SWill Fiveash * 68970f41fc1SWill Fiveash * Note: we want to limit preauth types to just those for PKINIT 69070f41fc1SWill Fiveash * but krb5_get_init_creds() doesn't support that at this point. 69170f41fc1SWill Fiveash * Instead we rely on pam_krb5_prompter() to limit prompts to 69270f41fc1SWill Fiveash * non-password types. So all we can do here is set the preauth 69370f41fc1SWill Fiveash * list so krb5_get_init_creds() will try that first. 69470f41fc1SWill Fiveash */ 69570f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = { 69670f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ, 69770f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ_OLD 69870f41fc1SWill Fiveash }; 699488060a6SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(opts, pk_pa_list, 2); 70070f41fc1SWill Fiveash 701488060a6SWill Fiveash if (*krb5_pass == NULL || strlen(*krb5_pass) != 0) { 702488060a6SWill Fiveash if (*krb5_pass != NULL) { 703488060a6SWill Fiveash /* treat the krb5_pass as a PIN */ 704488060a6SWill Fiveash code = krb5_get_init_creds_opt_set_pa( 705488060a6SWill Fiveash kmd->kcontext, opts, "PIN", *krb5_pass); 706488060a6SWill Fiveash } 707488060a6SWill Fiveash 708488060a6SWill Fiveash if (!code) { 709488060a6SWill Fiveash code = __krb5_get_init_creds_password( 710488060a6SWill Fiveash kmd->kcontext, 711488060a6SWill Fiveash my_creds, 712488060a6SWill Fiveash me, 713488060a6SWill Fiveash NULL, /* clear text passwd */ 714488060a6SWill Fiveash pam_krb5_prompter, /* prompter */ 715488060a6SWill Fiveash pamh, /* prompter data */ 716488060a6SWill Fiveash 0, /* start time */ 717488060a6SWill Fiveash NULL, /* defaults to krbtgt@REALM */ 718488060a6SWill Fiveash opts, 719488060a6SWill Fiveash &as_reply); 720488060a6SWill Fiveash } 72170f41fc1SWill Fiveash } else { 722488060a6SWill Fiveash /* invalid PIN */ 72370f41fc1SWill Fiveash code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 72470f41fc1SWill Fiveash } 7257c478bd9Sstevel@tonic-gate } else { 7263125ebfcSsemery /* 72770f41fc1SWill Fiveash * Do password based preauths 72870f41fc1SWill Fiveash * 72970f41fc1SWill Fiveash * See earlier PKINIT comment. We are doing something similar 73070f41fc1SWill Fiveash * here but we do not pass in a prompter (we assume 73170f41fc1SWill Fiveash * pam_authtok_get has already prompted for that). 7323125ebfcSsemery */ 73370f41fc1SWill Fiveash if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) { 73470f41fc1SWill Fiveash code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 73570f41fc1SWill Fiveash } else { 73670f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = { 73770f41fc1SWill Fiveash KRB5_PADATA_ENC_TIMESTAMP 73870f41fc1SWill Fiveash }; 73970f41fc1SWill Fiveash 740488060a6SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(opts, 74170f41fc1SWill Fiveash pk_pa_list, 1); 74270f41fc1SWill Fiveash 74370f41fc1SWill Fiveash /* 74470f41fc1SWill Fiveash * We call our own private version of gic_pwd, because 74570f41fc1SWill Fiveash * we need more information, such as password/account 74670f41fc1SWill Fiveash * expiration, that is found in the as_reply. The 74770f41fc1SWill Fiveash * "prompter" interface is not granular enough for PAM 74870f41fc1SWill Fiveash * to make use of. 74970f41fc1SWill Fiveash */ 75070f41fc1SWill Fiveash code = __krb5_get_init_creds_password(kmd->kcontext, 75170f41fc1SWill Fiveash my_creds, 75270f41fc1SWill Fiveash me, 75370f41fc1SWill Fiveash *krb5_pass, /* clear text passwd */ 75470f41fc1SWill Fiveash NULL, /* prompter */ 75570f41fc1SWill Fiveash NULL, /* data */ 75670f41fc1SWill Fiveash 0, /* start time */ 75770f41fc1SWill Fiveash NULL, /* defaults to krbtgt@REALM */ 758488060a6SWill Fiveash opts, 75970f41fc1SWill Fiveash &as_reply); 76070f41fc1SWill Fiveash } 7617c478bd9Sstevel@tonic-gate } 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate if (kmd->debug) 7643bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 7657c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: " 7667c478bd9Sstevel@tonic-gate "krb5_get_init_creds_password returns: %s", 7677c478bd9Sstevel@tonic-gate code == 0 ? "SUCCESS" : error_message(code)); 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate switch (code) { 7707c478bd9Sstevel@tonic-gate case 0: 7717c478bd9Sstevel@tonic-gate /* got a tgt, let's verify it */ 7727c478bd9Sstevel@tonic-gate if (verify_tik) { 7737c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt vopts; 7747c478bd9Sstevel@tonic-gate 775a0709436Smp krb5_principal sp = NULL; 776a0709436Smp char kt_name[MAX_KEYTAB_NAME_LEN]; 777a0709436Smp char *fqdn; 778a0709436Smp 7797c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt_init(&vopts); 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate code = krb5_verify_init_creds(kmd->kcontext, 782b0c1f5b7SWill Fiveash my_creds, 783b0c1f5b7SWill Fiveash NULL, /* defaults to host/localhost@REALM */ 784b0c1f5b7SWill Fiveash NULL, 785b0c1f5b7SWill Fiveash NULL, 786b0c1f5b7SWill Fiveash &vopts); 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate if (code) { 7897c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 7907c478bd9Sstevel@tonic-gate 7910be37caaSsemery /* 7920be37caaSsemery * Give a better error message when the 7930be37caaSsemery * keytable entry isn't found or the keytab 7940be37caaSsemery * file cannot be found. 7950be37caaSsemery */ 796a0709436Smp if (krb5_sname_to_principal(kmd->kcontext, NULL, 797b0c1f5b7SWill Fiveash NULL, KRB5_NT_SRV_HST, &sp)) 798a0709436Smp fqdn = "<fqdn>"; 799a0709436Smp else 800a0709436Smp fqdn = sp->data[1].data; 801a0709436Smp 802a0709436Smp if (krb5_kt_default_name(kmd->kcontext, kt_name, 803b0c1f5b7SWill Fiveash sizeof (kt_name))) 804b0c1f5b7SWill Fiveash (void) strlcpy(kt_name, 805b0c1f5b7SWill Fiveash "default keytab", 806b0c1f5b7SWill Fiveash sizeof (kt_name)); 807a0709436Smp 808a0709436Smp switch (code) { 809a0709436Smp case KRB5_KT_NOTFOUND: 8103bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 811b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): " 812b0c1f5b7SWill Fiveash "krb5_verify_init_creds failed:" 813b0c1f5b7SWill Fiveash " Key table entry \"host/%s\"" 814b0c1f5b7SWill Fiveash " not found in %s", 815b0c1f5b7SWill Fiveash fqdn, kt_name); 816a0709436Smp break; 817a0709436Smp case ENOENT: 8183bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 819b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): " 820b0c1f5b7SWill Fiveash "krb5_verify_init_creds failed:" 821b0c1f5b7SWill Fiveash " Keytab file \"%s\"" 822b0c1f5b7SWill Fiveash " does not exist.\n", 823b0c1f5b7SWill Fiveash kt_name); 824a0709436Smp break; 825a0709436Smp default: 8263bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 827b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): " 828b0c1f5b7SWill Fiveash "krb5_verify_init_creds failed:" 829b0c1f5b7SWill Fiveash " %s", 830b0c1f5b7SWill Fiveash error_message(code)); 831a0709436Smp break; 832a0709436Smp } 833a0709436Smp 834a0709436Smp if (sp) 835a0709436Smp krb5_free_principal(kmd->kcontext, sp); 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate } 8383125ebfcSsemery 8393125ebfcSsemery if (code == 0) 8403125ebfcSsemery kmd->expiration = as_reply->enc_part2->key_exp; 8413125ebfcSsemery 8427c478bd9Sstevel@tonic-gate break; 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 8457c478bd9Sstevel@tonic-gate /* 8467c478bd9Sstevel@tonic-gate * Since this principal is not part of the local 8477c478bd9Sstevel@tonic-gate * Kerberos realm, we just return PAM_USER_UNKNOWN. 8487c478bd9Sstevel@tonic-gate */ 8497c478bd9Sstevel@tonic-gate result = PAM_USER_UNKNOWN; 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate if (kmd->debug) 8523bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 853b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): attempt_krb5_auth:" 854b0c1f5b7SWill Fiveash " User is not part of the local Kerberos" 855b0c1f5b7SWill Fiveash " realm: %s", error_message(code)); 8567c478bd9Sstevel@tonic-gate break; 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_PREAUTH_FAILED: 8597c478bd9Sstevel@tonic-gate case KRB5KRB_AP_ERR_BAD_INTEGRITY: 8607c478bd9Sstevel@tonic-gate /* 8617c478bd9Sstevel@tonic-gate * We could be trying the password from a previous 8627c478bd9Sstevel@tonic-gate * pam authentication module, but we don't want to 8637c478bd9Sstevel@tonic-gate * generate an error if the unix password is different 8647c478bd9Sstevel@tonic-gate * than the Kerberos password... 8657c478bd9Sstevel@tonic-gate */ 8667c478bd9Sstevel@tonic-gate break; 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_KEY_EXP: 8697c478bd9Sstevel@tonic-gate if (!kmd->err_on_exp) { 8707c478bd9Sstevel@tonic-gate /* 87170f41fc1SWill Fiveash * Request a tik for changepw service and it will tell 87270f41fc1SWill Fiveash * us if pw is good or not. If PKINIT is being done it 87370f41fc1SWill Fiveash * is possible that *krb5_pass may be NULL so check for 87470f41fc1SWill Fiveash * that. If that is the case this function will return 87570f41fc1SWill Fiveash * an error. 8767c478bd9Sstevel@tonic-gate */ 87770f41fc1SWill Fiveash if (*krb5_pass != NULL) { 87870f41fc1SWill Fiveash code = krb5_verifypw(kuser, *krb5_pass, 87970f41fc1SWill Fiveash kmd->debug); 88070f41fc1SWill Fiveash if (kmd->debug) { 88170f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 88270f41fc1SWill Fiveash "PAM-KRB5 (auth): " 88370f41fc1SWill Fiveash "attempt_krb5_auth: " 88470f41fc1SWill Fiveash "verifypw %d", code); 88570f41fc1SWill Fiveash } 88670f41fc1SWill Fiveash if (code == 0) { 88770f41fc1SWill Fiveash /* 88870f41fc1SWill Fiveash * pw is good, set age status for 88970f41fc1SWill Fiveash * acct_mgmt. 89070f41fc1SWill Fiveash */ 89170f41fc1SWill Fiveash kmd->age_status = PAM_NEW_AUTHTOK_REQD; 89270f41fc1SWill Fiveash } 8937c478bd9Sstevel@tonic-gate } 89470f41fc1SWill Fiveash 8957c478bd9Sstevel@tonic-gate } 8967c478bd9Sstevel@tonic-gate break; 8977c478bd9Sstevel@tonic-gate 8987c478bd9Sstevel@tonic-gate default: 8997c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 9007c478bd9Sstevel@tonic-gate if (kmd->debug) 9013bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 902b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): error %d - %s", 903b0c1f5b7SWill Fiveash code, error_message(code)); 9047c478bd9Sstevel@tonic-gate break; 9057c478bd9Sstevel@tonic-gate } 9067c478bd9Sstevel@tonic-gate 9077c478bd9Sstevel@tonic-gate if (code == 0) { 9087c478bd9Sstevel@tonic-gate /* 90970f41fc1SWill Fiveash * success for the entered pw or PKINIT succeeded. 9107c478bd9Sstevel@tonic-gate * 9117c478bd9Sstevel@tonic-gate * we can't rely on the pw in PAM_AUTHTOK 9127c478bd9Sstevel@tonic-gate * to be the (correct) krb5 one so 9137c478bd9Sstevel@tonic-gate * store krb5 pw in module data for 91470f41fc1SWill Fiveash * use in acct_mgmt. Note that *krb5_pass may be NULL if we're 91570f41fc1SWill Fiveash * doing PKINIT. 9167c478bd9Sstevel@tonic-gate */ 91770f41fc1SWill Fiveash if (*krb5_pass != NULL && 91870f41fc1SWill Fiveash !(kmd->password = strdup(*krb5_pass))) { 91970f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_ERR, 920b0c1f5b7SWill Fiveash "Cannot strdup password"); 9217c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 9227c478bd9Sstevel@tonic-gate goto out_err; 9237c478bd9Sstevel@tonic-gate } 92470f41fc1SWill Fiveash 9257c478bd9Sstevel@tonic-gate result = PAM_SUCCESS; 9267c478bd9Sstevel@tonic-gate goto out; 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate out_err: 9307c478bd9Sstevel@tonic-gate /* jump (or reach) here if error and cred cache has been init */ 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate if (kmd->debug) 9333bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 9347c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): clearing initcreds in " 9357c478bd9Sstevel@tonic-gate "pam_authenticate()"); 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 9387c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate out: 9417c478bd9Sstevel@tonic-gate if (server) 9427c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, server); 9437c478bd9Sstevel@tonic-gate if (me) 9447c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, me); 9453125ebfcSsemery if (as_reply) 9463125ebfcSsemery krb5_free_kdc_rep(kmd->kcontext, as_reply); 94767c90040Ssemery 94867c90040Ssemery /* 94967c90040Ssemery * clientp or serverp could be NULL in certain error cases in this 95067c90040Ssemery * function. mycreds->[client|server] could also be NULL in case 95167c90040Ssemery * of error in this function, see out_err above. The pointers clientp 95267c90040Ssemery * and serverp reference the input argument in my_creds for 95367c90040Ssemery * get_init_creds and must be freed if the input argument does not 95467c90040Ssemery * match the output argument, which occurs during a successful call 95567c90040Ssemery * to get_init_creds. 95667c90040Ssemery */ 95767c90040Ssemery if (clientp && my_creds->client && clientp != my_creds->client) 95867c90040Ssemery krb5_free_principal(kmd->kcontext, clientp); 95967c90040Ssemery if (serverp && my_creds->server && serverp != my_creds->server) 96067c90040Ssemery krb5_free_principal(kmd->kcontext, serverp); 96167c90040Ssemery 9627c478bd9Sstevel@tonic-gate if (kmd->kcontext) { 9637c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext); 9647c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 9657c478bd9Sstevel@tonic-gate } 966488060a6SWill Fiveash if (opts) 967488060a6SWill Fiveash krb5_get_init_creds_opt_free(kmd->kcontext, opts); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate if (kmd->debug) 9703bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 9717c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth returning %d", 9727c478bd9Sstevel@tonic-gate result); 9737c478bd9Sstevel@tonic-gate 9747c478bd9Sstevel@tonic-gate return (kmd->auth_status = result); 9757c478bd9Sstevel@tonic-gate } 9767c478bd9Sstevel@tonic-gate 9777c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 9787c478bd9Sstevel@tonic-gate void 9797c478bd9Sstevel@tonic-gate krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status) 9807c478bd9Sstevel@tonic-gate { 9817c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = (krb5_module_data_t *)data; 9827c478bd9Sstevel@tonic-gate 9837c478bd9Sstevel@tonic-gate if (kmd == NULL) 9847c478bd9Sstevel@tonic-gate return; 9857c478bd9Sstevel@tonic-gate 9867c478bd9Sstevel@tonic-gate if (kmd->debug) { 9873bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 988b0c1f5b7SWill Fiveash "PAM-KRB5 (auth): krb5_cleanup auth_status = %d", 9897c478bd9Sstevel@tonic-gate kmd->auth_status); 9907c478bd9Sstevel@tonic-gate } 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate /* 99367c90040Ssemery * Apps could be calling pam_end here, so we should always clean 99467c90040Ssemery * up regardless of success or failure here. 9957c478bd9Sstevel@tonic-gate */ 99667c90040Ssemery if (kmd->ccache) 9975ad42b1bSSurya Prakki (void) krb5_cc_close(kmd->kcontext, kmd->ccache); 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate if (kmd->password) { 10007c478bd9Sstevel@tonic-gate (void) memset(kmd->password, 0, strlen(kmd->password)); 10017c478bd9Sstevel@tonic-gate free(kmd->password); 10027c478bd9Sstevel@tonic-gate } 10037c478bd9Sstevel@tonic-gate 100467c90040Ssemery if (kmd->user) 10053bfb48feSsemery free(kmd->user); 10063bfb48feSsemery 100767c90040Ssemery if (kmd->env) 100867c90040Ssemery free(kmd->env); 100967c90040Ssemery 101067c90040Ssemery krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 101167c90040Ssemery (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 10127c478bd9Sstevel@tonic-gate 10137c478bd9Sstevel@tonic-gate free(kmd); 10147c478bd9Sstevel@tonic-gate } 1015