pam.c revision 57422
1/* 2 * Copyright (c) 1995 - 2000 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,v 1.24 2000/02/18 14:33:06 bg Exp $"); 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 63log_error(int level, const char *format, ...) 64{ 65 va_list args; 66 va_start(args, format); 67 openlog("pam_krb4", LOG_CONS|LOG_PID, LOG_AUTH); 68 vsyslog(level | LOG_AUTH, 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 log_error(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 | LOG_AUTH, format, args); 133 va_end(args); 134 closelog(); 135} 136 137#define ENTRY(f) pdeb("%s() ruid = %d euid = %d", f, 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 if (setreuid(old_ruid, old_euid) != 0) 186 { 187 log_error(LOG_ALERT , "setreuid(%d, %d) failed", old_ruid, old_euid); 188 exit(1); 189 } 190 191 switch(ret) { 192 case KSUCCESS: 193 return PAM_SUCCESS; 194 case KDC_PR_UNKNOWN: 195 return PAM_USER_UNKNOWN; 196 case SKDC_CANT: 197 case SKDC_RETRY: 198 case RD_AP_TIME: 199 return PAM_AUTHINFO_UNAVAIL; 200 default: 201 return PAM_AUTH_ERR; 202 } 203} 204 205static int 206krb4_auth(pam_handle_t *pamh, 207 int flags, 208 const char *name, 209 const char *inst, 210 struct pam_conv *conv) 211{ 212 struct pam_response *resp; 213 char prompt[128]; 214 struct pam_message msg, *pmsg = &msg; 215 int ret; 216 217 if (ctrl_on(KRB4_TRY_FIRST_PASS) || ctrl_on(KRB4_USE_FIRST_PASS)) 218 { 219 char *pass = 0; 220 ret = pam_get_item(pamh, PAM_AUTHTOK, (void **) &pass); 221 if (ret != PAM_SUCCESS) 222 { 223 log_error(LOG_ERR , "pam_get_item returned error to get-password"); 224 return ret; 225 } 226 else if (pass != 0 && verify_pass(pamh, name, inst, pass) == PAM_SUCCESS) 227 return PAM_SUCCESS; 228 else if (ctrl_on(KRB4_USE_FIRST_PASS)) 229 return PAM_AUTHTOK_RECOVERY_ERR; /* Wrong password! */ 230 else 231 /* We tried the first password but it didn't work, cont. */; 232 } 233 234 msg.msg_style = PAM_PROMPT_ECHO_OFF; 235 if (*inst == 0) 236 snprintf(prompt, sizeof(prompt), "%s's Password: ", name); 237 else 238 snprintf(prompt, sizeof(prompt), "%s.%s's Password: ", name, inst); 239 msg.msg = prompt; 240 241 ret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr); 242 if (ret != PAM_SUCCESS) 243 return ret; 244 245 ret = verify_pass(pamh, name, inst, resp->resp); 246 if (ret == PAM_SUCCESS) 247 { 248 memset(resp->resp, 0, strlen(resp->resp)); /* Erase password! */ 249 free(resp->resp); 250 free(resp); 251 } 252 else 253 { 254 pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* Save password. */ 255 /* free(resp->resp); XXX */ 256 /* free(resp); XXX */ 257 } 258 259 return ret; 260} 261 262int 263pam_sm_authenticate(pam_handle_t *pamh, 264 int flags, 265 int argc, 266 const char **argv) 267{ 268 char *user; 269 int ret; 270 struct pam_conv *conv; 271 struct passwd *pw; 272 uid_t uid = -1; 273 const char *name, *inst; 274 275 parse_ctrl(argc, argv); 276 ENTRY("pam_sm_authenticate"); 277 278 ret = pam_get_user(pamh, &user, "login: "); 279 if (ret != PAM_SUCCESS) 280 return ret; 281 282 if (ctrl_on(KRB4_IGNORE_ROOT) && strcmp(user, "root") == 0) 283 return PAM_AUTHINFO_UNAVAIL; 284 285 ret = pam_get_item(pamh, PAM_CONV, (void*)&conv); 286 if (ret != PAM_SUCCESS) 287 return ret; 288 289 pw = getpwnam(user); 290 if (pw != 0) 291 { 292 uid = pw->pw_uid; 293 set_tkt_string(uid); 294 } 295 296 if (strcmp(user, "root") == 0 && getuid() != 0) 297 { 298 pw = getpwuid(getuid()); 299 if (pw != 0) 300 { 301 name = strdup(pw->pw_name); 302 inst = "root"; 303 } 304 } 305 else 306 { 307 name = user; 308 inst = ""; 309 } 310 311 ret = krb4_auth(pamh, flags, name, inst, conv); 312 313 /* 314 * The realm was lost inside krb_verify_user() so we can't simply do 315 * a krb_kuserok() when inst != "". 316 */ 317 if (ret == PAM_SUCCESS && inst[0] != 0) 318 { 319 char realm[REALM_SZ]; 320 uid_t old_euid = geteuid(); 321 uid_t old_ruid = getuid(); 322 323 realm[0] = 0; 324 setreuid(0, 0); /* To read ticket file. */ 325 if (krb_get_tf_fullname(tkt_string(), 0, 0, realm) != KSUCCESS) 326 ret = PAM_SERVICE_ERR; 327 else if (krb_kuserok(name, inst, realm, user) != KSUCCESS) 328 { 329 setreuid(0, uid); /* To read ~/.klogin. */ 330 if (krb_kuserok(name, inst, realm, user) != KSUCCESS) 331 ret = PAM_PERM_DENIED; 332 } 333 334 if (ret != PAM_SUCCESS) 335 { 336 dest_tkt(); /* Passwd known, ok to kill ticket. */ 337 log_error(LOG_NOTICE, 338 "%s.%s@%s is not allowed to log in as %s", 339 name, inst, realm, user); 340 } 341 342 if (setreuid(old_ruid, old_euid) != 0) 343 { 344 log_error(LOG_ALERT , "setreuid(%d, %d) failed", old_ruid, old_euid); 345 exit(1); 346 } 347 } 348 349 if (ret == PAM_SUCCESS) 350 chown(tkt_string(), uid, -1); 351 352 /* Sun dtlogin unlock screen does not call any other pam_* funcs. */ 353 if (ret == PAM_SUCCESS 354 && ctrl_on(KRB4_REAFSLOG) 355 && k_hasafs() 356 && (pw = getpwnam(user)) != 0) 357 krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0, pw->pw_uid, pw->pw_dir); 358 359 return ret; 360} 361 362int 363pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 364{ 365 parse_ctrl(argc, argv); 366 ENTRY("pam_sm_setcred"); 367 pdeb("flags = 0x%x", flags); 368 369 switch (flags & ~PAM_SILENT) { 370 case 0: 371 case PAM_ESTABLISH_CRED: 372 if (k_hasafs()) 373 k_setpag(); 374 /* Fill PAG with credentials below. */ 375 case PAM_REINITIALIZE_CRED: 376 case PAM_REFRESH_CRED: 377 if (k_hasafs()) 378 { 379 void *user = 0; 380 381 if (pam_get_item(pamh, PAM_USER, &user) == PAM_SUCCESS) 382 { 383 struct passwd *pw = getpwnam((char *)user); 384 if (pw != 0) 385 krb_afslog_uid_home(/*cell*/ 0,/*realm_hint*/ 0, 386 pw->pw_uid, pw->pw_dir); 387 } 388 } 389 break; 390 case PAM_DELETE_CRED: 391 dest_tkt(); 392 if (k_hasafs()) 393 k_unlog(); 394 break; 395 default: 396 log_error(LOG_ALERT , "pam_sm_setcred: unknown flags 0x%x", flags); 397 break; 398 } 399 400 return PAM_SUCCESS; 401} 402 403int 404pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 405{ 406 parse_ctrl(argc, argv); 407 ENTRY("pam_sm_open_session"); 408 409 return PAM_SUCCESS; 410} 411 412 413int 414pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char**argv) 415{ 416 parse_ctrl(argc, argv); 417 ENTRY("pam_sm_close_session"); 418 419 /* This isn't really kosher, but it's handy. */ 420 dest_tkt(); 421 if (k_hasafs()) 422 k_unlog(); 423 424 return PAM_SUCCESS; 425} 426