9511 printf family isn't aware of multibyte decimal point characters (fix regression)
[unleashed.git] / usr / src / lib / pam_modules / krb5 / krb5_password.c
blob0a33a72f7835d06ec6c33d0bcbcff24ccc5dcf89
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <kadm5/admin.h>
27 #include <krb5.h>
29 #include <security/pam_appl.h>
30 #include <security/pam_modules.h>
31 #include <security/pam_impl.h>
32 #include <syslog.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <pwd.h>
38 #include <libintl.h>
39 #include <netdb.h>
40 #include "utils.h"
41 #include "krb5_repository.h"
43 extern int attempt_krb5_auth(pam_handle_t *, krb5_module_data_t *, char *,
44 char **, boolean_t);
45 extern int krb5_verifypw(char *, char *, int);
47 static void display_msg(pam_handle_t *, int, char *);
48 static void display_msgs(pam_handle_t *, int, int,
49 char msgs[][PAM_MAX_MSG_SIZE]);
50 static int krb5_changepw(pam_handle_t *, char *, char *, char *, int);
53 * set_ccname()
55 * set KRB5CCNAME shell var
57 static void
58 set_ccname(
59 pam_handle_t *pamh,
60 krb5_module_data_t *kmd,
61 int login_result,
62 int debug)
64 int result;
66 if (debug)
67 __pam_log(LOG_AUTH | LOG_DEBUG,
68 "PAM-KRB5 (password): password: finalize"
69 " ccname env, login_result =%d, env ='%s'",
70 login_result, kmd->env ? kmd->env : "<null>");
72 if (kmd->env) {
74 if (login_result == PAM_SUCCESS) {
76 * Put ccname into the pamh so that login
77 * apps can pick this up when they run
78 * pam_getenvlist().
80 if ((result = pam_putenv(pamh, kmd->env))
81 != PAM_SUCCESS) {
82 /* should not happen but... */
83 __pam_log(LOG_AUTH | LOG_ERR,
84 "PAM-KRB5 (password):"
85 " pam_putenv failed: result: %d",
86 result);
87 goto cleanupccname;
89 } else {
90 cleanupccname:
91 /* for lack of a Solaris unputenv() */
92 krb5_unsetenv(KRB5_ENV_CCNAME);
93 free(kmd->env);
94 kmd->env = NULL;
100 * get_set_creds()
102 * do a krb5 login to get and set krb5 creds (needed after a pw change
103 * on pw expire on login)
105 static void
106 get_set_creds(
107 pam_handle_t *pamh,
108 krb5_module_data_t *kmd,
109 char *user,
110 char *newpass,
111 int debug)
113 int login_result;
115 if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
116 return;
119 * if pw has expired, get/set krb5 creds ala auth mod
121 * pwchange verified user sufficiently, so don't request strict
122 * tgt verification (will cause rcache perm issues possibly anyways)
124 login_result = attempt_krb5_auth(pamh, kmd, user, &newpass, 0);
125 if (debug)
126 __pam_log(LOG_AUTH | LOG_DEBUG,
127 "PAM-KRB5 (password): get_set_creds: login_result= %d",
128 login_result);
130 * the krb5 login should not fail, but if so,
131 * warn the user they have to kinit(1)
133 if (login_result != PAM_SUCCESS) {
134 display_msg(pamh, PAM_TEXT_INFO,
135 dgettext(TEXT_DOMAIN,
136 "Warning: "
137 "Could not cache Kerberos"
138 " credentials, please run "
139 "kinit(1) or re-login\n"));
141 set_ccname(pamh, kmd, login_result, debug);
144 * This is the PAM Kerberos Password Change module
149 pam_sm_chauthtok(
150 pam_handle_t *pamh,
151 int flags,
152 int argc,
153 const char **argv)
156 char *user;
157 int err, result = PAM_AUTHTOK_ERR;
158 char *newpass = NULL;
159 char *oldpass = NULL;
160 int i;
161 int debug = 0;
162 uid_t pw_uid;
163 krb5_module_data_t *kmd = NULL;
164 pam_repository_t *rep_data = NULL;
166 for (i = 0; i < argc; i++) {
167 if (strcmp(argv[i], "debug") == 0)
168 debug = 1;
169 else
170 __pam_log(LOG_AUTH | LOG_ERR,
171 "PAM-KRB5 (password): illegal option %s",
172 argv[i]);
175 if (debug)
176 __pam_log(LOG_AUTH | LOG_DEBUG,
177 "PAM-KRB5 (password): start: flags = %x",
178 flags);
180 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
182 if (rep_data != NULL) {
183 if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
184 if (debug)
185 __pam_log(LOG_AUTH | LOG_DEBUG,
186 "PAM-KRB5 (auth): wrong"
187 "repository found (%s), returning "
188 "PAM_IGNORE", rep_data->type);
189 return (PAM_IGNORE);
193 if (flags & PAM_PRELIM_CHECK) {
194 /* Nothing to do here */
195 if (debug)
196 __pam_log(LOG_AUTH | LOG_DEBUG,
197 "PAM-KRB5 (password): prelim check");
198 return (PAM_IGNORE);
201 /* make sure PAM framework is telling us to update passwords */
202 if (!(flags & PAM_UPDATE_AUTHTOK)) {
203 __pam_log(LOG_AUTH | LOG_ERR,
204 "PAM-KRB5 (password): bad flags: %d",
205 flags);
206 return (PAM_SYSTEM_ERR);
210 if ((err = pam_get_data(pamh, KRB5_DATA, (const void **)&kmd))
211 != PAM_SUCCESS) {
212 if (debug)
213 __pam_log(LOG_AUTH | LOG_DEBUG,
214 "PAM-KRB5 (password): get mod data failed %d",
215 err);
216 kmd = NULL;
219 if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
220 /* let's make sure we know the krb5 pw has expired */
222 if (debug)
223 __pam_log(LOG_AUTH | LOG_DEBUG,
224 "PAM-KRB5 (password): kmd age status %d",
225 kmd ? kmd->age_status : -99);
227 if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
228 return (PAM_IGNORE);
231 (void) pam_get_item(pamh, PAM_USER, (void **)&user);
233 if (user == NULL || *user == '\0') {
234 __pam_log(LOG_AUTH | LOG_ERR,
235 "PAM-KRB5 (password): username is empty");
236 return (PAM_USER_UNKNOWN);
239 if (!get_pw_uid(user, &pw_uid)) {
240 __pam_log(LOG_AUTH | LOG_ERR,
241 "PAM-KRB5 (password): can't get uid for %s", user);
242 return (PAM_USER_UNKNOWN);
246 * if root key exists in the keytab, it's a random key so no
247 * need to prompt for pw and we just return IGNORE
249 if ((strcmp(user, ROOT_UNAME) == 0) &&
250 key_in_keytab(user, debug)) {
251 if (debug)
252 __pam_log(LOG_AUTH | LOG_DEBUG,
253 "PAM-KRB5 (password): "
254 "key for '%s' in keytab, returning IGNORE", user);
255 result = PAM_IGNORE;
256 goto out;
259 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpass);
262 * If the preauth type done didn't use a passwd just ignore the error.
264 if (newpass == NULL)
265 if (kmd && kmd->preauth_type == KRB_PKINIT)
266 return (PAM_IGNORE);
267 else
268 return (PAM_SYSTEM_ERR);
270 (void) pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&oldpass);
272 if (oldpass == NULL)
273 if (kmd && kmd->preauth_type == KRB_PKINIT)
274 return (PAM_IGNORE);
275 else
276 return (PAM_SYSTEM_ERR);
278 result = krb5_verifypw(user, oldpass, debug);
279 if (debug)
280 __pam_log(LOG_AUTH | LOG_DEBUG,
281 "PAM-KRB5 (password): verifypw %d", result);
284 * If it's a bad password or general failure, we are done.
286 if (result != 0) {
288 * if the preauth type done didn't use a passwd just ignore the
289 * error.
291 if (kmd && kmd->preauth_type == KRB_PKINIT)
292 return (PAM_IGNORE);
294 if (result == 2)
295 display_msg(pamh, PAM_ERROR_MSG, dgettext(TEXT_DOMAIN,
296 "Old Kerberos password incorrect\n"));
297 return (PAM_AUTHTOK_ERR);
301 * If the old password verifies try to change it regardless of the
302 * preauth type and do not ignore the error.
304 result = krb5_changepw(pamh, user, oldpass, newpass, debug);
305 if (result == PAM_SUCCESS) {
306 display_msg(pamh, PAM_TEXT_INFO, dgettext(TEXT_DOMAIN,
307 "Kerberos password successfully changed\n"));
309 get_set_creds(pamh, kmd, user, newpass, debug);
312 out:
313 if (debug)
314 __pam_log(LOG_AUTH | LOG_DEBUG,
315 "PAM-KRB5 (password): out: returns %d",
316 result);
318 return (result);
322 krb5_verifypw(
323 char *princ_str,
324 char *old_password,
325 int debug)
327 kadm5_ret_t code;
328 krb5_principal princ = 0;
329 char admin_realm[1024];
330 char kprinc[2*MAXHOSTNAMELEN];
331 char *cpw_service;
332 void *server_handle;
333 krb5_context context;
334 kadm5_config_params params;
336 (void) memset((char *)&params, 0, sizeof (params));
338 if (code = krb5_init_secure_context(&context)) {
339 return (6);
342 if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
343 2*MAXHOSTNAMELEN)) != 0) {
344 return (code);
347 /* Need to get a krb5_principal struct */
349 code = krb5_parse_name(context, kprinc, &princ);
351 if (code != 0)
352 return (6);
354 if (strlen(old_password) == 0) {
355 krb5_free_principal(context, princ);
356 return (5);
359 (void) strlcpy(admin_realm,
360 krb5_princ_realm(context, princ)->data,
361 sizeof (admin_realm));
363 params.mask |= KADM5_CONFIG_REALM;
364 params.realm = admin_realm;
367 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
368 __pam_log(LOG_AUTH | LOG_ERR,
369 "PAM-KRB5 (password): unable to get host based "
370 "service name for realm %s\n",
371 admin_realm);
372 krb5_free_principal(context, princ);
373 return (3);
376 code = kadm5_init_with_password(kprinc, old_password, cpw_service,
377 &params, KADM5_STRUCT_VERSION,
378 KADM5_API_VERSION_2, NULL,
379 &server_handle);
380 if (code != 0) {
381 if (debug)
382 __pam_log(LOG_AUTH | LOG_DEBUG,
383 "PAM-KRB5: krb5_verifypw: init_with_pw"
384 " failed: (%s)", error_message(code));
385 krb5_free_principal(context, princ);
386 return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
389 krb5_free_principal(context, princ);
391 (void) kadm5_destroy(server_handle);
393 return (0);
397 * Function: krb5_changepw
399 * Purpose: Initialize and call lower level routines to change a password
401 * Arguments:
403 * princ_str principal name to use, optional
404 * old_password old password
405 * new_password new password
407 * Returns:
408 * exit status of PAM_SUCCESS for success
409 * else returns PAM failure
411 * Requires:
412 * Passwords cannot be more than 255 characters long.
414 * Modifies:
416 * Changes the principal's password.
419 static int
420 krb5_changepw(
421 pam_handle_t *pamh,
422 char *princ_str,
423 char *old_password,
424 char *new_password,
425 int debug)
427 kadm5_ret_t code;
428 krb5_principal princ = 0;
429 char msg_ret[1024], admin_realm[1024];
430 char kprinc[2*MAXHOSTNAMELEN];
431 char *cpw_service;
432 void *server_handle;
433 krb5_context context;
434 kadm5_config_params params;
436 (void) memset((char *)&params, 0, sizeof (params));
438 if (krb5_init_secure_context(&context) != 0)
439 return (PAM_SYSTEM_ERR);
441 if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
442 2*MAXHOSTNAMELEN)) != 0) {
443 return (code);
446 /* Need to get a krb5_principal struct */
448 code = krb5_parse_name(context, kprinc, &princ);
449 if (code != 0)
450 return (PAM_SYSTEM_ERR);
452 if (strlen(old_password) == 0) {
453 krb5_free_principal(context, princ);
454 return (PAM_AUTHTOK_ERR);
457 (void) snprintf(admin_realm, sizeof (admin_realm), "%s",
458 krb5_princ_realm(context, princ)->data);
459 params.mask |= KADM5_CONFIG_REALM;
460 params.realm = admin_realm;
463 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
464 __pam_log(LOG_AUTH | LOG_ERR,
465 "PAM-KRB5 (password):unable to get host based "
466 "service name for realm %s\n",
467 admin_realm);
468 return (PAM_SYSTEM_ERR);
471 code = kadm5_init_with_password(kprinc, old_password, cpw_service,
472 &params, KADM5_STRUCT_VERSION,
473 KADM5_API_VERSION_2, NULL,
474 &server_handle);
475 free(cpw_service);
476 if (code != 0) {
477 if (debug)
478 __pam_log(LOG_AUTH | LOG_DEBUG,
479 "PAM-KRB5 (password): changepw: "
480 "init_with_pw failed: (%s)", error_message(code));
481 krb5_free_principal(context, princ);
482 return ((code == KADM5_BAD_PASSWORD) ?
483 PAM_AUTHTOK_ERR : PAM_SYSTEM_ERR);
486 code = kadm5_chpass_principal_util(server_handle, princ,
487 new_password,
488 NULL /* don't need pw back */,
489 msg_ret,
490 sizeof (msg_ret));
492 if (code) {
493 char msgs[2][PAM_MAX_MSG_SIZE];
495 (void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, "%s",
496 dgettext(TEXT_DOMAIN,
497 "Kerberos password not changed: "));
498 (void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, "%s", msg_ret);
500 display_msgs(pamh, PAM_ERROR_MSG, 2, msgs);
503 krb5_free_principal(context, princ);
505 (void) kadm5_destroy(server_handle);
507 if (debug)
508 __pam_log(LOG_AUTH | LOG_DEBUG,
509 "PAM-KRB5 (password): changepw: end %d", code);
511 if (code != 0)
512 return (PAM_AUTHTOK_ERR);
514 return (PAM_SUCCESS);
517 static void
518 display_msgs(pam_handle_t *pamh,
519 int msg_style, int nmsg, char msgs[][PAM_MAX_MSG_SIZE])
521 (void) __pam_display_msg(pamh, msg_style, nmsg, msgs, NULL);
525 static void
526 display_msg(pam_handle_t *pamh, int msg_style, char *msg)
528 char pam_msg[1][PAM_MAX_MSG_SIZE];
530 (void) snprintf(pam_msg[0], PAM_MAX_MSG_SIZE, "%s", msg);
531 display_msgs(pamh, msg_style, 1, pam_msg);