krb5_migrate_authenticate.c revision 4621:77407f8b6bb2
1174993Srafan/* 250276Speter * CDDL HEADER START 3174993Srafan * 450276Speter * The contents of this file are subject to the terms of the 550276Speter * Common Development and Distribution License (the "License"). 650276Speter * You may not use this file except in compliance with the License. 750276Speter * 850276Speter * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 950276Speter * or http://www.opensolaris.org/os/licensing. 1050276Speter * See the License for the specific language governing permissions 1150276Speter * and limitations under the License. 1250276Speter * 1350276Speter * When distributing Covered Code, include this CDDL HEADER in each 1450276Speter * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1550276Speter * If applicable, add the following below this CDDL HEADER, with the 1650276Speter * fields enclosed by brackets "[]" replaced with your own identifying 1750276Speter * information: Portions Copyright [yyyy] [name of copyright owner] 1850276Speter * 1950276Speter * CDDL HEADER END 2050276Speter */ 2150276Speter/* 2250276Speter * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2350276Speter * Use is subject to license terms. 2450276Speter */ 2550276Speter 2650276Speter#pragma ident "%Z%%M% %I% %E% SMI" 2750276Speter 2850276Speter#include <kadm5/admin.h> 2950276Speter#include <krb5.h> 30174993Srafan#include <security/pam_appl.h> 3150276Speter#include <security/pam_modules.h> 3250276Speter#include <security/pam_impl.h> 3350276Speter#include <string.h> 3450276Speter#include <stdio.h> 3550276Speter#include <stdlib.h> 3650276Speter#include <pwd.h> 3750276Speter#include <syslog.h> 3850276Speter#include <libintl.h> 3950276Speter 4050276Speter#define KRB5_AUTOMIGRATE_DATA "SUNW-KRB5-AUTOMIGRATE-DATA" 4150276Speter 4250276Speterstatic void krb5_migrate_cleanup(pam_handle_t *pamh, void *data, 4350276Speter int pam_status); 4450276Speter 4550276Speter/* 4650276Speter * pam_sm_authenticate - Authenticate a host-based client service 4750276Speter * principal to kadmind in order to permit the creation of a new user 48166124Srafan * principal in the client's default realm. 49166124Srafan */ 50166124Srafanint pam_sm_authenticate(pam_handle_t *pamh, int flags, 5150276Speter int argc, const char **argv) 5266963Speter{ 5350276Speter char *user = NULL; 5450276Speter char *userdata = NULL; 5550276Speter char *password = NULL; 5697049Speter int err, i; 5750276Speter time_t now; 5850276Speter 5950276Speter /* pam.conf options */ 6076726Speter int debug = 0; 61166124Srafan int quiet = 0; 62166124Srafan int expire_pw = 0; 63166124Srafan char *service = NULL; 64166124Srafan 65166124Srafan /* krb5-specific defines */ 6676726Speter kadm5_ret_t retval = 0; 6750276Speter krb5_context context = NULL; 6850276Speter kadm5_config_params params; 69166124Srafan krb5_principal svcprinc; 7050276Speter char *svcprincstr = NULL; 7150276Speter krb5_principal userprinc; 7250276Speter char *userprincstr = NULL; 7350276Speter int strlength = 0; 7450276Speter kadm5_principal_ent_rec kadm5_userprinc; 7550276Speter char *kadmin_princ = NULL; 7650276Speter char *def_realm = NULL; 7750276Speter void *handle = NULL; 7850276Speter long mask = 0; 7962449Speter 8050276Speter for (i = 0; i < argc; i++) { 8150276Speter if (strcmp(argv[i], "debug") == 0) { 82166124Srafan debug = 1; 8350276Speter } else if (strcmp(argv[i], "quiet") == 0) { 8450276Speter quiet = 1; 8550276Speter } else if (strcmp(argv[i], "expire_pw") == 0) { 8676726Speter expire_pw = 1; 8750276Speter } else if ((strstr(argv[i], "client_service=") != NULL) && 8850276Speter (strcmp((strstr(argv[i], "=") + 1), "") != 0)) { 8950276Speter service = (char *)strdup(strstr(argv[i], "=") + 1); 9050276Speter } else { 9150276Speter __pam_log(LOG_AUTH | LOG_ERR, 9250276Speter "PAM-KRB5-AUTOMIGRATE (auth): unrecognized " 9350276Speter "option %s", 94174993Srafan argv[i]); 9550276Speter } 9650276Speter } 9797049Speter 9862449Speter if (flags & PAM_SILENT) 9950276Speter quiet = 1; 10050276Speter 10150276Speter err = pam_get_item(pamh, PAM_USER, (void**)&user); 10276726Speter if (err != PAM_SUCCESS) { 10376726Speter goto cleanup; 10450276Speter } 10550276Speter 10650276Speter /* 107166124Srafan * Check if user name is *not* NULL 10850276Speter */ 10950276Speter if (user == NULL || (user[0] == '\0')) { 11050276Speter if (debug) 11150276Speter __pam_log(LOG_AUTH | LOG_DEBUG, 11250276Speter "PAM-KRB5-AUTOMIGRATE (auth): " 11350276Speter "user empty or null"); 11450276Speter goto cleanup; 11550276Speter } 11650276Speter 11750276Speter /* 11850276Speter * Grok the user password 11950276Speter */ 12050276Speter err = pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 12150276Speter if (err != PAM_SUCCESS) { 12262449Speter goto cleanup; 12350276Speter } 12450276Speter 12550276Speter if (password == NULL || (password[0] == '\0')) { 12650276Speter if (debug) 12797049Speter __pam_log(LOG_AUTH | LOG_DEBUG, 12866963Speter "PAM-KRB5-AUTOMIGRATE (auth): " 129174993Srafan "authentication token is empty or null"); 13050276Speter goto cleanup; 13150276Speter } 13250276Speter 13350276Speter 13450276Speter /* 13550276Speter * Now, lets do the all krb5/kadm5 setup for the principal addition 13650276Speter */ 13750276Speter if (retval = krb5_init_context(&context)) { 13850276Speter __pam_log(LOG_AUTH | LOG_ERR, 13950276Speter "PAM-KRB5-AUTOMIGRATE (auth): Error initializing " 14050276Speter "krb5: %s", 14150276Speter error_message(retval)); 14250276Speter goto cleanup; 14350276Speter } 14450276Speter 14550276Speter (void) memset((char *)¶ms, 0, sizeof (params)); 14650276Speter (void) memset(&kadm5_userprinc, 0, sizeof (kadm5_userprinc)); 14750276Speter 14850276Speter if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { 14950276Speter __pam_log(LOG_AUTH | LOG_ERR, 15050276Speter "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining " 15166963Speter "default krb5 realm"); 15266963Speter goto cleanup; 15350276Speter } 15450276Speter 15597049Speter params.mask |= KADM5_CONFIG_REALM; 15650276Speter params.realm = def_realm; 15750276Speter 15850276Speter if (kadm5_get_adm_host_srv_name(context, def_realm, 15950276Speter &kadmin_princ)) { 16050276Speter __pam_log(LOG_AUTH | LOG_ERR, 16150276Speter "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining " 16250276Speter "host based service name for realm %s\n", def_realm); 16350276Speter goto cleanup; 16450276Speter } 16550276Speter 16650276Speter if (retval = krb5_sname_to_principal(context, NULL, 16750276Speter (service != NULL)?service:"host", 168 KRB5_NT_SRV_HST, 169 &svcprinc)) { 170 __pam_log(LOG_AUTH | LOG_ERR, 171 "PAM-KRB5-AUTOMIGRATE (auth): Error while creating " 172 "krb5 host service principal: %s", 173 error_message(retval)); 174 goto cleanup; 175 } 176 177 if (retval = krb5_unparse_name(context, svcprinc, 178 &svcprincstr)) { 179 __pam_log(LOG_AUTH | LOG_ERR, 180 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 181 "unparsing principal name: %s", 182 error_message(retval)); 183 krb5_free_principal(context, svcprinc); 184 goto cleanup; 185 } 186 187 krb5_free_principal(context, svcprinc); 188 189 /* 190 * Initialize the kadm5 connection using the default keytab 191 */ 192 retval = kadm5_init_with_skey(svcprincstr, NULL, 193 kadmin_princ, 194 ¶ms, 195 KADM5_STRUCT_VERSION, 196 KADM5_API_VERSION_2, 197 &handle); 198 if (retval) { 199 __pam_log(LOG_AUTH | LOG_ERR, 200 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 201 "doing kadm5_init_with_skey: %s", 202 error_message(retval)); 203 goto cleanup; 204 } 205 206 207 /* 208 * The RPCSEC_GSS connection has been established; Lets check to see 209 * if the corresponding user principal exists in the KDC database. 210 * If not, lets create a new one. 211 */ 212 213 strlength = strlen(user) + strlen(def_realm) + 2; 214 userprincstr = (char *)malloc(strlength); 215 (void) strlcpy(userprincstr, user, strlength); 216 (void) strlcat(userprincstr, "@", strlength); 217 (void) strlcat(userprincstr, def_realm, strlength); 218 219 220 if (retval = krb5_parse_name(context, userprincstr, 221 &userprinc)) { 222 __pam_log(LOG_AUTH | LOG_ERR, 223 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 224 "parsing user principal name: %s", 225 error_message(retval)); 226 goto cleanup; 227 } 228 229 retval = kadm5_get_principal(handle, userprinc, &kadm5_userprinc, 230 KADM5_PRINCIPAL_NORMAL_MASK); 231 232 krb5_free_principal(context, userprinc); 233 234 if (retval) { 235 switch (retval) { 236 case KADM5_AUTH_GET: 237 if (debug) 238 __pam_log(LOG_AUTH | LOG_DEBUG, 239 "PAM-KRB5-AUTOMIGRATE (auth): %s does " 240 "not have the GET privilege " 241 "for kadm5_get_principal: %s", 242 svcprincstr, error_message(retval)); 243 break; 244 245 case KADM5_UNK_PRINC: 246 default: 247 break; 248 } 249 /* 250 * We will try & add this principal anyways, continue on ... 251 */ 252 (void) memset(&kadm5_userprinc, 0, sizeof (kadm5_userprinc)); 253 } else { 254 /* 255 * Principal already exists in the KDC database, quit now 256 */ 257 if (debug) 258 __pam_log(LOG_AUTH | LOG_DEBUG, 259 "PAM-KRB5-AUTOMIGRATE (auth): Principal %s " 260 "already exists in Kerberos KDC database", 261 userprincstr); 262 goto cleanup; 263 } 264 265 266 267 if (retval = krb5_parse_name(context, userprincstr, 268 &(kadm5_userprinc.principal))) { 269 __pam_log(LOG_AUTH | LOG_ERR, 270 "PAM-KRB5-AUTOMIGRATE (auth): Error while " 271 "parsing user principal name: %s", 272 error_message(retval)); 273 goto cleanup; 274 } 275 276 if (expire_pw) { 277 (void) time(&now); 278 /* 279 * The local system time could actually be later than the 280 * system time of the KDC we are authenticating to. We expire 281 * w/the local system time minus clockskew so that we are 282 * assured that it is expired on this login, not the next. 283 */ 284 now -= context->clockskew; 285 kadm5_userprinc.pw_expiration = now; 286 mask |= KADM5_PW_EXPIRATION; 287 } 288 289 mask |= KADM5_PRINCIPAL; 290 retval = kadm5_create_principal(handle, &kadm5_userprinc, 291 mask, password); 292 if (retval) { 293 switch (retval) { 294 case KADM5_AUTH_ADD: 295 if (debug) 296 __pam_log(LOG_AUTH | LOG_DEBUG, 297 "PAM-KRB5-AUTOMIGRATE (auth): %s does " 298 "not have the ADD privilege " 299 "for kadm5_create_principal: %s", 300 svcprincstr, error_message(retval)); 301 break; 302 303 default: 304 __pam_log(LOG_AUTH | LOG_ERR, 305 "PAM-KRB5-AUTOMIGRATE (auth): Generic error" 306 "while doing kadm5_create_principal: %s", 307 error_message(retval)); 308 break; 309 } 310 goto cleanup; 311 } 312 313 /* 314 * Success, new user principal has been added ! 315 */ 316 if (!quiet) { 317 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 318 319 (void) snprintf(messages[0], sizeof (messages[0]), 320 dgettext(TEXT_DOMAIN, "\nUser `%s' has been " 321 "automatically migrated to the Kerberos realm %s\n"), 322 user, def_realm); 323 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, 324 messages, NULL); 325 } 326 if (debug) 327 __pam_log(LOG_AUTH | LOG_DEBUG, 328 "PAM-KRB5-AUTOMIGRATE (auth): User %s " 329 "has been added to the Kerberos KDC database", 330 userprincstr); 331 332 /* 333 * Since this is a new krb5 principal, do a pam_set_data() 334 * for possible use by the acct_mgmt routine of pam_krb5(5) 335 */ 336 if (pam_get_data(pamh, KRB5_AUTOMIGRATE_DATA, 337 (const void **)&userdata) == PAM_SUCCESS) { 338 /* 339 * We created a princ in a previous run on the same handle and 340 * it must have been for a different PAM_USER / princ name, 341 * otherwise we couldn't succeed here, unless that princ 342 * got deleted. 343 */ 344 if (userdata != NULL) 345 free(userdata); 346 } 347 userdata = (char *)strdup(user); 348 if (pam_set_data(pamh, KRB5_AUTOMIGRATE_DATA, userdata, 349 krb5_migrate_cleanup) != PAM_SUCCESS) { 350 if (userdata != NULL) 351 free(userdata); 352 } 353 354cleanup: 355 if (service) 356 free(service); 357 if (kadmin_princ) 358 free(kadmin_princ); 359 if (svcprincstr) 360 free(svcprincstr); 361 if (userprincstr) 362 free(userprincstr); 363 if (def_realm) 364 free(def_realm); 365 (void) kadm5_free_principal_ent(handle, &kadm5_userprinc); 366 (void) kadm5_destroy((void *)handle); 367 if (context != NULL) 368 krb5_free_context(context); 369 370 return (PAM_IGNORE); 371} 372 373/*ARGSUSED*/ 374static void 375krb5_migrate_cleanup(pam_handle_t *pamh, void *data, int pam_status) { 376 if (data != NULL) 377 free((char *)data); 378} 379 380/*ARGSUSED*/ 381int 382pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 383{ 384 return (PAM_IGNORE); 385} 386