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 /* 23*70f41fc1SWill Fiveash * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <security/pam_appl.h> 287c478bd9Sstevel@tonic-gate #include <security/pam_modules.h> 297c478bd9Sstevel@tonic-gate #include <security/pam_impl.h> 307c478bd9Sstevel@tonic-gate #include <string.h> 317c478bd9Sstevel@tonic-gate #include <stdio.h> 327c478bd9Sstevel@tonic-gate #include <stdlib.h> 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <sys/stat.h> 357c478bd9Sstevel@tonic-gate #include <pwd.h> 367c478bd9Sstevel@tonic-gate #include <syslog.h> 377c478bd9Sstevel@tonic-gate #include <libintl.h> 387c478bd9Sstevel@tonic-gate #include <k5-int.h> 397c478bd9Sstevel@tonic-gate #include "profile/prof_int.h" 407c478bd9Sstevel@tonic-gate #include <netdb.h> 417c478bd9Sstevel@tonic-gate #include <ctype.h> 427c478bd9Sstevel@tonic-gate #include "utils.h" 437c478bd9Sstevel@tonic-gate #include "krb5_repository.h" 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate #define KRB5_DEFAULT_OPTIONS 0 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate int forwardable_flag = 0; 487c478bd9Sstevel@tonic-gate int renewable_flag = 0; 497c478bd9Sstevel@tonic-gate int proxiable_flag = 0; 507c478bd9Sstevel@tonic-gate int no_address_flag = 0; 517c478bd9Sstevel@tonic-gate profile_options_boolean config_option[] = { 527c478bd9Sstevel@tonic-gate { "forwardable", &forwardable_flag, 0 }, 537c478bd9Sstevel@tonic-gate { "renewable", &renewable_flag, 0 }, 547c478bd9Sstevel@tonic-gate { "proxiable", &proxiable_flag, 0 }, 557c478bd9Sstevel@tonic-gate { "no_addresses", &no_address_flag, 0 }, 567c478bd9Sstevel@tonic-gate { NULL, NULL, 0 } 577c478bd9Sstevel@tonic-gate }; 587c478bd9Sstevel@tonic-gate char *renew_timeval; 597c478bd9Sstevel@tonic-gate char *life_timeval; 607c478bd9Sstevel@tonic-gate profile_option_strings config_times[] = { 617c478bd9Sstevel@tonic-gate { "max_life", &life_timeval, 0 }, 627c478bd9Sstevel@tonic-gate { "max_renewable_life", &renew_timeval, 0 }, 637c478bd9Sstevel@tonic-gate { NULL, NULL, 0 } 647c478bd9Sstevel@tonic-gate }; 657c478bd9Sstevel@tonic-gate char *realmdef[] = { "realms", NULL, NULL, NULL }; 667c478bd9Sstevel@tonic-gate char *appdef[] = { "appdefaults", "kinit", NULL }; 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate #define krb_realm (*(realmdef + 1)) 697c478bd9Sstevel@tonic-gate 70*70f41fc1SWill Fiveash int attempt_krb5_auth(pam_handle_t *, krb5_module_data_t *, char *, 71*70f41fc1SWill Fiveash char **, boolean_t); 727c478bd9Sstevel@tonic-gate void krb5_cleanup(pam_handle_t *, void *, int); 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_boolean(); 757c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_string(); 762278144aSsemery extern int krb5_verifypw(char *, char *, int); 777c478bd9Sstevel@tonic-gate extern krb5_error_code krb5_verify_init_creds(krb5_context, 787c478bd9Sstevel@tonic-gate krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *, 797c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt *); 803125ebfcSsemery extern krb5_error_code __krb5_get_init_creds_password(krb5_context, 813125ebfcSsemery krb5_creds *, krb5_principal, char *, krb5_prompter_fct, void *, 823125ebfcSsemery krb5_deltat, char *, krb5_get_init_creds_opt *, 833125ebfcSsemery krb5_kdc_rep **); 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate /* 867c478bd9Sstevel@tonic-gate * pam_sm_authenticate - Authenticate user 877c478bd9Sstevel@tonic-gate */ 887c478bd9Sstevel@tonic-gate int 897c478bd9Sstevel@tonic-gate pam_sm_authenticate( 907c478bd9Sstevel@tonic-gate pam_handle_t *pamh, 917c478bd9Sstevel@tonic-gate int flags, 927c478bd9Sstevel@tonic-gate int argc, 937c478bd9Sstevel@tonic-gate const char **argv) 947c478bd9Sstevel@tonic-gate { 950be37caaSsemery char *user = NULL; 967c478bd9Sstevel@tonic-gate int err; 977c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR; 987c478bd9Sstevel@tonic-gate /* pam.conf options */ 997c478bd9Sstevel@tonic-gate int debug = 0; 1007c478bd9Sstevel@tonic-gate int warn = 1; 1017c478bd9Sstevel@tonic-gate /* return an error on password expire */ 1027c478bd9Sstevel@tonic-gate int err_on_exp = 0; 1037c478bd9Sstevel@tonic-gate int i; 1047c478bd9Sstevel@tonic-gate char *password = NULL; 1057c478bd9Sstevel@tonic-gate uid_t pw_uid; 1067c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = NULL; 1077c478bd9Sstevel@tonic-gate krb5_repository_data_t *krb5_data = NULL; 1087c478bd9Sstevel@tonic-gate pam_repository_t *rep_data = NULL; 109*70f41fc1SWill Fiveash boolean_t do_pkinit = FALSE; 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) { 1127c478bd9Sstevel@tonic-gate if (strcmp(argv[i], "debug") == 0) { 1137c478bd9Sstevel@tonic-gate debug = 1; 1147c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "nowarn") == 0) { 1157c478bd9Sstevel@tonic-gate warn = 0; 1167c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "err_on_exp") == 0) { 1177c478bd9Sstevel@tonic-gate err_on_exp = 1; 118*70f41fc1SWill Fiveash } else if (strcmp(argv[i], "pkinit") == 0) { 119*70f41fc1SWill Fiveash do_pkinit = TRUE; 1207c478bd9Sstevel@tonic-gate } else { 1213bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 122*70f41fc1SWill Fiveash "PAM-KRB5 (auth) unrecognized option %s", argv[i]); 1237c478bd9Sstevel@tonic-gate } 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate if (flags & PAM_SILENT) warn = 0; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate if (debug) 1283bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 1297c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_authenticate flags=%d", 1307c478bd9Sstevel@tonic-gate flags); 1317c478bd9Sstevel@tonic-gate 132*70f41fc1SWill Fiveash /* 133*70f41fc1SWill Fiveash * pam_get_data could fail if we are being called for the first time 134*70f41fc1SWill Fiveash * or if the module is not found, PAM_NO_MODULE_DATA is not an error 135*70f41fc1SWill Fiveash */ 136*70f41fc1SWill Fiveash err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd); 137*70f41fc1SWill Fiveash if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA)) 138*70f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 139*70f41fc1SWill Fiveash 140*70f41fc1SWill Fiveash /* 141*70f41fc1SWill Fiveash * If pam_krb5 was stacked higher in the auth stack and did PKINIT 142*70f41fc1SWill Fiveash * preauth sucessfully then this instance is a fallback to password 143*70f41fc1SWill Fiveash * based preauth and should just return PAM_IGNORE. 144*70f41fc1SWill Fiveash * 145*70f41fc1SWill Fiveash * The else clause is handled further down. 146*70f41fc1SWill Fiveash */ 147*70f41fc1SWill Fiveash if (kmd != NULL) { 148*70f41fc1SWill Fiveash if (++(kmd->auth_calls) > 2) { 149*70f41fc1SWill Fiveash /* 150*70f41fc1SWill Fiveash * pam_krb5 has been stacked > 2 times in the auth 151*70f41fc1SWill Fiveash * stack. Clear out the current kmd and proceed as if 152*70f41fc1SWill Fiveash * this is the first time pam_krb5 auth has been called. 153*70f41fc1SWill Fiveash */ 154*70f41fc1SWill Fiveash if (debug) { 155*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 156*70f41fc1SWill Fiveash "PAM-KRB5 (auth): stacked more than" 157*70f41fc1SWill Fiveash " two times, clearing kmd"); 158*70f41fc1SWill Fiveash } 159*70f41fc1SWill Fiveash /* clear out/free current kmd */ 160*70f41fc1SWill Fiveash err = pam_set_data(pamh, KRB5_DATA, NULL, NULL); 161*70f41fc1SWill Fiveash if (err != PAM_SUCCESS) { 162*70f41fc1SWill Fiveash krb5_cleanup(pamh, kmd, err); 163*70f41fc1SWill Fiveash result = err; 164*70f41fc1SWill Fiveash goto out; 165*70f41fc1SWill Fiveash } 166*70f41fc1SWill Fiveash kmd = NULL; 167*70f41fc1SWill Fiveash } else if (kmd->auth_calls == 2 && 168*70f41fc1SWill Fiveash kmd->auth_status == PAM_SUCCESS) { 169*70f41fc1SWill Fiveash /* 170*70f41fc1SWill Fiveash * The previous instance of pam_krb5 succeeded and this 171*70f41fc1SWill Fiveash * instance was a fall back in case it didn't succeed so 172*70f41fc1SWill Fiveash * return ignore. 173*70f41fc1SWill Fiveash */ 174*70f41fc1SWill Fiveash if (debug) { 175*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 176*70f41fc1SWill Fiveash "PAM-KRB5 (auth): PKINIT succeeded " 177*70f41fc1SWill Fiveash "earlier so returning PAM_IGNORE"); 178*70f41fc1SWill Fiveash } 179*70f41fc1SWill Fiveash return (PAM_IGNORE); 180*70f41fc1SWill Fiveash } 181*70f41fc1SWill Fiveash } 182*70f41fc1SWill Fiveash 1830be37caaSsemery (void) pam_get_item(pamh, PAM_USER, (void**) &user); 1847c478bd9Sstevel@tonic-gate 185a0e56b0eSsemery if (user == NULL || *user == '\0') { 186*70f41fc1SWill Fiveash if (do_pkinit) { 187*70f41fc1SWill Fiveash /* 188*70f41fc1SWill Fiveash * If doing PKINIT it is okay to prompt for the user 189*70f41fc1SWill Fiveash * name. 190*70f41fc1SWill Fiveash */ 191*70f41fc1SWill Fiveash if ((err = pam_get_user(pamh, &user, NULL)) != 192*70f41fc1SWill Fiveash PAM_SUCCESS) { 193*70f41fc1SWill Fiveash if (debug) { 194*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 195*70f41fc1SWill Fiveash "PAM-KRB5 (auth): get user failed: " 196*70f41fc1SWill Fiveash "%s", pam_strerror(pamh, err)); 197*70f41fc1SWill Fiveash } 198*70f41fc1SWill Fiveash return (err); 199*70f41fc1SWill Fiveash } 200*70f41fc1SWill Fiveash } else { 201*70f41fc1SWill Fiveash if (debug) 202*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 203*70f41fc1SWill Fiveash "PAM-KRB5 (auth): user empty or null"); 204*70f41fc1SWill Fiveash return (PAM_USER_UNKNOWN); 205*70f41fc1SWill Fiveash } 2067c478bd9Sstevel@tonic-gate } 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate /* make sure a password entry exists for this user */ 2090be37caaSsemery if (!get_pw_uid(user, &pw_uid)) 2100be37caaSsemery return (PAM_USER_UNKNOWN); 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate if (kmd == NULL) { 2137c478bd9Sstevel@tonic-gate kmd = calloc(1, sizeof (krb5_module_data_t)); 2147c478bd9Sstevel@tonic-gate if (kmd == NULL) { 2157c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 2167c478bd9Sstevel@tonic-gate goto out; 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup); 2207c478bd9Sstevel@tonic-gate if (err != PAM_SUCCESS) { 2217c478bd9Sstevel@tonic-gate free(kmd); 2227c478bd9Sstevel@tonic-gate result = err; 2237c478bd9Sstevel@tonic-gate goto out; 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate if (!kmd->env) { 2287c478bd9Sstevel@tonic-gate char buffer[512]; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate if (snprintf(buffer, sizeof (buffer), 2317c478bd9Sstevel@tonic-gate "%s=FILE:/tmp/krb5cc_%d", 2327c478bd9Sstevel@tonic-gate KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) { 2337c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 2347c478bd9Sstevel@tonic-gate goto out; 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate /* we MUST copy this to the heap for the putenv to work! */ 2387c478bd9Sstevel@tonic-gate kmd->env = strdup(buffer); 2397c478bd9Sstevel@tonic-gate if (!kmd->env) { 2407c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 2417c478bd9Sstevel@tonic-gate goto out; 2427c478bd9Sstevel@tonic-gate } else { 2437c478bd9Sstevel@tonic-gate if (putenv(kmd->env)) { 2447c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 2457c478bd9Sstevel@tonic-gate goto out; 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 25067c90040Ssemery if (kmd->user != NULL) 25167c90040Ssemery free(kmd->user); 2523bfb48feSsemery if ((kmd->user = strdup(user)) == NULL) { 2533bfb48feSsemery result = PAM_BUF_ERR; 2543bfb48feSsemery goto out; 2553bfb48feSsemery } 2563bfb48feSsemery 2577c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_AUTH_ERR; 2587c478bd9Sstevel@tonic-gate kmd->debug = debug; 2597c478bd9Sstevel@tonic-gate kmd->warn = warn; 2607c478bd9Sstevel@tonic-gate kmd->err_on_exp = err_on_exp; 2617c478bd9Sstevel@tonic-gate kmd->ccache = NULL; 2627c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 2637c478bd9Sstevel@tonic-gate kmd->password = NULL; 2647c478bd9Sstevel@tonic-gate kmd->age_status = PAM_SUCCESS; 2657c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 266*70f41fc1SWill Fiveash kmd->auth_calls = 1; 267*70f41fc1SWill Fiveash kmd->preauth_type = do_pkinit ? KRB_PKINIT : KRB_PASSWD; 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate /* 2707c478bd9Sstevel@tonic-gate * For apps that already did krb5 auth exchange... 2717c478bd9Sstevel@tonic-gate * Now that we've created the kmd structure, we can 2727c478bd9Sstevel@tonic-gate * return SUCCESS. 'kmd' may be needed later by other 2737c478bd9Sstevel@tonic-gate * PAM functions, thats why we wait until this point to 2747c478bd9Sstevel@tonic-gate * return. 2757c478bd9Sstevel@tonic-gate */ 2760be37caaSsemery (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data); 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate if (rep_data != NULL) { 2797c478bd9Sstevel@tonic-gate if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) { 2807c478bd9Sstevel@tonic-gate if (debug) 2813bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 2823bfb48feSsemery "PAM-KRB5 (auth): wrong" 2837c478bd9Sstevel@tonic-gate "repository found (%s), returning " 2847c478bd9Sstevel@tonic-gate "PAM_IGNORE", rep_data->type); 2857c478bd9Sstevel@tonic-gate return (PAM_IGNORE); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate if (rep_data->scope_len == sizeof (krb5_repository_data_t)) { 2887c478bd9Sstevel@tonic-gate krb5_data = (krb5_repository_data_t *)rep_data->scope; 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate if (krb5_data->flags == 2917c478bd9Sstevel@tonic-gate SUNW_PAM_KRB5_ALREADY_AUTHENTICATED && 2927c478bd9Sstevel@tonic-gate krb5_data->principal != NULL && 2937c478bd9Sstevel@tonic-gate strlen(krb5_data->principal)) { 2947c478bd9Sstevel@tonic-gate if (debug) 2953bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 2967c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Principal " 2977c478bd9Sstevel@tonic-gate "%s already authenticated", 2987c478bd9Sstevel@tonic-gate krb5_data->principal); 2997c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_SUCCESS; 3007c478bd9Sstevel@tonic-gate return (PAM_SUCCESS); 3017c478bd9Sstevel@tonic-gate } 3027c478bd9Sstevel@tonic-gate } 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate /* 3067c478bd9Sstevel@tonic-gate * if root key exists in the keytab, it's a random key so no 3077c478bd9Sstevel@tonic-gate * need to prompt for pw and we just return IGNORE. 3087c478bd9Sstevel@tonic-gate * 3097c478bd9Sstevel@tonic-gate * note we don't need to force a prompt for pw as authtok_get 3107c478bd9Sstevel@tonic-gate * is required to be stacked above this module. 3117c478bd9Sstevel@tonic-gate */ 3127c478bd9Sstevel@tonic-gate if ((strcmp(user, ROOT_UNAME) == 0) && 3137c478bd9Sstevel@tonic-gate key_in_keytab(user, debug)) { 3147c478bd9Sstevel@tonic-gate if (debug) 3153bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3167c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): " 3177c478bd9Sstevel@tonic-gate "key for '%s' in keytab, returning IGNORE", user); 3187c478bd9Sstevel@tonic-gate result = PAM_IGNORE; 3197c478bd9Sstevel@tonic-gate goto out; 3207c478bd9Sstevel@tonic-gate } 3217c478bd9Sstevel@tonic-gate 3220be37caaSsemery (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 3237c478bd9Sstevel@tonic-gate 324*70f41fc1SWill Fiveash result = attempt_krb5_auth(pamh, kmd, user, &password, 1); 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate out: 3277c478bd9Sstevel@tonic-gate if (kmd) { 3287c478bd9Sstevel@tonic-gate if (debug) 3293bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3307c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_auth finalize" 3317c478bd9Sstevel@tonic-gate " ccname env, result =%d, env ='%s'," 3327c478bd9Sstevel@tonic-gate " age = %d, status = %d", 3337c478bd9Sstevel@tonic-gate result, kmd->env ? kmd->env : "<null>", 3347c478bd9Sstevel@tonic-gate kmd->age_status, kmd->auth_status); 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate if (kmd->env && 3377c478bd9Sstevel@tonic-gate !(kmd->age_status == PAM_NEW_AUTHTOK_REQD && 3387c478bd9Sstevel@tonic-gate kmd->auth_status == PAM_SUCCESS)) { 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate if (result == PAM_SUCCESS) { 3427c478bd9Sstevel@tonic-gate /* 3437c478bd9Sstevel@tonic-gate * Put ccname into the pamh so that login 3447c478bd9Sstevel@tonic-gate * apps can pick this up when they run 3457c478bd9Sstevel@tonic-gate * pam_getenvlist(). 3467c478bd9Sstevel@tonic-gate */ 3477c478bd9Sstevel@tonic-gate if ((result = pam_putenv(pamh, kmd->env)) 3487c478bd9Sstevel@tonic-gate != PAM_SUCCESS) { 3497c478bd9Sstevel@tonic-gate /* should not happen but... */ 3503bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 3517c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth):" 3523bfb48feSsemery " pam_putenv failed: result: %d", 3537c478bd9Sstevel@tonic-gate result); 3547c478bd9Sstevel@tonic-gate goto cleanupccname; 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate } else { 3577c478bd9Sstevel@tonic-gate cleanupccname: 3587c478bd9Sstevel@tonic-gate /* for lack of a Solaris unputenv() */ 3597c478bd9Sstevel@tonic-gate krb5_unsetenv(KRB5_ENV_CCNAME); 3607c478bd9Sstevel@tonic-gate free(kmd->env); 3617c478bd9Sstevel@tonic-gate kmd->env = NULL; 3627c478bd9Sstevel@tonic-gate } 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate kmd->auth_status = result; 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate if (debug) 3683bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 3697c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result)); 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate return (result); 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate 374*70f41fc1SWill Fiveash static krb5_error_code 375*70f41fc1SWill Fiveash pam_krb5_prompter( 376*70f41fc1SWill Fiveash krb5_context ctx, 377*70f41fc1SWill Fiveash void *data, 378*70f41fc1SWill Fiveash /* ARGSUSED1 */ 379*70f41fc1SWill Fiveash const char *name, 380*70f41fc1SWill Fiveash const char *banner, 381*70f41fc1SWill Fiveash int num_prompts, 382*70f41fc1SWill Fiveash krb5_prompt prompts[]) 383*70f41fc1SWill Fiveash { 384*70f41fc1SWill Fiveash krb5_error_code rc; 385*70f41fc1SWill Fiveash pam_handle_t *pamh = (pam_handle_t *)data; 386*70f41fc1SWill Fiveash struct pam_conv *pam_convp; 387*70f41fc1SWill Fiveash struct pam_message *msgs; 388*70f41fc1SWill Fiveash struct pam_response *ret_respp; 389*70f41fc1SWill Fiveash int i; 390*70f41fc1SWill Fiveash krb5_prompt_type *prompt_type = krb5_get_prompt_types(ctx); 391*70f41fc1SWill Fiveash char tmpbuf[PAM_MAX_MSG_SIZE]; 392*70f41fc1SWill Fiveash 393*70f41fc1SWill Fiveash /* 394*70f41fc1SWill Fiveash * Because this function should never be used for password prompts, 395*70f41fc1SWill Fiveash * disallow password prompts. 396*70f41fc1SWill Fiveash */ 397*70f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 398*70f41fc1SWill Fiveash if (prompt_type[i] == KRB5_PROMPT_TYPE_PASSWORD) 399*70f41fc1SWill Fiveash return (KRB5_LIBOS_CANTREADPWD); 400*70f41fc1SWill Fiveash } 401*70f41fc1SWill Fiveash 402*70f41fc1SWill Fiveash if (num_prompts == 0) { 403*70f41fc1SWill Fiveash if (prompts) { 404*70f41fc1SWill Fiveash /* This shouldn't happen */ 405*70f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 406*70f41fc1SWill Fiveash } else { 407*70f41fc1SWill Fiveash /* no prompts so return */ 408*70f41fc1SWill Fiveash return (0); 409*70f41fc1SWill Fiveash } 410*70f41fc1SWill Fiveash } 411*70f41fc1SWill Fiveash if ((rc = pam_get_item(pamh, PAM_CONV, (void **)&pam_convp)) 412*70f41fc1SWill Fiveash != PAM_SUCCESS) { 413*70f41fc1SWill Fiveash return (rc); 414*70f41fc1SWill Fiveash } 415*70f41fc1SWill Fiveash if (pam_convp == NULL) { 416*70f41fc1SWill Fiveash return (PAM_SYSTEM_ERR); 417*70f41fc1SWill Fiveash } 418*70f41fc1SWill Fiveash 419*70f41fc1SWill Fiveash msgs = (struct pam_message *)calloc(num_prompts, 420*70f41fc1SWill Fiveash sizeof (struct pam_message)); 421*70f41fc1SWill Fiveash if (msgs == NULL) { 422*70f41fc1SWill Fiveash return (PAM_BUF_ERR); 423*70f41fc1SWill Fiveash } 424*70f41fc1SWill Fiveash (void) memset(msgs, 0, sizeof (struct pam_message) * num_prompts); 425*70f41fc1SWill Fiveash 426*70f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 427*70f41fc1SWill Fiveash /* convert krb prompt style to PAM style */ 428*70f41fc1SWill Fiveash if (prompts[i].hidden) { 429*70f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_OFF; 430*70f41fc1SWill Fiveash } else { 431*70f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_ON; 432*70f41fc1SWill Fiveash } 433*70f41fc1SWill Fiveash /* 434*70f41fc1SWill Fiveash * krb expects the prompting function to append ": " to the 435*70f41fc1SWill Fiveash * prompt string. 436*70f41fc1SWill Fiveash */ 437*70f41fc1SWill Fiveash if (snprintf(tmpbuf, sizeof (tmpbuf), "%s: ", 438*70f41fc1SWill Fiveash prompts[i].prompt) < 0) { 439*70f41fc1SWill Fiveash rc = PAM_BUF_ERR; 440*70f41fc1SWill Fiveash goto cleanup; 441*70f41fc1SWill Fiveash } 442*70f41fc1SWill Fiveash msgs[i].msg = strdup(tmpbuf); 443*70f41fc1SWill Fiveash if (msgs[i].msg == NULL) { 444*70f41fc1SWill Fiveash rc = PAM_BUF_ERR; 445*70f41fc1SWill Fiveash goto cleanup; 446*70f41fc1SWill Fiveash } 447*70f41fc1SWill Fiveash } 448*70f41fc1SWill Fiveash 449*70f41fc1SWill Fiveash /* 450*70f41fc1SWill Fiveash * Call PAM conv function to display the prompt. 451*70f41fc1SWill Fiveash */ 452*70f41fc1SWill Fiveash rc = (pam_convp->conv)(num_prompts, &msgs, &ret_respp, 453*70f41fc1SWill Fiveash pam_convp->appdata_ptr); 454*70f41fc1SWill Fiveash 455*70f41fc1SWill Fiveash if (rc == PAM_SUCCESS) { 456*70f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 457*70f41fc1SWill Fiveash /* convert PAM response to krb prompt reply format */ 458*70f41fc1SWill Fiveash prompts[i].reply->length = strlen(ret_respp[i].resp) + 459*70f41fc1SWill Fiveash 1; /* adding 1 for NULL terminator */ 460*70f41fc1SWill Fiveash prompts[i].reply->data = ret_respp[i].resp; 461*70f41fc1SWill Fiveash } 462*70f41fc1SWill Fiveash /* 463*70f41fc1SWill Fiveash * Note, just free ret_respp, not the resp data since that is 464*70f41fc1SWill Fiveash * being referenced by the krb prompt reply data pointer. 465*70f41fc1SWill Fiveash */ 466*70f41fc1SWill Fiveash free(ret_respp); 467*70f41fc1SWill Fiveash } 468*70f41fc1SWill Fiveash 469*70f41fc1SWill Fiveash cleanup: 470*70f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) { 471*70f41fc1SWill Fiveash if (msgs[i].msg) { 472*70f41fc1SWill Fiveash free(msgs[i].msg); 473*70f41fc1SWill Fiveash } 474*70f41fc1SWill Fiveash } 475*70f41fc1SWill Fiveash free(msgs); 476*70f41fc1SWill Fiveash return (rc); 477*70f41fc1SWill Fiveash } 478*70f41fc1SWill Fiveash 4797c478bd9Sstevel@tonic-gate int 4807c478bd9Sstevel@tonic-gate attempt_krb5_auth( 481*70f41fc1SWill Fiveash pam_handle_t *pamh, 4827c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd, 4837c478bd9Sstevel@tonic-gate char *user, 4847c478bd9Sstevel@tonic-gate char **krb5_pass, 4850be37caaSsemery boolean_t verify_tik) 4867c478bd9Sstevel@tonic-gate { 48767c90040Ssemery krb5_principal me = NULL, clientp = NULL; 48867c90040Ssemery krb5_principal server = NULL, serverp = NULL; 4897c478bd9Sstevel@tonic-gate krb5_creds *my_creds; 4907c478bd9Sstevel@tonic-gate krb5_timestamp now; 4917c478bd9Sstevel@tonic-gate krb5_error_code code = 0; 4927c478bd9Sstevel@tonic-gate char kuser[2*MAXHOSTNAMELEN]; 4937c478bd9Sstevel@tonic-gate krb5_deltat lifetime; 4947c478bd9Sstevel@tonic-gate krb5_deltat rlife; 4957c478bd9Sstevel@tonic-gate krb5_deltat krb5_max_duration; 4967c478bd9Sstevel@tonic-gate int options = KRB5_DEFAULT_OPTIONS; 4977c478bd9Sstevel@tonic-gate krb5_data tgtname = { 4987c478bd9Sstevel@tonic-gate 0, 4997c478bd9Sstevel@tonic-gate KRB5_TGS_NAME_SIZE, 5007c478bd9Sstevel@tonic-gate KRB5_TGS_NAME 5017c478bd9Sstevel@tonic-gate }; 5027c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt opts; 5033125ebfcSsemery krb5_kdc_rep *as_reply = NULL; 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * "result" should not be assigned PAM_SUCCESS unless 5067c478bd9Sstevel@tonic-gate * authentication has succeeded and there are no other errors. 5077c478bd9Sstevel@tonic-gate * 5087c478bd9Sstevel@tonic-gate * "code" is sometimes used for PAM codes, sometimes for krb5 5097c478bd9Sstevel@tonic-gate * codes. Be careful. 5107c478bd9Sstevel@tonic-gate */ 5117c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR; 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate if (kmd->debug) 5143bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 5157c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'", 5167c478bd9Sstevel@tonic-gate user ? user : "<null>"); 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_init(&opts); 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate /* need to free context with krb5_free_context */ 5218ce3ffdfSPeter Shoults if (code = krb5_init_secure_context(&kmd->kcontext)) { 5223bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 5237c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Error initializing " 5243bfb48feSsemery "krb5: %s", 5257c478bd9Sstevel@tonic-gate error_message(code)); 5267c478bd9Sstevel@tonic-gate return (PAM_SYSTEM_ERR); 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser, 5307c478bd9Sstevel@tonic-gate 2*MAXHOSTNAMELEN)) != 0) { 5317c478bd9Sstevel@tonic-gate /* get_kmd_kuser returns proper PAM error statuses */ 5327c478bd9Sstevel@tonic-gate return (code); 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) { 5367c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext); 5377c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 5383bfb48feSsemery return (PAM_SYSTEM_ERR); 5397c478bd9Sstevel@tonic-gate } 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate /* call krb5_free_cred_contents() on error */ 5427c478bd9Sstevel@tonic-gate my_creds = &kmd->initcreds; 5437c478bd9Sstevel@tonic-gate 5443bfb48feSsemery if ((code = 5453bfb48feSsemery krb5_copy_principal(kmd->kcontext, me, &my_creds->client))) { 5463bfb48feSsemery result = PAM_SYSTEM_ERR; 5473bfb48feSsemery goto out_err; 5483bfb48feSsemery } 54967c90040Ssemery clientp = my_creds->client; 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate if (code = krb5_build_principal_ext(kmd->kcontext, &server, 5527c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->length, 5537c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->data, 5547c478bd9Sstevel@tonic-gate tgtname.length, tgtname.data, 5557c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->length, 5567c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->data, 0)) { 5573bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 5583bfb48feSsemery "PAM-KRB5 (auth): attempt_krb5_auth: " 5593bfb48feSsemery "krb5_build_princ_ext failed: %s", 5603bfb48feSsemery error_message(code)); 5613bfb48feSsemery result = PAM_SYSTEM_ERR; 5627c478bd9Sstevel@tonic-gate goto out; 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate if (code = krb5_copy_principal(kmd->kcontext, server, 5667c478bd9Sstevel@tonic-gate &my_creds->server)) { 5673bfb48feSsemery result = PAM_SYSTEM_ERR; 5687c478bd9Sstevel@tonic-gate goto out_err; 5697c478bd9Sstevel@tonic-gate } 57067c90040Ssemery serverp = my_creds->server; 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate if (code = krb5_timeofday(kmd->kcontext, &now)) { 5733bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 5743bfb48feSsemery "PAM-KRB5 (auth): attempt_krb5_auth: " 5753bfb48feSsemery "krb5_timeofday failed: %s", 5763bfb48feSsemery error_message(code)); 5773bfb48feSsemery result = PAM_SYSTEM_ERR; 5787c478bd9Sstevel@tonic-gate goto out; 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate /* 5827c478bd9Sstevel@tonic-gate * set the values for lifetime and rlife to be the maximum 5837c478bd9Sstevel@tonic-gate * possible 5847c478bd9Sstevel@tonic-gate */ 5857c478bd9Sstevel@tonic-gate krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 5867c478bd9Sstevel@tonic-gate lifetime = krb5_max_duration; 5877c478bd9Sstevel@tonic-gate rlife = krb5_max_duration; 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate /* 5907c478bd9Sstevel@tonic-gate * Let us get the values for various options 5917c478bd9Sstevel@tonic-gate * from Kerberos configuration file 5927c478bd9Sstevel@tonic-gate */ 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate krb_realm = krb5_princ_realm(kmd->kcontext, me)->data; 5957c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile, 5967c478bd9Sstevel@tonic-gate realmdef, config_option); 5977c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile, 5987c478bd9Sstevel@tonic-gate appdef, config_option); 5997c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile, 6007c478bd9Sstevel@tonic-gate realmdef, config_times); 6017c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile, 6027c478bd9Sstevel@tonic-gate appdef, config_times); 6037c478bd9Sstevel@tonic-gate 6047c478bd9Sstevel@tonic-gate if (renew_timeval) { 6057c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(renew_timeval, &rlife); 6067c478bd9Sstevel@tonic-gate if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 6073bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 6087c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Bad max_renewable_life " 6093bfb48feSsemery " value '%s' in Kerberos config file", 6107c478bd9Sstevel@tonic-gate renew_timeval); 6117c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 6127c478bd9Sstevel@tonic-gate goto out; 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate if (life_timeval) { 6167c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(life_timeval, &lifetime); 6177c478bd9Sstevel@tonic-gate if (code != 0 || lifetime == 0 || 6187c478bd9Sstevel@tonic-gate lifetime > krb5_max_duration) { 6193bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 6203bfb48feSsemery "lifetime value '%s' in Kerberos config file", 6217c478bd9Sstevel@tonic-gate life_timeval); 6227c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 6237c478bd9Sstevel@tonic-gate goto out; 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate } 6267c478bd9Sstevel@tonic-gate /* start timer when request gets to KDC */ 6277c478bd9Sstevel@tonic-gate my_creds->times.starttime = 0; 6287c478bd9Sstevel@tonic-gate my_creds->times.endtime = now + lifetime; 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate if (options & KDC_OPT_RENEWABLE) { 6317c478bd9Sstevel@tonic-gate my_creds->times.renew_till = now + rlife; 6327c478bd9Sstevel@tonic-gate } else 6337c478bd9Sstevel@tonic-gate my_creds->times.renew_till = 0; 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime); 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate if (proxiable_flag) { /* Set in config file */ 6387c478bd9Sstevel@tonic-gate if (kmd->debug) 6393bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 6407c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Proxiable tickets " 6413bfb48feSsemery "requested"); 6427c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_set_proxiable(&opts, TRUE); 6437c478bd9Sstevel@tonic-gate } 6447c478bd9Sstevel@tonic-gate if (forwardable_flag) { 6457c478bd9Sstevel@tonic-gate if (kmd->debug) 6463bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 6477c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Forwardable tickets " 6483bfb48feSsemery "requested"); 6497c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_set_forwardable(&opts, TRUE); 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate if (renewable_flag) { 6527c478bd9Sstevel@tonic-gate if (kmd->debug) 6533bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 6547c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Renewable tickets " 6553bfb48feSsemery "requested"); 6567c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_set_renew_life(&opts, rlife); 6577c478bd9Sstevel@tonic-gate } 6587c478bd9Sstevel@tonic-gate if (no_address_flag) { 6597c478bd9Sstevel@tonic-gate if (kmd->debug) 6603bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 6617c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Addressless tickets " 6623bfb48feSsemery "requested"); 6637c478bd9Sstevel@tonic-gate krb5_get_init_creds_opt_set_address_list(&opts, NULL); 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate /* 667*70f41fc1SWill Fiveash * mech_krb5 interprets empty passwords as NULL passwords and tries to 668*70f41fc1SWill Fiveash * read a password from stdin. Since we are in pam this is bad and 669*70f41fc1SWill Fiveash * should not be allowed. 670*70f41fc1SWill Fiveash * 671*70f41fc1SWill Fiveash * Note, the logic now is that if the preauth_type is PKINIT then 672*70f41fc1SWill Fiveash * provide a proper PAMcentric prompt function that the underlying 673*70f41fc1SWill Fiveash * PKINIT preauth plugin will use to prompt for the PIN. 6747c478bd9Sstevel@tonic-gate */ 675*70f41fc1SWill Fiveash if (kmd->preauth_type == KRB_PKINIT) { 676*70f41fc1SWill Fiveash /* 677*70f41fc1SWill Fiveash * Do PKINIT preauth 678*70f41fc1SWill Fiveash * 679*70f41fc1SWill Fiveash * Note: we want to limit preauth types to just those for PKINIT 680*70f41fc1SWill Fiveash * but krb5_get_init_creds() doesn't support that at this point. 681*70f41fc1SWill Fiveash * Instead we rely on pam_krb5_prompter() to limit prompts to 682*70f41fc1SWill Fiveash * non-password types. So all we can do here is set the preauth 683*70f41fc1SWill Fiveash * list so krb5_get_init_creds() will try that first. 684*70f41fc1SWill Fiveash */ 685*70f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = { 686*70f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ, 687*70f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ_OLD 688*70f41fc1SWill Fiveash }; 689*70f41fc1SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(&opts, pk_pa_list, 2); 690*70f41fc1SWill Fiveash 691*70f41fc1SWill Fiveash if (*krb5_pass == NULL) { 692*70f41fc1SWill Fiveash /* let preauth plugin prompt for PIN */ 693*70f41fc1SWill Fiveash code = __krb5_get_init_creds_password(kmd->kcontext, 694*70f41fc1SWill Fiveash my_creds, 695*70f41fc1SWill Fiveash me, 696*70f41fc1SWill Fiveash NULL, /* clear text passwd */ 697*70f41fc1SWill Fiveash pam_krb5_prompter, /* prompter */ 698*70f41fc1SWill Fiveash pamh, /* prompter data */ 699*70f41fc1SWill Fiveash 0, /* start time */ 700*70f41fc1SWill Fiveash NULL, /* defaults to krbtgt@REALM */ 701*70f41fc1SWill Fiveash &opts, 702*70f41fc1SWill Fiveash &as_reply); 703*70f41fc1SWill Fiveash } else { 704*70f41fc1SWill Fiveash /* 705*70f41fc1SWill Fiveash * krb pkinit does not support setting the PIN so we 706*70f41fc1SWill Fiveash * punt on trying to use krb5_pass as the PIN for now. 707*70f41fc1SWill Fiveash * Note that once this is supported by pkinit the code 708*70f41fc1SWill Fiveash * should make sure krb5_pass isn't empty and if it is 709*70f41fc1SWill Fiveash * then that's an error. 710*70f41fc1SWill Fiveash */ 711*70f41fc1SWill Fiveash code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 712*70f41fc1SWill Fiveash } 7137c478bd9Sstevel@tonic-gate } else { 7143125ebfcSsemery /* 715*70f41fc1SWill Fiveash * Do password based preauths 716*70f41fc1SWill Fiveash * 717*70f41fc1SWill Fiveash * See earlier PKINIT comment. We are doing something similar 718*70f41fc1SWill Fiveash * here but we do not pass in a prompter (we assume 719*70f41fc1SWill Fiveash * pam_authtok_get has already prompted for that). 7203125ebfcSsemery */ 721*70f41fc1SWill Fiveash if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) { 722*70f41fc1SWill Fiveash code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 723*70f41fc1SWill Fiveash } else { 724*70f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = { 725*70f41fc1SWill Fiveash KRB5_PADATA_ENC_TIMESTAMP 726*70f41fc1SWill Fiveash }; 727*70f41fc1SWill Fiveash 728*70f41fc1SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(&opts, 729*70f41fc1SWill Fiveash pk_pa_list, 1); 730*70f41fc1SWill Fiveash 731*70f41fc1SWill Fiveash /* 732*70f41fc1SWill Fiveash * We call our own private version of gic_pwd, because 733*70f41fc1SWill Fiveash * we need more information, such as password/account 734*70f41fc1SWill Fiveash * expiration, that is found in the as_reply. The 735*70f41fc1SWill Fiveash * "prompter" interface is not granular enough for PAM 736*70f41fc1SWill Fiveash * to make use of. 737*70f41fc1SWill Fiveash */ 738*70f41fc1SWill Fiveash code = __krb5_get_init_creds_password(kmd->kcontext, 739*70f41fc1SWill Fiveash my_creds, 740*70f41fc1SWill Fiveash me, 741*70f41fc1SWill Fiveash *krb5_pass, /* clear text passwd */ 742*70f41fc1SWill Fiveash NULL, /* prompter */ 743*70f41fc1SWill Fiveash NULL, /* data */ 744*70f41fc1SWill Fiveash 0, /* start time */ 745*70f41fc1SWill Fiveash NULL, /* defaults to krbtgt@REALM */ 746*70f41fc1SWill Fiveash &opts, 747*70f41fc1SWill Fiveash &as_reply); 748*70f41fc1SWill Fiveash } 7497c478bd9Sstevel@tonic-gate } 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate if (kmd->debug) 7523bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 7537c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: " 7547c478bd9Sstevel@tonic-gate "krb5_get_init_creds_password returns: %s", 7557c478bd9Sstevel@tonic-gate code == 0 ? "SUCCESS" : error_message(code)); 7567c478bd9Sstevel@tonic-gate 7577c478bd9Sstevel@tonic-gate switch (code) { 7587c478bd9Sstevel@tonic-gate case 0: 7597c478bd9Sstevel@tonic-gate /* got a tgt, let's verify it */ 7607c478bd9Sstevel@tonic-gate if (verify_tik) { 7617c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt vopts; 7627c478bd9Sstevel@tonic-gate 763a0709436Smp krb5_principal sp = NULL; 764a0709436Smp char kt_name[MAX_KEYTAB_NAME_LEN]; 765a0709436Smp char *fqdn; 766a0709436Smp 7677c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt_init(&vopts); 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate code = krb5_verify_init_creds(kmd->kcontext, 7707c478bd9Sstevel@tonic-gate my_creds, 7717c478bd9Sstevel@tonic-gate NULL, /* defaults to host/localhost@REALM */ 7727c478bd9Sstevel@tonic-gate NULL, 7737c478bd9Sstevel@tonic-gate NULL, 7747c478bd9Sstevel@tonic-gate &vopts); 7757c478bd9Sstevel@tonic-gate 7767c478bd9Sstevel@tonic-gate if (code) { 7777c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 7787c478bd9Sstevel@tonic-gate 7790be37caaSsemery /* 7800be37caaSsemery * Give a better error message when the 7810be37caaSsemery * keytable entry isn't found or the keytab 7820be37caaSsemery * file cannot be found. 7830be37caaSsemery */ 784a0709436Smp if (krb5_sname_to_principal(kmd->kcontext, NULL, 785a0709436Smp NULL, KRB5_NT_SRV_HST, &sp)) 786a0709436Smp fqdn = "<fqdn>"; 787a0709436Smp else 788a0709436Smp fqdn = sp->data[1].data; 789a0709436Smp 790a0709436Smp if (krb5_kt_default_name(kmd->kcontext, kt_name, 791a0709436Smp sizeof (kt_name))) 792a0709436Smp (void) strncpy(kt_name, 793a0709436Smp "default keytab", 794a0709436Smp sizeof (kt_name)); 795a0709436Smp 796a0709436Smp switch (code) { 797a0709436Smp case KRB5_KT_NOTFOUND: 7983bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 799a0709436Smp "PAM-KRB5 (auth): " 800a0709436Smp "krb5_verify_init_creds failed:" 801a0709436Smp " Key table entry \"host/%s\"" 8023bfb48feSsemery " not found in %s", 803a0709436Smp fqdn, kt_name); 804a0709436Smp break; 805a0709436Smp case ENOENT: 8063bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 807a0709436Smp "PAM-KRB5 (auth): " 808a0709436Smp "krb5_verify_init_creds failed:" 809a0709436Smp " Keytab file \"%s\"" 8103bfb48feSsemery " does not exist.\n", 811a0709436Smp kt_name); 812a0709436Smp break; 813a0709436Smp default: 8143bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR, 815a0709436Smp "PAM-KRB5 (auth): " 816a0709436Smp "krb5_verify_init_creds failed:" 8173bfb48feSsemery " %s", 818a0709436Smp error_message(code)); 819a0709436Smp break; 820a0709436Smp } 821a0709436Smp 822a0709436Smp if (sp) 823a0709436Smp krb5_free_principal(kmd->kcontext, sp); 8247c478bd9Sstevel@tonic-gate } 8257c478bd9Sstevel@tonic-gate } 8263125ebfcSsemery 8273125ebfcSsemery if (code == 0) 8283125ebfcSsemery kmd->expiration = as_reply->enc_part2->key_exp; 8293125ebfcSsemery 8307c478bd9Sstevel@tonic-gate break; 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 8337c478bd9Sstevel@tonic-gate /* 8347c478bd9Sstevel@tonic-gate * Since this principal is not part of the local 8357c478bd9Sstevel@tonic-gate * Kerberos realm, we just return PAM_USER_UNKNOWN. 8367c478bd9Sstevel@tonic-gate */ 8377c478bd9Sstevel@tonic-gate result = PAM_USER_UNKNOWN; 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate if (kmd->debug) 8403bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 8413bfb48feSsemery "PAM-KRB5 (auth): attempt_krb5_auth:" 8427c478bd9Sstevel@tonic-gate " User is not part of the local Kerberos" 8437c478bd9Sstevel@tonic-gate " realm: %s", error_message(code)); 8447c478bd9Sstevel@tonic-gate break; 8457c478bd9Sstevel@tonic-gate 8467c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_PREAUTH_FAILED: 8477c478bd9Sstevel@tonic-gate case KRB5KRB_AP_ERR_BAD_INTEGRITY: 8487c478bd9Sstevel@tonic-gate /* 8497c478bd9Sstevel@tonic-gate * We could be trying the password from a previous 8507c478bd9Sstevel@tonic-gate * pam authentication module, but we don't want to 8517c478bd9Sstevel@tonic-gate * generate an error if the unix password is different 8527c478bd9Sstevel@tonic-gate * than the Kerberos password... 8537c478bd9Sstevel@tonic-gate */ 8547c478bd9Sstevel@tonic-gate break; 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_KEY_EXP: 8577c478bd9Sstevel@tonic-gate if (!kmd->err_on_exp) { 8587c478bd9Sstevel@tonic-gate /* 859*70f41fc1SWill Fiveash * Request a tik for changepw service and it will tell 860*70f41fc1SWill Fiveash * us if pw is good or not. If PKINIT is being done it 861*70f41fc1SWill Fiveash * is possible that *krb5_pass may be NULL so check for 862*70f41fc1SWill Fiveash * that. If that is the case this function will return 863*70f41fc1SWill Fiveash * an error. 8647c478bd9Sstevel@tonic-gate */ 865*70f41fc1SWill Fiveash if (*krb5_pass != NULL) { 866*70f41fc1SWill Fiveash code = krb5_verifypw(kuser, *krb5_pass, 867*70f41fc1SWill Fiveash kmd->debug); 868*70f41fc1SWill Fiveash if (kmd->debug) { 869*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG, 870*70f41fc1SWill Fiveash "PAM-KRB5 (auth): " 871*70f41fc1SWill Fiveash "attempt_krb5_auth: " 872*70f41fc1SWill Fiveash "verifypw %d", code); 873*70f41fc1SWill Fiveash } 874*70f41fc1SWill Fiveash if (code == 0) { 875*70f41fc1SWill Fiveash /* 876*70f41fc1SWill Fiveash * pw is good, set age status for 877*70f41fc1SWill Fiveash * acct_mgmt. 878*70f41fc1SWill Fiveash */ 879*70f41fc1SWill Fiveash kmd->age_status = PAM_NEW_AUTHTOK_REQD; 880*70f41fc1SWill Fiveash } 8817c478bd9Sstevel@tonic-gate } 882*70f41fc1SWill Fiveash 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate break; 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate default: 8877c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR; 8887c478bd9Sstevel@tonic-gate if (kmd->debug) 8893bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 8903bfb48feSsemery "PAM-KRB5 (auth): error %d - %s", 8917c478bd9Sstevel@tonic-gate code, error_message(code)); 8927c478bd9Sstevel@tonic-gate break; 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate if (code == 0) { 8967c478bd9Sstevel@tonic-gate /* 897*70f41fc1SWill Fiveash * success for the entered pw or PKINIT succeeded. 8987c478bd9Sstevel@tonic-gate * 8997c478bd9Sstevel@tonic-gate * we can't rely on the pw in PAM_AUTHTOK 9007c478bd9Sstevel@tonic-gate * to be the (correct) krb5 one so 9017c478bd9Sstevel@tonic-gate * store krb5 pw in module data for 902*70f41fc1SWill Fiveash * use in acct_mgmt. Note that *krb5_pass may be NULL if we're 903*70f41fc1SWill Fiveash * doing PKINIT. 9047c478bd9Sstevel@tonic-gate */ 905*70f41fc1SWill Fiveash if (*krb5_pass != NULL && 906*70f41fc1SWill Fiveash !(kmd->password = strdup(*krb5_pass))) { 907*70f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_ERR, 908*70f41fc1SWill Fiveash "Cannot strdup password"); 9097c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR; 9107c478bd9Sstevel@tonic-gate goto out_err; 9117c478bd9Sstevel@tonic-gate } 912*70f41fc1SWill Fiveash 9137c478bd9Sstevel@tonic-gate result = PAM_SUCCESS; 9147c478bd9Sstevel@tonic-gate goto out; 9157c478bd9Sstevel@tonic-gate } 9167c478bd9Sstevel@tonic-gate 9177c478bd9Sstevel@tonic-gate out_err: 9187c478bd9Sstevel@tonic-gate /* jump (or reach) here if error and cred cache has been init */ 9197c478bd9Sstevel@tonic-gate 9207c478bd9Sstevel@tonic-gate if (kmd->debug) 9213bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 9227c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): clearing initcreds in " 9237c478bd9Sstevel@tonic-gate "pam_authenticate()"); 9247c478bd9Sstevel@tonic-gate 9257c478bd9Sstevel@tonic-gate krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 9267c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 9277c478bd9Sstevel@tonic-gate 9287c478bd9Sstevel@tonic-gate out: 9297c478bd9Sstevel@tonic-gate if (server) 9307c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, server); 9317c478bd9Sstevel@tonic-gate if (me) 9327c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, me); 9333125ebfcSsemery if (as_reply) 9343125ebfcSsemery krb5_free_kdc_rep(kmd->kcontext, as_reply); 93567c90040Ssemery 93667c90040Ssemery /* 93767c90040Ssemery * clientp or serverp could be NULL in certain error cases in this 93867c90040Ssemery * function. mycreds->[client|server] could also be NULL in case 93967c90040Ssemery * of error in this function, see out_err above. The pointers clientp 94067c90040Ssemery * and serverp reference the input argument in my_creds for 94167c90040Ssemery * get_init_creds and must be freed if the input argument does not 94267c90040Ssemery * match the output argument, which occurs during a successful call 94367c90040Ssemery * to get_init_creds. 94467c90040Ssemery */ 94567c90040Ssemery if (clientp && my_creds->client && clientp != my_creds->client) 94667c90040Ssemery krb5_free_principal(kmd->kcontext, clientp); 94767c90040Ssemery if (serverp && my_creds->server && serverp != my_creds->server) 94867c90040Ssemery krb5_free_principal(kmd->kcontext, serverp); 94967c90040Ssemery 9507c478bd9Sstevel@tonic-gate if (kmd->kcontext) { 9517c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext); 9527c478bd9Sstevel@tonic-gate kmd->kcontext = NULL; 9537c478bd9Sstevel@tonic-gate } 9547c478bd9Sstevel@tonic-gate 9557c478bd9Sstevel@tonic-gate if (kmd->debug) 9563bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 9577c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth returning %d", 9587c478bd9Sstevel@tonic-gate result); 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate return (kmd->auth_status = result); 9617c478bd9Sstevel@tonic-gate } 9627c478bd9Sstevel@tonic-gate 9637c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 9647c478bd9Sstevel@tonic-gate void 9657c478bd9Sstevel@tonic-gate krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status) 9667c478bd9Sstevel@tonic-gate { 9677c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = (krb5_module_data_t *)data; 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate if (kmd == NULL) 9707c478bd9Sstevel@tonic-gate return; 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate if (kmd->debug) { 9733bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG, 9743bfb48feSsemery "PAM-KRB5 (auth): krb5_cleanup auth_status = %d", 9757c478bd9Sstevel@tonic-gate kmd->auth_status); 9767c478bd9Sstevel@tonic-gate } 9777c478bd9Sstevel@tonic-gate 9787c478bd9Sstevel@tonic-gate /* 97967c90040Ssemery * Apps could be calling pam_end here, so we should always clean 98067c90040Ssemery * up regardless of success or failure here. 9817c478bd9Sstevel@tonic-gate */ 98267c90040Ssemery if (kmd->ccache) 9835ad42b1bSSurya Prakki (void) krb5_cc_close(kmd->kcontext, kmd->ccache); 9847c478bd9Sstevel@tonic-gate 9857c478bd9Sstevel@tonic-gate if (kmd->password) { 9867c478bd9Sstevel@tonic-gate (void) memset(kmd->password, 0, strlen(kmd->password)); 9877c478bd9Sstevel@tonic-gate free(kmd->password); 9887c478bd9Sstevel@tonic-gate } 9897c478bd9Sstevel@tonic-gate 99067c90040Ssemery if (kmd->user) 9913bfb48feSsemery free(kmd->user); 9923bfb48feSsemery 99367c90040Ssemery if (kmd->env) 99467c90040Ssemery free(kmd->env); 99567c90040Ssemery 99667c90040Ssemery krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 99767c90040Ssemery (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate free(kmd); 10007c478bd9Sstevel@tonic-gate } 1001