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. 12 * 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. 16 * 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. 20 * 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. 32 */ 33 34#ifdef HAVE_CONFIG_H 35#include<config.h> 36RCSID("$Id: pam.c 11417 2002-09-09 15:57:24Z joda $"); 37#endif 38 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> 46 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 52 53#include <netinet/in.h> 54#include <krb.h> 55#include <kafs.h> 56 57#if 0 58/* Debugging PAM modules is a royal pain, truss helps. */ 59#define DEBUG(msg) (access(msg " at line", __LINE__)) 60#endif 61 62static void 63psyslog(int level, const char *format, ...) 64{ 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(); 71} 72 73enum { 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. */ 81}; 82 83#define KRB4_DEFAULTS 0 84 85static int ctrl_flags = KRB4_DEFAULTS; 86#define ctrl_on(x) (krb4_args[x].flag & ctrl_flags) 87#define ctrl_off(x) (!ctrl_on(x)) 88 89typedef struct 90{ 91 const char *token; 92 unsigned int flag; 93} krb4_ctrls_t; 94 95static krb4_ctrls_t krb4_args[KRB4_CTRLS] = 96{ 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 }, 103}; 104 105static void 106parse_ctrl(int argc, const char **argv) 107{ 108 int i, j; 109 110 ctrl_flags = KRB4_DEFAULTS; 111 for (i = 0; i < argc; i++) 112 { 113 for (j = 0; j < KRB4_CTRLS; j++) 114 if (strcmp(argv[i], krb4_args[j].token) == 0) 115 break; 116 117 if (j >= KRB4_CTRLS) 118 psyslog(LOG_ALERT, "unrecognized option [%s]", *argv); 119 else 120 ctrl_flags |= krb4_args[j].flag; 121 } 122} 123 124static void 125pdeb(const char *format, ...) 126{ 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(); 135} 136 137#define ENTRY(func) pdeb("%s() flags = %d ruid = %d euid = %d", func, flags, getuid(), geteuid()) 138 139static void 140set_tkt_string(uid_t uid) 141{ 142 char buf[128]; 143 144 snprintf(buf, sizeof(buf), "%s%u", TKT_ROOT, (unsigned)uid); 145 krb_set_tkt_string(buf); 146 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) 151 { 152 pam_putenv(pamh, var); 153 } 154#endif 155 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) 159 { 160 char *var = malloc(sizeof(buf)); 161 snprintf(var, sizeof(buf), "KRBTKFILE=%s", tkt_string()); 162 putenv(var); 163 /* free(var); XXX */ 164 } 165} 166 167static int 168verify_pass(pam_handle_t *pamh, 169 const char *name, 170 const char *inst, 171 const char *pass) 172{ 173 char realm[REALM_SZ]; 174 int ret, krb_verify, old_euid, old_ruid; 175 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) 190 { 191 psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d", 192 old_ruid, old_euid, __LINE__); 193 exit(1); 194 } 195 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; 207 } 208} 209 210static int 211krb4_auth(pam_handle_t *pamh, 212 int flags, 213 const char *name, 214 const char *inst, 215 struct pam_conv *conv) 216{ 217 struct pam_response *resp; 218 char prompt[128]; 219 struct pam_message msg, *pmsg = &msg; 220 int ret; 221 222 if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS)) 223 { 224 char *pass = 0; 225 ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass); 226 if (ret != PAM_SUCCESS) 227 { 228 psyslog(LOG_ERR , "pam_get_item returned error to get-password"); 229 return ret; 230 } 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. */; 237 } 238 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; 245 246 ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr); 247 if (ret != PAM_SUCCESS) 248 return ret; 249 250 ret = verify_pass(pamh, name, inst, resp->resp); 251 if (ret == PAM_SUCCESS) 252 { 253 memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */ 254 free(resp->resp); 255 free(resp); 256 } 257 else 258 { 259 pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */ 260 /* free(resp->resp); XXX */ 261 /* free(resp); XXX */ 262 } 263 264 return ret; 265} 266 267int 268pam_sm_authenticate(pam_handle_t *pamh, 269 int flags, 270 int argc, 271 const char **argv) 272{ 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; 281 282 parse_ctrl(argc, argv); 283 ENTRY("pam_sm_authenticate"); 284 285 ret = pam_get_user(pamh, &user, "login: "); 286 if (ret != PAM_SUCCESS) 287 return ret; 288 289 if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0) 290 return PAM_AUTHINFO_UNAVAIL; 291 292 ret = pam_get_item(pamh, PAM_CONV, (void*)&conv); 293 if (ret != PAM_SUCCESS) 294 return ret; 295 296 pw = getpwnam(user); 297 if (pw != 0) 298 { 299 uid = pw->pw_uid; 300 set_tkt_string(uid); 301 } 302 303 if (strcmp(user, "root") == 0 && getuid() != 0) 304 { 305 pw = getpwuid(getuid()); 306 if (pw != 0) 307 { 308 name = strdup(pw->pw_name); 309 inst = "root"; 310 } 311 } 312 else 313 { 314 name = user; 315 inst = ""; 316 } 317 318 ret = krb4_auth(pamh, flags, name, inst, conv); 319 320 /* 321 * The realm was lost inside krb_verify_user() so we can't simply do 322 * a krb_kuserok() when inst != "". 323 */ 324 if (ret == PAM_SUCCESS && inst[0] != 0) 325 { 326 uid_t old_euid = geteuid(); 327 uid_t old_ruid = getuid(); 328 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) 333 { 334 setreuid(0, uid); /* To read ~/.klogin. */ 335 if (krb_kuserok(name, inst, realm, user) != KSUCCESS) 336 ret = PAM_PERM_DENIED; 337 } 338 339 if (ret != PAM_SUCCESS) 340 { 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); 345 } 346 347 setreuid(old_ruid, old_euid); 348 if (getuid() != old_ruid || geteuid() != old_euid) 349 { 350 psyslog(LOG_ALERT , "setreuid(%d, %d) failed at line %d", 351 old_ruid, old_euid, __LINE__); 352 exit(1); 353 } 354 } 355 356 if (ret == PAM_SUCCESS) 357 { 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) 362 { 363 dest_tkt(); 364 psyslog(LOG_ALERT , "chown(%s, %d, -1) failed", tkt_string(), uid); 365 exit(1); 366 } 367 } 368 369 /* 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. 373 * 374 * This hack is designed as a workaround to that problem. 375 */ 376 if (ctrl_on(KRB4_REAFSLOG)) 377 if (ret == PAM_SUCCESS) 378 pam_sm_setcred(pamh, PAM_REFRESH_CRED, argc, argv); 379 380 return ret; 381} 382 383int 384pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 385{ 386 parse_ctrl(argc, argv); 387 ENTRY("pam_sm_setcred"); 388 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()) 398 { 399 void *user = 0; 400 401 if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS) 402 { 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); 407 } 408 } 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; 418 } 419 420 return PAM_SUCCESS; 421} 422 423int 424pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 425{ 426 parse_ctrl(argc, argv); 427 ENTRY("pam_sm_open_session"); 428 429 return PAM_SUCCESS; 430} 431 432 433int 434pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv) 435{ 436 parse_ctrl(argc, argv); 437 ENTRY("pam_sm_close_session"); 438 439 /* This isn't really kosher, but it's handy. */ 440 pam_sm_setcred(pamh, PAM_DELETE_CRED, argc, argv); 441 442 return PAM_SUCCESS; 443} 444