1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <kadm5/admin.h> 30 #include <krb5.h> 31 #include <security/pam_appl.h> 32 #include <security/pam_modules.h> 33 #include <security/pam_impl.h> 34 #include <string.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <pwd.h> 38 #include <syslog.h> 39 #include <libintl.h> 40 41 #define KRB5_AUTOMIGRATE_DATA "SUNW-KRB5-AUTOMIGRATE-DATA" 42 43 static void krb5_migrate_cleanup(pam_handle_t *pamh, void *data, 44 int pam_status); 45 46 /* 47 * pam_sm_authenticate - Authenticate a host-based client service 48 * principal to kadmind in order to permit the creation of a new user 49 * principal in the client's default realm. 50 */ 51 int pam_sm_authenticate(pam_handle_t *pamh, int flags, 52 int argc, const char **argv) 53 { 54 char *user = NULL; 55 char *userdata = NULL; 56 char *password = NULL; 57 int err, i; 58 time_t now; 59 60 /* pam.conf options */ 61 int debug = 0; 62 int quiet = 0; 63 int expire_pw = 0; 64 char *service = NULL; 65 66 /* krb5-specific defines */ 67 kadm5_ret_t retval = 0; 68 krb5_context context = NULL; 69 kadm5_config_params params; 70 krb5_principal svcprinc; 71 char *svcprincstr = NULL; 72 krb5_principal userprinc; 73 char *userprincstr = NULL; 74 int strlength = 0; 75 kadm5_principal_ent_rec kadm5_userprinc; 76 char *kadmin_princ = NULL; 77 char *def_realm = NULL; 78 void *handle = NULL; 79 long mask = 0; 80 81 for (i = 0; i < argc; i++) { 82 if (strcmp(argv[i], "debug") == 0) { 83 debug = 1; 84 } else if (strcmp(argv[i], "quiet") == 0) { 85 quiet = 1; 86 } else if (strcmp(argv[i], "expire_pw") == 0) { 87 expire_pw = 1; 88 } else if ((strstr(argv[i], "client_service=") != NULL) && 89 (strcmp((strstr(argv[i], "=") + 1), "") != 0)) { 90 service = (char *)strdup(strstr(argv[i], "=") + 1); 91 } else { 92 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 93 "PAM-KRB5-AUTOMIGRATE (auth): unrecognized " 94 "option %s"), 95 argv[i]); 96 } 97 } 98 99 if (flags & PAM_SILENT) 100 quiet = 1; 101 102 err = pam_get_item(pamh, PAM_USER, (void**)&user); 103 if (err != PAM_SUCCESS) { 104 goto cleanup; 105 } 106 107 /* 108 * Check if user name is *not* NULL 109 */ 110 if (user == NULL || (user[0] == '\0')) { 111 if (debug) 112 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 113 "PAM-KRB5-AUTOMIGRATE (auth): " 114 "user empty or null")); 115 goto cleanup; 116 } 117 118 /* 119 * Grok the user password 120 */ 121 err = pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 122 if (err != PAM_SUCCESS) { 123 goto cleanup; 124 } 125 126 if (password == NULL || (password[0] == '\0')) { 127 if (debug) 128 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 129 "PAM-KRB5-AUTOMIGRATE (auth): " 130 "authentication token is empty or null")); 131 goto cleanup; 132 } 133 134 135 /* 136 * Now, lets do the all krb5/kadm5 setup for the principal addition 137 */ 138 if (retval = krb5_init_context(&context)) { 139 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 140 "PAM-KRB5-AUTOMIGRATE (auth): Error initializing " 141 "krb5: %s"), 142 error_message(retval)); 143 goto cleanup; 144 } 145 146 (void) memset((char *)¶ms, 0, sizeof (params)); 147 (void) memset(&kadm5_userprinc, 0, sizeof (kadm5_userprinc)); 148 149 if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { 150 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 151 "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining " 152 "default krb5 realm")); 153 goto cleanup; 154 } 155 156 params.mask |= KADM5_CONFIG_REALM; 157 params.realm = def_realm; 158 159 if (kadm5_get_adm_host_srv_name(context, def_realm, 160 &kadmin_princ)) { 161 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 162 "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining " 163 "host based service name for realm %s\n"), def_realm); 164 goto cleanup; 165 } 166 167 if (retval = krb5_sname_to_principal(context, NULL, 168 (service != NULL)?service:"host", 169 KRB5_NT_SRV_HST, 170 &svcprinc)) { 171 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 172 "PAM-KRB5-AUTOMIGRATE (auth): Error while creating " 173 "krb5 host service principal: %s"), 174 error_message(retval)); 175 goto cleanup; 176 } 177 178 if (retval = krb5_unparse_name(context, svcprinc, 179 &svcprincstr)) { 180 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 181 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 182 "unparsing principal name: %s"), 183 error_message(retval)); 184 krb5_free_principal(context, svcprinc); 185 goto cleanup; 186 } 187 188 krb5_free_principal(context, svcprinc); 189 190 /* 191 * Initialize the kadm5 connection using the default keytab 192 */ 193 retval = kadm5_init_with_skey(svcprincstr, NULL, 194 kadmin_princ, 195 ¶ms, 196 KADM5_STRUCT_VERSION, 197 KADM5_API_VERSION_2, 198 &handle); 199 if (retval) { 200 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 201 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 202 "doing kadm5_init_with_skey: %s"), 203 error_message(retval)); 204 goto cleanup; 205 } 206 207 208 /* 209 * The RPCSEC_GSS connection has been established; Lets check to see 210 * if the corresponding user principal exists in the KDC database. 211 * If not, lets create a new one. 212 */ 213 214 strlength = strlen(user) + strlen(def_realm) + 2; 215 userprincstr = (char *)malloc(strlength); 216 (void) strlcpy(userprincstr, user, strlength); 217 (void) strlcat(userprincstr, "@", strlength); 218 (void) strlcat(userprincstr, def_realm, strlength); 219 220 221 if (retval = krb5_parse_name(context, userprincstr, 222 &userprinc)) { 223 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 224 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 225 "parsing user principal name: %s"), 226 error_message(retval)); 227 goto cleanup; 228 } 229 230 retval = kadm5_get_principal(handle, userprinc, &kadm5_userprinc, 231 KADM5_PRINCIPAL_NORMAL_MASK); 232 233 krb5_free_principal(context, userprinc); 234 235 if (retval) { 236 switch (retval) { 237 case KADM5_AUTH_GET: 238 if (debug) 239 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 240 "PAM-KRB5-AUTOMIGRATE (auth): %s does " 241 "not have the GET privilege " 242 "for kadm5_get_principal: %s"), 243 svcprincstr, error_message(retval)); 244 break; 245 246 case KADM5_UNK_PRINC: 247 default: 248 break; 249 } 250 /* 251 * We will try & add this principal anyways, continue on ... 252 */ 253 (void) memset(&kadm5_userprinc, 0, sizeof (kadm5_userprinc)); 254 } else { 255 /* 256 * Principal already exists in the KDC database, quit now 257 */ 258 if (debug) 259 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 260 "PAM-KRB5-AUTOMIGRATE (auth): Principal %s " 261 "already exists in Kerberos KDC database"), 262 userprincstr); 263 goto cleanup; 264 } 265 266 267 268 if (retval = krb5_parse_name(context, userprincstr, 269 &(kadm5_userprinc.principal))) { 270 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 271 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 272 "parsing user principal name: %s"), 273 error_message(retval)); 274 goto cleanup; 275 } 276 277 if (expire_pw) { 278 (void) time(&now); 279 kadm5_userprinc.pw_expiration = now; 280 mask |= KADM5_PW_EXPIRATION; 281 } 282 283 mask |= KADM5_PRINCIPAL; 284 retval = kadm5_create_principal(handle, &kadm5_userprinc, 285 mask, password); 286 if (retval) { 287 switch (retval) { 288 case KADM5_AUTH_ADD: 289 if (debug) 290 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 291 "PAM-KRB5-AUTOMIGRATE (auth): %s does " 292 "not have the ADD privilege " 293 "for kadm5_create_principal: %s"), 294 svcprincstr, error_message(retval)); 295 break; 296 297 default: 298 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 299 "PAM-KRB5-AUTOMIGRATE (auth): Generic error" 300 "while doing kadm5_create_principal: %s"), 301 error_message(retval)); 302 break; 303 } 304 goto cleanup; 305 } 306 307 /* 308 * Success, new user principal has been added ! 309 */ 310 if (!quiet) { 311 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 312 313 (void) snprintf(messages[0], sizeof (messages[0]), 314 dgettext(TEXT_DOMAIN, "\nUser `%s' has been " 315 "automatically migrated to the Kerberos realm %s\n"), 316 user, def_realm); 317 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, 318 messages, NULL); 319 } 320 if (debug) 321 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 322 "PAM-KRB5-AUTOMIGRATE (auth): User %s " 323 "has been added to the Kerberos KDC database"), 324 userprincstr); 325 326 /* 327 * Since this is a new krb5 principal, do a pam_set_data() 328 * for possible use by the acct_mgmt routine of pam_krb5(5) 329 */ 330 if (pam_get_data(pamh, KRB5_AUTOMIGRATE_DATA, 331 (const void **)&userdata) == PAM_SUCCESS) { 332 /* 333 * We created a princ in a previous run on the same handle and 334 * it must have been for a different PAM_USER / princ name, 335 * otherwise we couldn't succeed here, unless that princ 336 * got deleted. 337 */ 338 if (userdata != NULL) 339 free(userdata); 340 } 341 userdata = (char *)strdup(user); 342 if (pam_set_data(pamh, KRB5_AUTOMIGRATE_DATA, userdata, 343 krb5_migrate_cleanup) != PAM_SUCCESS) { 344 if (userdata != NULL) 345 free(userdata); 346 } 347 348 cleanup: 349 if (service) 350 free(service); 351 if (kadmin_princ) 352 free(kadmin_princ); 353 if (svcprincstr) 354 free(svcprincstr); 355 if (userprincstr) 356 free(userprincstr); 357 if (def_realm) 358 free(def_realm); 359 (void) kadm5_free_principal_ent(handle, &kadm5_userprinc); 360 (void) kadm5_destroy((void *)handle); 361 if (context != NULL) 362 krb5_free_context(context); 363 364 return (PAM_IGNORE); 365 } 366 367 /*ARGSUSED*/ 368 static void 369 krb5_migrate_cleanup(pam_handle_t *pamh, void *data, int pam_status) { 370 if (data != NULL) 371 free((char *)data); 372 } 373 374 /*ARGSUSED*/ 375 int 376 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 377 { 378 return (PAM_IGNORE); 379 } 380