4 * PAM authentication management functions for pam_krb5
6 * $FreeBSD: src/lib/libpam/modules/pam_krb5/pam_krb5_auth.c,v 1.1.2.2 2001/07/29 18:57:30 markm Exp $
7 * $DragonFly: src/lib/libpam/modules/pam_krb5/Attic/pam_krb5_auth.c,v 1.2 2003/06/17 04:26:50 dillon Exp $
10 static const char rcsid[] = "$Id: pam_krb5_auth.c,v 1.18 2000/01/04 08:44:08 fcusack Exp $";
12 #include <sys/types.h>
15 #include <limits.h> /* PATH_MAX */
16 #include <pwd.h> /* getpwnam */
17 #include <stdio.h> /* tmpnam */
18 #include <stdlib.h> /* malloc */
19 #include <strings.h> /* strchr */
20 #include <syslog.h> /* syslog */
21 #include <unistd.h> /* chown */
23 #include <security/pam_appl.h>
24 #include <security/pam_modules.h>
30 extern krb5_cc_ops krb5_mcc_ops;
32 /* A useful logging macro */
33 #define DLOG(error_func, error_msg) \
35 syslog(LOG_DEBUG, "pam_krb5: pam_sm_authenticate(%s %s): %s: %s", \
36 service, name, error_func, error_msg)
38 /* Authenticate a user via krb5 */
40 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
43 krb5_error_code krbret;
44 krb5_context pam_context;
47 krb5_ccache ccache, ccache_check;
48 krb5_get_init_creds_opt opts;
52 char *princ_name = NULL;
53 char *pass = NULL, *service = NULL;
55 char cache_name[L_tmpnam + 8];
56 char lname[64]; /* local acct name */
59 int debug = 0, try_first_pass = 0, use_first_pass = 0;
60 int forwardable = 0, reuse_ccache = 0, no_ccache = 0;
62 for (i = 0; i < argc; i++) {
63 if (strcmp(argv[i], "debug") == 0)
65 else if (strcmp(argv[i], "try_first_pass") == 0)
67 else if (strcmp(argv[i], "use_first_pass") == 0)
69 else if (strcmp(argv[i], "forwardable") == 0)
71 else if (strcmp(argv[i], "reuse_ccache") == 0)
73 else if (strcmp(argv[i], "no_ccache") == 0)
78 if ((pamret = pam_get_user(pamh, &name, "login: ")) != PAM_SUCCESS) {
79 return PAM_SERVICE_ERR;
82 /* Get service name */
83 (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
89 if ((krbret = krb5_init_context(&pam_context)) != 0) {
90 DLOG("krb5_init_context()", error_message(krbret));
91 return PAM_SERVICE_ERR;
93 krb5_get_init_creds_opt_init(&opts);
94 memset(&creds, 0, sizeof(krb5_creds));
95 memset(cache_name, 0, sizeof(cache_name));
96 memset(lname, 0, sizeof(lname));
99 krb5_get_init_creds_opt_set_forwardable(&opts, 1);
102 if ((krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE)) != 0) {
103 /* Solaris dtlogin doesn't call pam_end() on failure */
104 if (krbret != KRB5_CC_TYPE_EXISTS) {
105 DLOG("krb5_cc_register()", error_message(krbret));
106 pamret = PAM_SERVICE_ERR;
111 /* Get principal name */
112 if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
113 DLOG("krb5_parse_name()", error_message(krbret));
114 pamret = PAM_SERVICE_ERR;
118 /* Now convert the principal name into something human readable */
119 if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
120 DLOG("krb5_unparse_name()", error_message(krbret));
121 pamret = PAM_SERVICE_ERR;
126 prompt = malloc(16 + strlen(princ_name));
128 DLOG("malloc()", "failure");
129 pamret = PAM_BUF_ERR;
132 (void) sprintf(prompt, "Password for %s: ", princ_name);
134 if (try_first_pass || use_first_pass)
135 (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
140 if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF,
142 DLOG("get_user_info()", pam_strerror(pamh, pamret));
143 pamret = PAM_SERVICE_ERR;
146 /* We have to free pass. */
147 if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
148 DLOG("pam_set_item()", pam_strerror(pamh, pamret));
150 pamret = PAM_SERVICE_ERR;
154 /* Now we get it back from the library. */
155 (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
158 /* Verify the local user exists (AFTER getting the password) */
159 if (strchr(name, '@')) {
160 /* get a local account name for this principal */
161 if ((krbret = krb5_aname_to_localname(pam_context, princ,
162 sizeof(lname), lname)) != 0) {
163 DLOG("krb5_aname_to_localname()", error_message(krbret));
164 pamret = PAM_USER_UNKNOWN;
167 DLOG("changing PAM_USER to", lname);
168 if ((pamret = pam_set_item(pamh, PAM_USER, lname)) != 0) {
169 DLOG("pam_set_item()", pam_strerror(pamh, pamret));
170 pamret = PAM_SERVICE_ERR;
173 if ((pamret = pam_get_item(pamh, PAM_USER, (const void **) &name)
175 DLOG("pam_get_item()", pam_strerror(pamh, pamret));
176 pamret = PAM_SERVICE_ERR;
182 DLOG("getpwnam()", lname);
183 pamret = PAM_USER_UNKNOWN;
188 if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
189 pass, pam_prompter, pamh, 0, NULL, &opts)) != 0) {
190 DLOG("krb5_get_init_creds_password()", error_message(krbret));
191 if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
195 pamret = PAM_AUTH_ERR;
199 /* Generate a unique cache_name */
200 strcpy(cache_name, "MEMORY:");
201 (void) tmpnam(&cache_name[7]);
203 if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache)) != 0) {
204 DLOG("krb5_cc_resolve()", error_message(krbret));
205 pamret = PAM_SERVICE_ERR;
208 if ((krbret = krb5_cc_initialize(pam_context, ccache, princ)) != 0) {
209 DLOG("krb5_cc_initialize()", error_message(krbret));
210 pamret = PAM_SERVICE_ERR;
213 if ((krbret = krb5_cc_store_cred(pam_context, ccache, &creds)) != 0) {
214 DLOG("krb5_cc_store_cred()", error_message(krbret));
215 (void) krb5_cc_destroy(pam_context, ccache);
216 pamret = PAM_SERVICE_ERR;
221 if (verify_krb_v5_tgt(pam_context, ccache, service, debug) == -1) {
222 (void) krb5_cc_destroy(pam_context, ccache);
223 pamret = PAM_AUTH_ERR;
227 /* A successful authentication, store ccache for sm_setcred() */
228 if (!pam_get_data(pamh, "ccache", (const void **) &ccache_check)) {
229 DLOG("pam_get_data()", "ccache data already present");
230 (void) krb5_cc_destroy(pam_context, ccache);
231 pamret = PAM_AUTH_ERR;
234 if ((pamret = pam_set_data(pamh, "ccache", ccache, cleanup_cache)) != 0) {
235 DLOG("pam_set_data()", pam_strerror(pamh, pamret));
236 (void) krb5_cc_destroy(pam_context, ccache);
237 pamret = PAM_SERVICE_ERR;
242 krb5_free_cred_contents(pam_context, &creds);
244 krb5_free_principal(pam_context, princ);
251 krb5_free_context(pam_context);
252 DLOG("exit", pamret ? "failure" : "success");
258 /* redefine this for pam_sm_setcred() */
260 #define DLOG(error_func, error_msg) \
262 syslog(LOG_DEBUG, "pam_krb5: pam_sm_setcred(%s %s): %s: %s", \
263 service, name, error_func, error_msg)
265 /* Called after a successful authentication. Set user credentials. */
267 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
271 krb5_error_code krbret;
272 krb5_context pam_context;
273 krb5_principal princ;
275 krb5_ccache ccache_temp, ccache_perm;
276 krb5_cc_cursor cursor;
279 char *name, *service = NULL;
280 char *cache_name = NULL, *cache_env_name;
281 struct passwd *pw = NULL;
287 if (flags == PAM_REINITIALIZE_CRED)
288 return PAM_SUCCESS; /* XXX Incorrect behavior */
290 if (flags != PAM_ESTABLISH_CRED)
291 return PAM_SERVICE_ERR;
293 for (i = 0; i < argc; i++) {
294 if (strcmp(argv[i], "debug") == 0)
296 else if (strcmp(argv[i], "no_ccache") == 0)
298 else if (strstr(argv[i], "ccache=") == argv[i])
299 cache_name = (char *) &argv[i][7]; /* save for later */
303 if (pam_get_item(pamh, PAM_USER, (const void **) &name)) {
304 return PAM_SERVICE_ERR;
307 /* Get service name */
308 (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
314 if ((krbret = krb5_init_context(&pam_context)) != 0) {
315 DLOG("krb5_init_context()", error_message(krbret));
316 return PAM_SERVICE_ERR;
319 euid = geteuid(); /* Usually 0 */
322 /* Retrieve the cache name */
323 if ((pamret = pam_get_data(pamh, "ccache", (const void **) &ccache_temp))
325 DLOG("pam_get_data()", pam_strerror(pamh, pamret));
326 pamret = PAM_CRED_UNAVAIL;
330 /* Get the uid. This should exist. */
333 DLOG("getpwnam()", name);
334 pamret = PAM_USER_UNKNOWN;
338 /* Avoid following a symlink as root */
339 if (setegid(pw->pw_gid)) {
340 DLOG("setegid()", name); /* XXX should really log group name or id */
341 pamret = PAM_SERVICE_ERR;
344 if (seteuid(pw->pw_uid)) {
345 DLOG("seteuid()", name);
346 pamret = PAM_SERVICE_ERR;
350 /* Get the cache name */
352 cache_name = malloc(64); /* plenty big */
354 DLOG("malloc()", "failure");
355 pamret = PAM_BUF_ERR;
358 sprintf(cache_name, "FILE:/tmp/krb5cc_%d", pw->pw_uid);
360 /* cache_name was supplied */
361 char *p = calloc(PATH_MAX + 10, 1); /* should be plenty */
362 char *q = cache_name;
364 DLOG("malloc()", "failure");
365 pamret = PAM_BUF_ERR;
370 /* convert %u and %p */
375 sprintf(p, "%d", pw->pw_uid);
377 } else if (*q == 'p') {
378 sprintf(p, "%d", getpid());
381 /* Not a special token */
392 /* Initialize the new ccache */
393 if ((krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ))
395 DLOG("krb5_cc_get_principal()", error_message(krbret));
396 pamret = PAM_SERVICE_ERR;
399 if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm))
401 DLOG("krb5_cc_resolve()", error_message(krbret));
402 pamret = PAM_SERVICE_ERR;
405 if ((krbret = krb5_cc_initialize(pam_context, ccache_perm, princ)) != 0) {
406 DLOG("krb5_cc_initialize()", error_message(krbret));
407 pamret = PAM_SERVICE_ERR;
411 /* Prepare for iteration over creds */
412 if ((krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor))
414 DLOG("krb5_cc_start_seq_get()", error_message(krbret));
415 (void) krb5_cc_destroy(pam_context, ccache_perm);
416 pamret = PAM_SERVICE_ERR;
420 /* Copy the creds (should be two of them) */
421 while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp,
422 &cursor, &creds) == 0)) {
423 if ((krbret = krb5_cc_store_cred(pam_context, ccache_perm,
425 DLOG("krb5_cc_store_cred()", error_message(krbret));
426 (void) krb5_cc_destroy(pam_context, ccache_perm);
427 krb5_free_cred_contents(pam_context, &creds);
428 pamret = PAM_SERVICE_ERR;
431 krb5_free_cred_contents(pam_context, &creds);
433 (void) krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
435 if (strstr(cache_name, "FILE:") == cache_name) {
436 if (chown(&cache_name[5], pw->pw_uid, pw->pw_gid) == -1) {
437 DLOG("chown()", strerror(errno));
438 (void) krb5_cc_destroy(pam_context, ccache_perm);
439 pamret = PAM_SERVICE_ERR;
442 if (chmod(&cache_name[5], (S_IRUSR|S_IWUSR)) == -1) {
443 DLOG("chmod()", strerror(errno));
444 (void) krb5_cc_destroy(pam_context, ccache_perm);
445 pamret = PAM_SERVICE_ERR;
449 (void) krb5_cc_close(pam_context, ccache_perm);
451 cache_env_name = malloc(strlen(cache_name) + 12);
452 if (!cache_env_name) {
453 DLOG("malloc()", "failure");
454 (void) krb5_cc_destroy(pam_context, ccache_perm);
455 pamret = PAM_BUF_ERR;
459 sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
460 if ((pamret = pam_putenv(pamh, cache_env_name)) != 0) {
461 DLOG("pam_putenv()", pam_strerror(pamh, pamret));
462 (void) krb5_cc_destroy(pam_context, ccache_perm);
463 pamret = PAM_SERVICE_ERR;
468 krb5_free_principal(pam_context, princ);
470 krb5_free_context(pam_context);
471 DLOG("exit", pamret ? "failure" : "success");
472 (void) seteuid(euid);
473 (void) setegid(egid);