1.2.2rc2
[heimdal.git] / lib / auth / pam / pam.c
bloba28fe760738a7ddb1e3ed5e7838c7b7c91cdee7c
1 /*
2 * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #ifdef HAVE_CONFIG_H
35 #include<config.h>
36 RCSID("$Id$");
37 #endif
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <pwd.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <syslog.h>
47 #include <security/pam_appl.h>
48 #include <security/pam_modules.h>
49 #ifndef PAM_AUTHTOK_RECOVERY_ERR /* Fix linsux typo. */
50 #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
51 #endif
53 #include <netinet/in.h>
54 #include <krb.h>
55 #include <kafs.h>
57 #if 0
58 /* Debugging PAM modules is a royal pain, truss helps. */
59 #define DEBUG(msg) (access(msg " at line", __LINE__))
60 #endif
62 static void
63 psyslog(int level, const char *format, ...)
65 va_list args;
66 va_start(args, format);
67 openlog("pam_krb4", LOG_PID, LOG_AUTH);
68 vsyslog(level, format, args);
69 va_end(args);
70 closelog();
73 enum {
74 KRB4_DEBUG,
75 KRB4_USE_FIRST_PASS,
76 KRB4_TRY_FIRST_PASS,
77 KRB4_IGNORE_ROOT,
78 KRB4_NO_VERIFY,
79 KRB4_REAFSLOG,
80 KRB4_CTRLS /* Number of ctrl arguments defined. */
83 #define KRB4_DEFAULTS 0
85 static int ctrl_flags = KRB4_DEFAULTS;
86 #define ctrl_on(x) (krb4_args[x].flag & ctrl_flags)
87 #define ctrl_off(x) (!ctrl_on(x))
89 typedef struct
91 const char *token;
92 unsigned int flag;
93 } krb4_ctrls_t;
95 static krb4_ctrls_t krb4_args[KRB4_CTRLS] =
97 /* KRB4_DEBUG */ { "debug", 0x01 },
98 /* KRB4_USE_FIRST_PASS */ { "use_first_pass", 0x02 },
99 /* KRB4_TRY_FIRST_PASS */ { "try_first_pass", 0x04 },
100 /* KRB4_IGNORE_ROOT */ { "ignore_root", 0x08 },
101 /* KRB4_NO_VERIFY */ { "no_verify", 0x10 },
102 /* KRB4_REAFSLOG */ { "reafslog", 0x20 },
105 static void
106 parse_ctrl(int argc, const char **argv)
108 int i, j;
110 ctrl_flags = KRB4_DEFAULTS;
111 for (i = 0; i < argc; i++)
113 for (j = 0; j < KRB4_CTRLS; j++)
114 if (strcmp(argv[i], krb4_args[j].token) == 0)
115 break;
117 if (j >= KRB4_CTRLS)
118 psyslog(LOG_ALERT, "unrecognized option [%s]", *argv);
119 else
120 ctrl_flags |= krb4_args[j].flag;
124 static void
125 pdeb(const char *format, ...)
127 va_list args;
128 if (ctrl_off(KRB4_DEBUG))
129 return;
130 va_start(args, format);
131 openlog("pam_krb4", LOG_PID, LOG_AUTH);
132 vsyslog(LOG_DEBUG, format, args);
133 va_end(args);
134 closelog();
137 #define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid())
139 static void
140 set_tkt_string(uid_t uid)
142 char buf[128];
144 snprintf(buf, sizeof(buf), "%s%u", TKT_ROOT, (unsigned)uid);
145 krb_set_tkt_string(buf);
147 #if 0
148 /* pam_set_data+pam_get_data are not guaranteed to work, grr. */
149 pam_set_data(pamh, "KRBTKFILE", strdup(t), cleanup);
150 if (pam_get_data(pamh, "KRBTKFILE", (const void**)&tkt) == PAM_SUCCESS)
152 pam_putenv(pamh, var);
154 #endif
156 /* We don't want to inherit this variable.
157 * If we still do, it must have a sane value. */
158 if (getenv("KRBTKFILE") != 0)
160 char *var = malloc(sizeof(buf));
161 snprintf(var, sizeof(buf), "KRBTKFILE=%s", tkt_string());
162 putenv(var);
163 /* free(var); XXX */
167 static int
168 verify_pass(pam_handle_t *pamh,
169 const char *name,
170 const char *inst,
171 const char *pass)
173 char realm[REALM_SZ];
174 int ret, krb_verify, old_euid, old_ruid;
176 krb_get_lrealm(realm, 1);
177 if (ctrl_on(KRB4_NO_VERIFY))
178 krb_verify = KRB_VERIFY_SECURE_FAIL;
179 else
180 krb_verify = KRB_VERIFY_SECURE;
181 old_ruid = getuid();
182 old_euid = geteuid();
183 setreuid(0, 0);
184 ret = krb_verify_user(name, inst, realm, pass, krb_verify, NULL);
185 pdeb("krb_verify_user(`%s', `%s', `%s', pw, %d, NULL) returns %s",
186 name, inst, realm, krb_verify,
187 krb_get_err_text(ret));
188 setreuid(old_ruid, old_euid);
189 if (getuid() != old_ruid || geteuid() != old_euid)
191 psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
192 old_ruid, old_euid, __LINE__);
193 exit(1);
196 switch(ret) {
197 case KSUCCESS:
198 return PAM_SUCCESS;
199 case KDC_PR_UNKNOWN:
200 return PAM_USER_UNKNOWN;
201 case SKDC_CANT:
202 case SKDC_RETRY:
203 case RD_AP_TIME:
204 return PAM_AUTHINFO_UNAVAIL;
205 default:
206 return PAM_AUTH_ERR;
210 static int
211 krb4_auth(pam_handle_t *pamh,
212 int flags,
213 const char *name,
214 const char *inst,
215 struct pam_conv *conv)
217 struct pam_response *resp;
218 char prompt[128];
219 struct pam_message msg, *pmsg = &msg;
220 int ret;
222 if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS))
224 char *pass = 0;
225 ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass);
226 if (ret != PAM_SUCCESS)
228 psyslog(LOG_ERR , "pam_get_item returned error to get-password");
229 return ret;
231 else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS)
232 return PAM_SUCCESS;
233 else if (ctrl_on(KRB4_USE_FIRST_PASS))
234 return PAM_AUTHTOK_RECOVERY_ERR; /* Wrong password! */
235 else
236 /* We tried the first password but it didn't work, cont. */;
239 msg.msg_style = PAM_PROMPT_ECHO_OFF;
240 if (*inst == 0)
241 snprintf(prompt, sizeof(prompt), "%s's Password: ", name);
242 else
243 snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst);
244 msg.msg = prompt;
246 ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
247 if (ret != PAM_SUCCESS)
248 return ret;
250 ret = verify_pass(pamh, name, inst, resp->resp);
251 if (ret == PAM_SUCCESS)
253 memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */
254 free(resp->resp);
255 free(resp);
257 else
259 pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */
260 /* free(resp->resp); XXX */
261 /* free(resp); XXX */
264 return ret;
268 pam_sm_authenticate(pam_handle_t *pamh,
269 int flags,
270 int argc,
271 const char **argv)
273 char *user;
274 int ret;
275 struct pam_conv *conv;
276 struct passwd *pw;
277 uid_t uid = -1;
278 const char *name, *inst;
279 char realm[REALM_SZ];
280 realm[0] = 0;
282 parse_ctrl(argc, argv);
283 ENTRY("pam_sm_authenticate");
285 ret = pam_get_user(pamh, &user, "login: ");
286 if (ret != PAM_SUCCESS)
287 return ret;
289 if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0)
290 return PAM_AUTHINFO_UNAVAIL;
292 ret = pam_get_item(pamh, PAM_CONV, (void*)&conv);
293 if (ret != PAM_SUCCESS)
294 return ret;
296 pw = getpwnam(user);
297 if (pw != 0)
299 uid = pw->pw_uid;
300 set_tkt_string(uid);
303 if (strcmp(user, "root") == 0 && getuid() != 0)
305 pw = getpwuid(getuid());
306 if (pw != 0)
308 name = strdup(pw->pw_name);
309 inst = "root";
312 else
314 name = user;
315 inst = "";
318 ret = krb4_auth(pamh, flags, name, inst, conv);
321 * The realm was lost inside krb_verify_user() so we can't simply do
322 * a krb_kuserok() when inst != "".
324 if (ret == PAM_SUCCESS && inst[0] != 0)
326 uid_t old_euid = geteuid();
327 uid_t old_ruid = getuid();
329 setreuid(0, 0); /* To read ticket file. */
330 if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS)
331 ret = PAM_SERVICE_ERR;
332 else if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
334 setreuid(0, uid); /* To read ~/.klogin. */
335 if (krb_kuserok(name, inst, realm, user) != KSUCCESS)
336 ret = PAM_PERM_DENIED;
339 if (ret != PAM_SUCCESS)
341 dest_tkt(); /* Passwd known, ok to kill ticket. */
342 psyslog(LOG_NOTICE,
343 "%s.%s@%s is not allowed to log in as %s",
344 name, inst, realm, user);
347 setreuid(old_ruid, old_euid);
348 if (getuid() != old_ruid || geteuid() != old_euid)
350 psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d",
351 old_ruid, old_euid, __LINE__);
352 exit(1);
356 if (ret == PAM_SUCCESS)
358 psyslog(LOG_INFO,
359 "%s.%s@%s authenticated as user %s",
360 name, inst, realm, user);
361 if (chown(tkt_string(), uid, -1) == -1)
363 dest_tkt();
364 psyslog(LOG_ALERT , "chown(%s, %d, -1) failed", tkt_string(), uid);
365 exit(1);
370 * Kludge alert!!! Sun dtlogin unlock screen fails to call
371 * pam_setcred(3) with PAM_REFRESH_CRED after a successful
372 * authentication attempt, sic.
374 * This hack is designed as a workaround to that problem.
376 if (ctrl_on(KRB4_REAFSLOG))
377 if (ret == PAM_SUCCESS)
378 pam_sm_setcred(pamh, PAM_REFRESH_CRED, argc, argv);
380 return ret;
383 int
384 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
386 parse_ctrl(argc, argv);
387 ENTRY("pam_sm_setcred");
389 switch (flags & ~PAM_SILENT) {
390 case 0:
391 case PAM_ESTABLISH_CRED:
392 if (k_hasafs())
393 k_setpag();
394 /* Fall through, fill PAG with credentials below. */
395 case PAM_REINITIALIZE_CRED:
396 case PAM_REFRESH_CRED:
397 if (k_hasafs())
399 void *user = 0;
401 if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS)
403 struct passwd *pw = getpwnam((char *)user);
404 if (pw != 0)
405 krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
406 pw->pw_uid, pw->pw_dir);
409 break;
410 case PAM_DELETE_CRED:
411 dest_tkt();
412 if (k_hasafs())
413 k_unlog();
414 break;
415 default:
416 psyslog(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags);
417 break;
420 return PAM_SUCCESS;
424 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
426 parse_ctrl(argc, argv);
427 ENTRY("pam_sm_open_session");
429 return PAM_SUCCESS;
434 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv)
436 parse_ctrl(argc, argv);
437 ENTRY("pam_sm_close_session");
439 /* This isn't really kosher, but it's handy. */
440 pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv);
442 return PAM_SUCCESS;