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 *)&params, 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					&params,
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