2 * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
36 __RCSID("$Heimdal: pam.c 11417 2002-09-09 15:57:24Z joda $"
45 #include <sys/types.h>
48 #include <security/pam_appl.h>
49 #include <security/pam_modules.h>
50 #ifndef PAM_AUTHTOK_RECOVERY_ERR /* Fix linsux typo. */
51 #define PAM_AUTHTOK_RECOVERY_ERR PAM_AUTHTOK_RECOVER_ERR
54 #include <netinet/in.h>
59 /* Debugging PAM modules is a royal pain, truss helps. */
60 #define DEBUG(msg) (access(msg " at line", __LINE__))
64 psyslog(int level
, const char *format
, ...)
67 va_start(args
, format
);
68 openlog("pam_krb4", LOG_PID
, LOG_AUTH
);
69 vsyslog(level
, format
, args
);
81 KRB4_CTRLS
/* Number of ctrl arguments defined. */
84 #define KRB4_DEFAULTS 0
86 static int ctrl_flags
= KRB4_DEFAULTS
;
87 #define ctrl_on(x) (krb4_args[x].flag & ctrl_flags)
88 #define ctrl_off(x) (!ctrl_on(x))
96 static krb4_ctrls_t krb4_args
[KRB4_CTRLS
] =
98 /* KRB4_DEBUG */ { "debug", 0x01 },
99 /* KRB4_USE_FIRST_PASS */ { "use_first_pass", 0x02 },
100 /* KRB4_TRY_FIRST_PASS */ { "try_first_pass", 0x04 },
101 /* KRB4_IGNORE_ROOT */ { "ignore_root", 0x08 },
102 /* KRB4_NO_VERIFY */ { "no_verify", 0x10 },
103 /* KRB4_REAFSLOG */ { "reafslog", 0x20 },
107 parse_ctrl(int argc
, const char **argv
)
111 ctrl_flags
= KRB4_DEFAULTS
;
112 for (i
= 0; i
< argc
; i
++)
114 for (j
= 0; j
< KRB4_CTRLS
; j
++)
115 if (strcmp(argv
[i
], krb4_args
[j
].token
) == 0)
119 psyslog(LOG_ALERT
, "unrecognized option [%s]", *argv
);
121 ctrl_flags
|= krb4_args
[j
].flag
;
126 pdeb(const char *format
, ...)
129 if (ctrl_off(KRB4_DEBUG
))
131 va_start(args
, format
);
132 openlog("pam_krb4", LOG_PID
, LOG_AUTH
);
133 vsyslog(LOG_DEBUG
, format
, args
);
138 #define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid())
141 set_tkt_string(uid_t uid
)
145 snprintf(buf
, sizeof(buf
), "%s%u", TKT_ROOT
, (unsigned)uid
);
146 krb_set_tkt_string(buf
);
149 /* pam_set_data+pam_get_data are not guaranteed to work, grr. */
150 pam_set_data(pamh
, "KRBTKFILE", strdup(t
), cleanup
);
151 if (pam_get_data(pamh
, "KRBTKFILE", (const void**)&tkt
) == PAM_SUCCESS
)
153 pam_putenv(pamh
, var
);
157 /* We don't want to inherit this variable.
158 * If we still do, it must have a sane value. */
159 if (getenv("KRBTKFILE") != 0)
161 char *var
= malloc(sizeof(buf
));
162 snprintf(var
, sizeof(buf
), "KRBTKFILE=%s", tkt_string());
169 verify_pass(pam_handle_t
*pamh
,
174 char realm
[REALM_SZ
];
175 int ret
, krb_verify
, old_euid
, old_ruid
;
177 krb_get_lrealm(realm
, 1);
178 if (ctrl_on(KRB4_NO_VERIFY
))
179 krb_verify
= KRB_VERIFY_SECURE_FAIL
;
181 krb_verify
= KRB_VERIFY_SECURE
;
183 old_euid
= geteuid();
185 ret
= krb_verify_user(name
, inst
, realm
, pass
, krb_verify
, NULL
);
186 pdeb("krb_verify_user(`%s', `%s', `%s', pw, %d, NULL) returns %s",
187 name
, inst
, realm
, krb_verify
,
188 krb_get_err_text(ret
));
189 setreuid(old_ruid
, old_euid
);
190 if (getuid() != old_ruid
|| geteuid() != old_euid
)
192 psyslog(LOG_ALERT
, "setreuid(%d, %d) failed at line %d",
193 old_ruid
, old_euid
, __LINE__
);
201 return PAM_USER_UNKNOWN
;
205 return PAM_AUTHINFO_UNAVAIL
;
212 krb4_auth(pam_handle_t
*pamh
,
216 struct pam_conv
*conv
)
218 struct pam_response
*resp
;
220 struct pam_message msg
, *pmsg
= &msg
;
223 if (ctrl_on(KRB4_TRY_FIRST_PASS
) || ctrl_on(KRB4_USE_FIRST_PASS
))
226 ret
= pam_get_item(pamh
, PAM_AUTHTOK
, (void **) &pass
);
227 if (ret
!= PAM_SUCCESS
)
229 psyslog(LOG_ERR
, "pam_get_item returned error to get-password");
232 else if (pass
!= 0 && verify_pass(pamh
, name
, inst
, pass
) == PAM_SUCCESS
)
234 else if (ctrl_on(KRB4_USE_FIRST_PASS
))
235 return PAM_AUTHTOK_RECOVERY_ERR
; /* Wrong password! */
237 /* We tried the first password but it didn't work, cont. */;
240 msg
.msg_style
= PAM_PROMPT_ECHO_OFF
;
242 snprintf(prompt
, sizeof(prompt
), "%s's Password: ", name
);
244 snprintf(prompt
, sizeof(prompt
), "%s.%s's Password: ", name
, inst
);
247 ret
= conv
->conv(1, &pmsg
, &resp
, conv
->appdata_ptr
);
248 if (ret
!= PAM_SUCCESS
)
251 ret
= verify_pass(pamh
, name
, inst
, resp
->resp
);
252 if (ret
== PAM_SUCCESS
)
254 memset(resp
->resp
, 0, strlen(resp
->resp
)); /* Erase password! */
260 pam_set_item(pamh
, PAM_AUTHTOK
, resp
->resp
); /* Save password. */
261 /* free(resp->resp); XXX */
262 /* free(resp); XXX */
269 pam_sm_authenticate(pam_handle_t
*pamh
,
276 struct pam_conv
*conv
;
279 const char *name
, *inst
;
280 char realm
[REALM_SZ
];
283 parse_ctrl(argc
, argv
);
284 ENTRY("pam_sm_authenticate");
286 ret
= pam_get_user(pamh
, &user
, "login: ");
287 if (ret
!= PAM_SUCCESS
)
290 if (ctrl_on(KRB4_IGNORE_ROOT
) && strcmp(user
, "root") == 0)
291 return PAM_AUTHINFO_UNAVAIL
;
293 ret
= pam_get_item(pamh
, PAM_CONV
, (void*)&conv
);
294 if (ret
!= PAM_SUCCESS
)
304 if (strcmp(user
, "root") == 0 && getuid() != 0)
306 pw
= getpwuid(getuid());
309 name
= strdup(pw
->pw_name
);
319 ret
= krb4_auth(pamh
, flags
, name
, inst
, conv
);
322 * The realm was lost inside krb_verify_user() so we can't simply do
323 * a krb_kuserok() when inst != "".
325 if (ret
== PAM_SUCCESS
&& inst
[0] != 0)
327 uid_t old_euid
= geteuid();
328 uid_t old_ruid
= getuid();
330 setreuid(0, 0); /* To read ticket file. */
331 if (krb_get_tf_fullname(tkt_string(), 0, 0, realm
) != KSUCCESS
)
332 ret
= PAM_SERVICE_ERR
;
333 else if (krb_kuserok(name
, inst
, realm
, user
) != KSUCCESS
)
335 setreuid(0, uid
); /* To read ~/.klogin. */
336 if (krb_kuserok(name
, inst
, realm
, user
) != KSUCCESS
)
337 ret
= PAM_PERM_DENIED
;
340 if (ret
!= PAM_SUCCESS
)
342 dest_tkt(); /* Passwd known, ok to kill ticket. */
344 "%s.%s@%s is not allowed to log in as %s",
345 name
, inst
, realm
, user
);
348 setreuid(old_ruid
, old_euid
);
349 if (getuid() != old_ruid
|| geteuid() != old_euid
)
351 psyslog(LOG_ALERT
, "setreuid(%d, %d) failed at line %d",
352 old_ruid
, old_euid
, __LINE__
);
357 if (ret
== PAM_SUCCESS
)
360 "%s.%s@%s authenticated as user %s",
361 name
, inst
, realm
, user
);
362 if (chown(tkt_string(), uid
, -1) == -1)
365 psyslog(LOG_ALERT
, "chown(%s, %d, -1) failed", tkt_string(), uid
);
371 * Kludge alert!!! Sun dtlogin unlock screen fails to call
372 * pam_setcred(3) with PAM_REFRESH_CRED after a successful
373 * authentication attempt, sic.
375 * This hack is designed as a workaround to that problem.
377 if (ctrl_on(KRB4_REAFSLOG
))
378 if (ret
== PAM_SUCCESS
)
379 pam_sm_setcred(pamh
, PAM_REFRESH_CRED
, argc
, argv
);
385 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
387 parse_ctrl(argc
, argv
);
388 ENTRY("pam_sm_setcred");
390 switch (flags
& ~PAM_SILENT
) {
392 case PAM_ESTABLISH_CRED
:
395 /* Fall through, fill PAG with credentials below. */
396 case PAM_REINITIALIZE_CRED
:
397 case PAM_REFRESH_CRED
:
402 if (pam_get_item(pamh
, PAM_USER
, &user
) == PAM_SUCCESS
)
404 struct passwd
*pw
= getpwnam((char *)user
);
406 krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0,
407 pw
->pw_uid
, pw
->pw_dir
);
411 case PAM_DELETE_CRED
:
417 psyslog(LOG_ALERT
, "pam_sm_setcred: unknown flags 0x%x", flags
);
425 pam_sm_open_session(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
427 parse_ctrl(argc
, argv
);
428 ENTRY("pam_sm_open_session");
435 pam_sm_close_session(pam_handle_t
*pamh
, int flags
, int argc
, const char**argv
)
437 parse_ctrl(argc
, argv
);
438 ENTRY("pam_sm_close_session");
440 /* This isn't really kosher, but it's handy. */
441 pam_sm_setcred(pamh
, PAM_DELETE_CRED
, argc
, argv
);