1 /* 2 * Tests for the pam-krb5 module with an expired password. 3 * 4 * This test case checks correct handling of an account whose password has 5 * expired and the multiple different paths the module can take for handling 6 * that case. 7 * 8 * Written by Russ Allbery <eagle@eyrie.org> 9 * Copyright 2020 Russ Allbery <eagle@eyrie.org> 10 * Copyright 2011-2012 11 * The Board of Trustees of the Leland Stanford Junior University 12 * 13 * SPDX-License-Identifier: BSD-3-clause or GPL-1+ 14 */ 15 16 #include <config.h> 17 #include <portable/system.h> 18 19 #include <pwd.h> 20 #include <time.h> 21 22 #include <tests/fakepam/pam.h> 23 #include <tests/fakepam/script.h> 24 #include <tests/tap/basic.h> 25 #include <tests/tap/kadmin.h> 26 #include <tests/tap/kerberos.h> 27 #include <tests/tap/process.h> 28 #include <tests/tap/string.h> 29 30 31 int 32 main(void) 33 { 34 struct script_config config; 35 struct kerberos_config *krbconf; 36 char *newpass, *date; 37 struct passwd pwd; 38 time_t now; 39 40 /* Load the Kerberos principal and password from a file. */ 41 krbconf = kerberos_setup(TAP_KRB_NEEDS_PASSWORD); 42 memset(&config, 0, sizeof(config)); 43 config.user = krbconf->username; 44 config.password = krbconf->password; 45 config.extra[0] = krbconf->userprinc; 46 47 /* 48 * Ensure we can expire the password. Heimdal has a prompt for the 49 * expiration time, so save that to use as a substitution in the script. 50 */ 51 now = time(NULL) - 1; 52 if (!kerberos_expire_password(krbconf->userprinc, now)) 53 skip_all("kadmin not configured or kadmin mismatch"); 54 date = bstrdup(ctime(&now)); 55 date[strlen(date) - 1] = '\0'; 56 config.extra[1] = date; 57 58 /* Generate a testing krb5.conf file. */ 59 kerberos_generate_conf(krbconf->realm); 60 61 /* Create a fake passwd struct for our user. */ 62 memset(&pwd, 0, sizeof(pwd)); 63 pwd.pw_name = krbconf->username; 64 pwd.pw_uid = getuid(); 65 pwd.pw_gid = getgid(); 66 basprintf(&pwd.pw_dir, "%s/tmp", getenv("BUILD")); 67 pam_set_pwd(&pwd); 68 69 /* 70 * We'll be changing the password to something new. This needs to be 71 * sufficiently random that it's unlikely to fall afoul of password 72 * strength checking. 73 */ 74 basprintf(&newpass, "ngh1,a%lu nn9af6", (unsigned long) getpid()); 75 config.newpass = newpass; 76 77 plan_lazy(); 78 79 /* 80 * Default behavior. We have to distinguish between two versions of 81 * Heimdal for testing because the prompts changed substantially. Use the 82 * existence of krb5_principal_set_comp_string to distinguish because it 83 * was introduced at the same time. 84 */ 85 #ifdef HAVE_KRB5_HEIMDAL 86 # ifdef HAVE_KRB5_PRINCIPAL_SET_COMP_STRING 87 run_script("data/scripts/expired/basic-heimdal", &config); 88 config.newpass = krbconf->password; 89 config.password = newpass; 90 kerberos_expire_password(krbconf->userprinc, now); 91 run_script("data/scripts/expired/basic-heimdal-debug", &config); 92 # else 93 run_script("data/scripts/expired/basic-heimdal-old", &config); 94 config.newpass = krbconf->password; 95 config.password = newpass; 96 kerberos_expire_password(krbconf->userprinc, now); 97 run_script("data/scripts/expired/basic-heimdal-old-debug", &config); 98 # endif 99 #else 100 run_script("data/scripts/expired/basic-mit", &config); 101 config.newpass = krbconf->password; 102 config.password = newpass; 103 kerberos_expire_password(krbconf->userprinc, now); 104 run_script("data/scripts/expired/basic-mit-debug", &config); 105 #endif 106 107 /* Test again with PAM_SILENT, specified two ways. */ 108 #ifdef HAVE_KRB5_HEIMDAL 109 config.newpass = newpass; 110 config.password = krbconf->password; 111 kerberos_expire_password(krbconf->userprinc, now); 112 run_script("data/scripts/expired/basic-heimdal-silent", &config); 113 config.newpass = krbconf->password; 114 config.password = newpass; 115 kerberos_expire_password(krbconf->userprinc, now); 116 run_script("data/scripts/expired/basic-heimdal-flag-silent", &config); 117 #else 118 config.newpass = newpass; 119 config.password = krbconf->password; 120 kerberos_expire_password(krbconf->userprinc, now); 121 run_script("data/scripts/expired/basic-mit-silent", &config); 122 config.newpass = krbconf->password; 123 config.password = newpass; 124 kerberos_expire_password(krbconf->userprinc, now); 125 run_script("data/scripts/expired/basic-mit-flag-silent", &config); 126 #endif 127 128 /* 129 * We can only run the remaining checks if we can suppress the Kerberos 130 * library behavior of prompting for a new password when the password has 131 * expired. 132 */ 133 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT 134 135 /* Check the forced failure behavior. */ 136 run_script("data/scripts/expired/fail", &config); 137 run_script("data/scripts/expired/fail-debug", &config); 138 139 /* 140 * Defer the error to the account management check. 141 * 142 * Skip this check on Heimdal currently (Heimdal 7.4.0) because its 143 * implementation of krb5_get_init_creds_opt_set_change_password_prompt is 144 * incomplete. See <https://github.com/heimdal/heimdal/issues/322>. 145 */ 146 # ifdef HAVE_KRB5_HEIMDAL 147 skip_block(2, "deferring password changes broken in Heimdal"); 148 # else 149 config.newpass = newpass; 150 config.password = krbconf->password; 151 config.authtok = krbconf->password; 152 kerberos_expire_password(krbconf->userprinc, now); 153 run_script("data/scripts/expired/defer-mit", &config); 154 config.newpass = krbconf->password; 155 config.password = newpass; 156 config.authtok = newpass; 157 kerberos_expire_password(krbconf->userprinc, now); 158 run_script("data/scripts/expired/defer-mit-debug", &config); 159 # endif 160 161 #else /* !HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT */ 162 163 /* Mention that we skipped something for the record. */ 164 skip_block(4, "cannot disable library password prompting"); 165 166 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT */ 167 168 /* In case we ran into some error, try to unexpire the password. */ 169 kerberos_expire_password(krbconf->userprinc, 0); 170 171 free(date); 172 free(newpass); 173 free(pwd.pw_dir); 174 return 0; 175 } 176