pam_krb5.c revision 93984
1/*- 2 * Copyright 2001 Mark R V Murray 3 * Copyright Frank Cusack fcusack@fcusack.com 1999-2000 4 * All rights reserved 5 * Copyright (c) 2002 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * 8 * Portions of this software were developed for the FreeBSD Project by 9 * ThinkSec AS and NAI Labs, the Security Research Division of Network 10 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 11 * ("CBOSS"), as part of the DARPA CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, and the entire permission notice in its entirety, 18 * including the disclaimer of warranties. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. The name of the author may not be used to endorse or promote 23 * products derived from this software without specific prior 24 * written permission. 25 * 26 * ALTERNATIVELY, this product may be distributed under the terms of 27 * the GNU Public License, in which case the provisions of the GPL are 28 * required INSTEAD OF the above restrictions. (This clause is 29 * necessary due to a potential bad interaction between the GPL and 30 * the restrictions contained in a BSD-style copyright.) 31 * 32 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 35 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 36 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 37 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 38 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 42 * OF THE POSSIBILITY OF SUCH DAMAGE. 43 * --------------------------------------------------------------------------- 44 * 45 * This software may contain code from Naomaru Itoi: 46 * 47 * PAM-kerberos5 module Copyright notice. 48 * Naomaru Itoi <itoi@eecs.umich.edu>, June 24, 1997. 49 * 50 * ---------------------------------------------------------------------------- 51 * COPYRIGHT (c) 1997 52 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN 53 * ALL RIGHTS RESERVED 54 * 55 * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE 56 * THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME 57 * OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY 58 * PERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, 59 * WRITTEN PRIOR AUTHORIZATION. IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER 60 * IDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY 61 * PORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED. 62 * 63 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF 64 * MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE 65 * UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 66 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A 67 * PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE 68 * LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 69 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN 70 * CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER 71 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 72 * 73 * PAM-kerberos5 module is written based on PAM-kerberos4 module 74 * by Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team. 75 * Permission to use, copy, modify, distribute this software is hereby 76 * granted, as long as it is granted by Derrick J. Brashear and 77 * M.I.T. kerberos team. Followings are their copyright information. 78 * ---------------------------------------------------------------------------- 79 * 80 * This software may contain code from Derrick J. Brashear: 81 * 82 * 83 * Copyright (c) Derrick J. Brashear, 1996. All rights reserved 84 * 85 * Redistribution and use in source and binary forms, with or without 86 * modification, are permitted provided that the following conditions 87 * are met: 88 * 1. Redistributions of source code must retain the above copyright 89 * notice, and the entire permission notice in its entirety, 90 * including the disclaimer of warranties. 91 * 2. Redistributions in binary form must reproduce the above copyright 92 * notice, this list of conditions and the following disclaimer in the 93 * documentation and/or other materials provided with the distribution. 94 * 3. The name of the author may not be used to endorse or promote 95 * products derived from this software without specific prior 96 * written permission. 97 * 98 * ALTERNATIVELY, this product may be distributed under the terms of 99 * the GNU Public License, in which case the provisions of the GPL are 100 * required INSTEAD OF the above restrictions. (This clause is 101 * necessary due to a potential bad interaction between the GPL and 102 * the restrictions contained in a BSD-style copyright.) 103 * 104 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 105 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 106 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 107 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 108 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 109 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 110 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 111 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 112 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 113 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 114 * OF THE POSSIBILITY OF SUCH DAMAGE. 115 * 116 * ---------------------------------------------------------------------------- 117 * 118 * This software may contain code from MIT Kerberos 5: 119 * 120 * Copyright Notice and Legal Administrivia 121 * ---------------------------------------- 122 * 123 * Copyright (C) 1996 by the Massachusetts Institute of Technology. 124 * 125 * All rights reserved. 126 * 127 * Export of this software from the United States of America may require 128 * a specific license from the United States Government. It is the 129 * responsibility of any person or organization contemplating export to 130 * obtain such a license before exporting. 131 * 132 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 133 * distribute this software and its documentation for any purpose and 134 * without fee is hereby granted, provided that the above copyright 135 * notice appear in all copies and that both that copyright notice and 136 * this permission notice appear in supporting documentation, and that 137 * the name of M.I.T. not be used in advertising or publicity pertaining 138 * to distribution of the software without specific, written prior 139 * permission. M.I.T. makes no representations about the suitability of 140 * this software for any purpose. It is provided "as is" without express 141 * or implied warranty. 142 * 143 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 144 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 145 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 146 * 147 * Individual source code files are copyright MIT, Cygnus Support, 148 * OpenVision, Oracle, Sun Soft, and others. 149 * 150 * Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, 151 * and Zephyr are trademarks of the Massachusetts Institute of Technology 152 * (MIT). No commercial use of these trademarks may be made without 153 * prior written permission of MIT. 154 * 155 * "Commercial use" means use of a name in a product or other for-profit 156 * manner. It does NOT prevent a commercial firm from referring to the 157 * MIT trademarks in order to convey information (although in doing so, 158 * recognition of their trademark status should be given). 159 * 160 * The following copyright and permission notice applies to the 161 * OpenVision Kerberos Administration system located in kadmin/create, 162 * kadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions 163 * of lib/rpc: 164 * 165 * Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved 166 * 167 * WARNING: Retrieving the OpenVision Kerberos Administration system 168 * source code, as described below, indicates your acceptance of the 169 * following terms. If you do not agree to the following terms, do not 170 * retrieve the OpenVision Kerberos administration system. 171 * 172 * You may freely use and distribute the Source Code and Object Code 173 * compiled from it, with or without modification, but this Source 174 * Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY, 175 * INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR 176 * FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER 177 * EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY 178 * FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF 179 * SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR 180 * CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, 181 * WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE 182 * CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY 183 * OTHER REASON. 184 * 185 * OpenVision retains all copyrights in the donated Source Code. OpenVision 186 * also retains copyright to derivative works of the Source Code, whether 187 * created by OpenVision or by a third party. The OpenVision copyright 188 * notice must be preserved if derivative works are made based on the 189 * donated Source Code. 190 * 191 * OpenVision Technologies, Inc. has donated this Kerberos 192 * Administration system to MIT for inclusion in the standard 193 * Kerberos 5 distribution. This donation underscores our 194 * commitment to continuing Kerberos technology development 195 * and our gratitude for the valuable work which has been 196 * performed by MIT and the Kerberos community. 197 * 198 */ 199 200#include <sys/cdefs.h> 201__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_krb5/pam_krb5.c 93984 2002-04-06 19:30:04Z des $"); 202 203#include <sys/types.h> 204#include <sys/stat.h> 205#include <errno.h> 206#include <limits.h> 207#include <pwd.h> 208#include <stdio.h> 209#include <stdlib.h> 210#include <string.h> 211#include <syslog.h> 212#include <unistd.h> 213 214#include <krb5.h> 215#include <com_err.h> 216 217#define PAM_SM_AUTH 218#define PAM_SM_ACCOUNT 219#define PAM_SM_SESSION 220#define PAM_SM_PASSWORD 221 222#include <security/pam_appl.h> 223#include <security/pam_modules.h> 224#include <security/pam_mod_misc.h> 225 226#define COMPAT_HEIMDAL 227/* #define COMPAT_MIT */ 228 229extern krb5_cc_ops krb5_mcc_ops; 230 231static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int); 232static void cleanup_cache(pam_handle_t *, void *, int); 233static const char *compat_princ_component(krb5_context, krb5_principal, int); 234static void compat_free_data_contents(krb5_context, krb5_data *); 235 236#define USER_PROMPT "Username: " 237#define PASSWORD_PROMPT "Password:" 238#define NEW_PASSWORD_PROMPT "New Password:" 239 240enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_CCACHE, PAM_OPT_FORWARDABLE, PAM_OPT_NO_CCACHE, PAM_OPT_REUSE_CCACHE }; 241 242static struct opttab other_options[] = { 243 { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, 244 { "ccache", PAM_OPT_CCACHE }, 245 { "forwardable", PAM_OPT_FORWARDABLE }, 246 { "no_ccache", PAM_OPT_NO_CCACHE }, 247 { "reuse_ccache", PAM_OPT_REUSE_CCACHE }, 248 { NULL, 0 } 249}; 250 251/* 252 * authentication management 253 */ 254PAM_EXTERN int 255pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 256{ 257 krb5_error_code krbret; 258 krb5_context pam_context; 259 krb5_creds creds; 260 krb5_principal princ; 261 krb5_ccache ccache, ccache_check; 262 krb5_get_init_creds_opt opts; 263 struct options options; 264 struct passwd *pwd; 265 int retval; 266 const char *sourceuser, *user, *pass, *service; 267 char *principal, *princ_name, *cache_name, luser[32], *srvdup; 268 269 pam_std_option(&options, other_options, argc, argv); 270 271 PAM_LOG("Options processed"); 272 273 retval = pam_get_user(pamh, &user, USER_PROMPT); 274 if (retval != PAM_SUCCESS) 275 PAM_RETURN(retval); 276 277 PAM_LOG("Got user: %s", user); 278 279 retval = pam_get_item(pamh, PAM_RUSER, (const void **)&sourceuser); 280 if (retval != PAM_SUCCESS) 281 PAM_RETURN(retval); 282 283 PAM_LOG("Got ruser: %s", sourceuser); 284 285 service = NULL; 286 pam_get_item(pamh, PAM_SERVICE, (const void **)&service); 287 if (service == NULL) 288 service = "unknown"; 289 290 PAM_LOG("Got service: %s", service); 291 292 krbret = krb5_init_context(&pam_context); 293 if (krbret != 0) { 294 PAM_VERBOSE_ERROR("Kerberos 5 error"); 295 PAM_RETURN(PAM_SERVICE_ERR); 296 } 297 298 PAM_LOG("Context initialised"); 299 300 krb5_get_init_creds_opt_init(&opts); 301 302 if (pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)) 303 krb5_get_init_creds_opt_set_forwardable(&opts, 1); 304 305 PAM_LOG("Credentials initialised"); 306 307 krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE); 308 if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) { 309 PAM_VERBOSE_ERROR("Kerberos 5 error"); 310 retval = PAM_SERVICE_ERR; 311 goto cleanup3; 312 } 313 314 PAM_LOG("Done krb5_cc_register()"); 315 316 /* Get principal name */ 317 if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) 318 asprintf(&principal, "%s/%s", sourceuser, user); 319 else 320 principal = strdup(user); 321 322 PAM_LOG("Created principal: %s", principal); 323 324 krbret = krb5_parse_name(pam_context, principal, &princ); 325 free(principal); 326 if (krbret != 0) { 327 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret)); 328 PAM_VERBOSE_ERROR("Kerberos 5 error"); 329 retval = PAM_SERVICE_ERR; 330 goto cleanup3; 331 } 332 333 PAM_LOG("Done krb5_parse_name()"); 334 335 /* Now convert the principal name into something human readable */ 336 princ_name = NULL; 337 krbret = krb5_unparse_name(pam_context, princ, &princ_name); 338 if (krbret != 0) { 339 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret)); 340 PAM_VERBOSE_ERROR("Kerberos 5 error"); 341 retval = PAM_SERVICE_ERR; 342 goto cleanup2; 343 } 344 345 PAM_LOG("Got principal: %s", princ_name); 346 347 /* Get password */ 348 retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); 349 if (retval != PAM_SUCCESS) 350 goto cleanup2; 351 352 PAM_LOG("Got password"); 353 354 /* Verify the local user exists (AFTER getting the password) */ 355 if (strchr(user, '@')) { 356 /* get a local account name for this principal */ 357 krbret = krb5_aname_to_localname(pam_context, princ, 358 sizeof(luser), luser); 359 if (krbret != 0) { 360 PAM_VERBOSE_ERROR("Kerberos 5 error"); 361 PAM_LOG("Error krb5_aname_to_localname(): %s", 362 error_message(krbret)); 363 retval = PAM_USER_UNKNOWN; 364 goto cleanup2; 365 } 366 367 retval = pam_set_item(pamh, PAM_USER, luser); 368 if (retval != PAM_SUCCESS) 369 goto cleanup2; 370 371 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 372 if (retval != PAM_SUCCESS) 373 goto cleanup2; 374 375 PAM_LOG("PAM_USER Redone"); 376 } 377 378 pwd = getpwnam(user); 379 if (pwd == NULL) { 380 retval = PAM_USER_UNKNOWN; 381 goto cleanup2; 382 } 383 384 PAM_LOG("Done getpwnam()"); 385 386 /* Get a TGT */ 387 memset(&creds, 0, sizeof(krb5_creds)); 388 krbret = krb5_get_init_creds_password(pam_context, &creds, princ, 389 pass, NULL, pamh, 0, NULL, &opts); 390 if (krbret != 0) { 391 PAM_VERBOSE_ERROR("Kerberos 5 error"); 392 PAM_LOG("Error krb5_get_init_creds_password(): %s", 393 error_message(krbret)); 394 retval = PAM_AUTH_ERR; 395 goto cleanup2; 396 } 397 398 PAM_LOG("Got TGT"); 399 400 /* Generate a unique cache_name */ 401 asprintf(&cache_name, "MEMORY:/tmp/%s.%d", service, getpid()); 402 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache); 403 free(cache_name); 404 if (krbret != 0) { 405 PAM_VERBOSE_ERROR("Kerberos 5 error"); 406 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret)); 407 retval = PAM_SERVICE_ERR; 408 goto cleanup; 409 } 410 krbret = krb5_cc_initialize(pam_context, ccache, princ); 411 if (krbret != 0) { 412 PAM_VERBOSE_ERROR("Kerberos 5 error"); 413 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret)); 414 retval = PAM_SERVICE_ERR; 415 goto cleanup; 416 } 417 krbret = krb5_cc_store_cred(pam_context, ccache, &creds); 418 if (krbret != 0) { 419 PAM_VERBOSE_ERROR("Kerberos 5 error"); 420 PAM_LOG("Error krb5_cc_store_cred(): %s", error_message(krbret)); 421 krb5_cc_destroy(pam_context, ccache); 422 retval = PAM_SERVICE_ERR; 423 goto cleanup; 424 } 425 426 PAM_LOG("Credentials stashed"); 427 428 /* Verify them */ 429 if ((srvdup = strdup(service)) == NULL) { 430 retval = PAM_BUF_ERR; 431 goto cleanup; 432 } 433 krbret = verify_krb_v5_tgt(pam_context, ccache, srvdup, 434 pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)); 435 free(srvdup); 436 if (krbret == -1) { 437 PAM_VERBOSE_ERROR("Kerberos 5 error"); 438 krb5_cc_destroy(pam_context, ccache); 439 retval = PAM_AUTH_ERR; 440 goto cleanup; 441 } 442 443 PAM_LOG("Credentials stash verified"); 444 445 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_check); 446 if (retval == PAM_SUCCESS) { 447 krb5_cc_destroy(pam_context, ccache); 448 PAM_VERBOSE_ERROR("Kerberos 5 error"); 449 retval = PAM_AUTH_ERR; 450 goto cleanup; 451 } 452 453 PAM_LOG("Credentials stash not pre-existing"); 454 455 retval = pam_set_data(pamh, "ccache", ccache, cleanup_cache); 456 if (retval != 0) { 457 krb5_cc_destroy(pam_context, ccache); 458 PAM_VERBOSE_ERROR("Kerberos 5 error"); 459 retval = PAM_SERVICE_ERR; 460 goto cleanup; 461 } 462 463 PAM_LOG("Credentials stash saved"); 464 465cleanup: 466 krb5_free_cred_contents(pam_context, &creds); 467 PAM_LOG("Done cleanup"); 468cleanup2: 469 krb5_free_principal(pam_context, princ); 470 PAM_LOG("Done cleanup2"); 471cleanup3: 472 if (princ_name) 473 free(princ_name); 474 475 krb5_free_context(pam_context); 476 477 PAM_LOG("Done cleanup3"); 478 479 if (retval != PAM_SUCCESS) 480 PAM_VERBOSE_ERROR("Kerberos 5 refuses you"); 481 482 PAM_RETURN(retval); 483} 484 485PAM_EXTERN int 486pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 487{ 488 489 krb5_error_code krbret; 490 krb5_context pam_context; 491 krb5_principal princ; 492 krb5_creds creds; 493 krb5_ccache ccache_temp, ccache_perm; 494 krb5_cc_cursor cursor; 495 struct options options; 496 struct passwd *pwd = NULL; 497 int retval; 498 char *user; 499 char *cache_name, *cache_env_name, *p, *q; 500 501 uid_t euid; 502 gid_t egid; 503 504 pam_std_option(&options, other_options, argc, argv); 505 506 PAM_LOG("Options processed"); 507 508 if (flags & PAM_DELETE_CRED) 509 PAM_RETURN(PAM_SUCCESS); 510 511 if (flags & PAM_REFRESH_CRED) 512 PAM_RETURN(PAM_SUCCESS); 513 514 if (flags & PAM_REINITIALIZE_CRED) 515 PAM_RETURN(PAM_SUCCESS); 516 517 if (!(flags & PAM_ESTABLISH_CRED)) 518 PAM_RETURN(PAM_SERVICE_ERR); 519 520 PAM_LOG("Establishing credentials"); 521 522 /* Get username */ 523 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 524 if (retval != PAM_SUCCESS) 525 PAM_RETURN(retval); 526 527 PAM_LOG("Got user: %s", user); 528 529 krbret = krb5_init_context(&pam_context); 530 if (krbret != 0) { 531 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret)); 532 PAM_RETURN(PAM_SERVICE_ERR); 533 } 534 535 PAM_LOG("Context initialised"); 536 537 euid = geteuid(); /* Usually 0 */ 538 egid = getegid(); 539 540 PAM_LOG("Got euid, egid: %d %d", euid, egid); 541 542 /* Retrieve the cache name */ 543 retval = pam_get_data(pamh, "ccache", (const void **)&ccache_temp); 544 if (retval != PAM_SUCCESS) 545 goto cleanup3; 546 547 /* Get the uid. This should exist. */ 548 pwd = getpwnam(user); 549 if (pwd == NULL) { 550 retval = PAM_USER_UNKNOWN; 551 goto cleanup3; 552 } 553 554 PAM_LOG("Done getpwnam()"); 555 556 /* Avoid following a symlink as root */ 557 if (setegid(pwd->pw_gid)) { 558 retval = PAM_SERVICE_ERR; 559 goto cleanup3; 560 } 561 if (seteuid(pwd->pw_uid)) { 562 retval = PAM_SERVICE_ERR; 563 goto cleanup3; 564 } 565 566 PAM_LOG("Done setegid() & seteuid()"); 567 568 /* Get the cache name */ 569 cache_name = NULL; 570 pam_test_option(&options, PAM_OPT_CCACHE, &cache_name); 571 if (cache_name == NULL) 572 asprintf(&cache_name, "FILE:/tmp/krb5cc_%d", pwd->pw_uid); 573 574 p = calloc(PATH_MAX + 16, sizeof(char)); 575 q = cache_name; 576 577 if (p == NULL) { 578 PAM_LOG("Error malloc(): failure"); 579 retval = PAM_BUF_ERR; 580 goto cleanup3; 581 } 582 cache_name = p; 583 584 /* convert %u and %p */ 585 while (*q) { 586 if (*q == '%') { 587 q++; 588 if (*q == 'u') { 589 sprintf(p, "%d", pwd->pw_uid); 590 p += strlen(p); 591 } 592 else if (*q == 'p') { 593 sprintf(p, "%d", getpid()); 594 p += strlen(p); 595 } 596 else { 597 /* Not a special token */ 598 *p++ = '%'; 599 q--; 600 } 601 q++; 602 } 603 else { 604 *p++ = *q++; 605 } 606 } 607 608 PAM_LOG("Got cache_name: %s", cache_name); 609 610 /* Initialize the new ccache */ 611 krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ); 612 if (krbret != 0) { 613 PAM_LOG("Error krb5_cc_get_principal(): %s", 614 error_message(krbret)); 615 retval = PAM_SERVICE_ERR; 616 goto cleanup3; 617 } 618 krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm); 619 if (krbret != 0) { 620 PAM_LOG("Error krb5_cc_resolve(): %s", error_message(krbret)); 621 retval = PAM_SERVICE_ERR; 622 goto cleanup2; 623 } 624 krbret = krb5_cc_initialize(pam_context, ccache_perm, princ); 625 if (krbret != 0) { 626 PAM_LOG("Error krb5_cc_initialize(): %s", error_message(krbret)); 627 retval = PAM_SERVICE_ERR; 628 goto cleanup2; 629 } 630 631 PAM_LOG("Cache initialised"); 632 633 /* Prepare for iteration over creds */ 634 krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor); 635 if (krbret != 0) { 636 PAM_LOG("Error krb5_cc_start_seq_get(): %s", error_message(krbret)); 637 krb5_cc_destroy(pam_context, ccache_perm); 638 retval = PAM_SERVICE_ERR; 639 goto cleanup2; 640 } 641 642 PAM_LOG("Prepared for iteration"); 643 644 /* Copy the creds (should be two of them) */ 645 while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp, 646 &cursor, &creds) == 0)) { 647 krbret = krb5_cc_store_cred(pam_context, ccache_perm, &creds); 648 if (krbret != 0) { 649 PAM_LOG("Error krb5_cc_store_cred(): %s", 650 error_message(krbret)); 651 krb5_cc_destroy(pam_context, ccache_perm); 652 krb5_free_cred_contents(pam_context, &creds); 653 retval = PAM_SERVICE_ERR; 654 goto cleanup2; 655 } 656 krb5_free_cred_contents(pam_context, &creds); 657 PAM_LOG("Iteration"); 658 } 659 krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor); 660 661 PAM_LOG("Done iterating"); 662 663 if (strstr(cache_name, "FILE:") == cache_name) { 664 if (chown(&cache_name[5], pwd->pw_uid, pwd->pw_gid) == -1) { 665 PAM_LOG("Error chown(): %s", strerror(errno)); 666 krb5_cc_destroy(pam_context, ccache_perm); 667 retval = PAM_SERVICE_ERR; 668 goto cleanup2; 669 } 670 PAM_LOG("Done chown()"); 671 672 if (chmod(&cache_name[5], (S_IRUSR | S_IWUSR)) == -1) { 673 PAM_LOG("Error chmod(): %s", strerror(errno)); 674 krb5_cc_destroy(pam_context, ccache_perm); 675 retval = PAM_SERVICE_ERR; 676 goto cleanup2; 677 } 678 PAM_LOG("Done chmod()"); 679 } 680 681 krb5_cc_close(pam_context, ccache_perm); 682 683 PAM_LOG("Cache closed"); 684 685 cache_env_name = malloc(strlen(cache_name) + 12); 686 if (!cache_env_name) { 687 PAM_LOG("Error malloc(): failure"); 688 krb5_cc_destroy(pam_context, ccache_perm); 689 retval = PAM_BUF_ERR; 690 goto cleanup2; 691 } 692 693 sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name); 694 if ((retval = pam_putenv(pamh, cache_env_name)) != 0) { 695 PAM_LOG("Error pam_putenv(): %s", pam_strerror(pamh, retval)); 696 krb5_cc_destroy(pam_context, ccache_perm); 697 retval = PAM_SERVICE_ERR; 698 goto cleanup2; 699 } 700 701 PAM_LOG("Environment done: KRB5CCNAME=%s", cache_name); 702 703cleanup2: 704 krb5_free_principal(pam_context, princ); 705 PAM_LOG("Done cleanup2"); 706cleanup3: 707 krb5_free_context(pam_context); 708 PAM_LOG("Done cleanup3"); 709 710 seteuid(euid); 711 setegid(egid); 712 713 PAM_LOG("Done seteuid() & setegid()"); 714 715 PAM_RETURN(retval); 716} 717 718/* 719 * account management 720 */ 721PAM_EXTERN int 722pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv) 723{ 724 krb5_error_code krbret; 725 krb5_context pam_context; 726 krb5_ccache ccache; 727 krb5_principal princ; 728 struct options options; 729 int retval; 730 const char *user; 731 732 pam_std_option(&options, other_options, argc, argv); 733 734 PAM_LOG("Options processed"); 735 736 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 737 if (retval != PAM_SUCCESS) 738 PAM_RETURN(retval); 739 740 PAM_LOG("Got user: %s", user); 741 742 retval = pam_get_data(pamh, "ccache", (const void **)&ccache); 743 if (retval != PAM_SUCCESS) 744 PAM_RETURN(PAM_SUCCESS); 745 746 PAM_LOG("Got ccache"); 747 748 krbret = krb5_init_context(&pam_context); 749 if (krbret != 0) { 750 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret)); 751 PAM_RETURN(PAM_PERM_DENIED); 752 } 753 754 PAM_LOG("Context initialised"); 755 756 krbret = krb5_cc_get_principal(pam_context, ccache, &princ); 757 if (krbret != 0) { 758 PAM_LOG("Error krb5_cc_get_principal(): %s", error_message(krbret)); 759 retval = PAM_PERM_DENIED;; 760 goto cleanup; 761 } 762 763 PAM_LOG("Got principal"); 764 765 if (krb5_kuserok(pam_context, princ, user)) 766 retval = PAM_SUCCESS; 767 else 768 retval = PAM_PERM_DENIED; 769 krb5_free_principal(pam_context, princ); 770 771 PAM_LOG("Done kuserok()"); 772 773cleanup: 774 krb5_free_context(pam_context); 775 PAM_LOG("Done cleanup"); 776 777 PAM_RETURN(retval); 778 779} 780 781/* 782 * session management 783 * 784 * logging only 785 */ 786PAM_EXTERN int 787pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 788{ 789 struct options options; 790 791 pam_std_option(&options, NULL, argc, argv); 792 793 PAM_LOG("Options processed"); 794 795 PAM_RETURN(PAM_SUCCESS); 796} 797 798PAM_EXTERN int 799pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv) 800{ 801 struct options options; 802 803 pam_std_option(&options, NULL, argc, argv); 804 805 PAM_LOG("Options processed"); 806 807 PAM_RETURN(PAM_SUCCESS); 808} 809 810/* 811 * password management 812 */ 813PAM_EXTERN int 814pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) 815{ 816 krb5_error_code krbret; 817 krb5_context pam_context; 818 krb5_creds creds; 819 krb5_principal princ; 820 krb5_get_init_creds_opt opts; 821 krb5_data result_code_string, result_string; 822 struct options options; 823 int result_code, retval; 824 const char *user, *pass; 825 char *princ_name, *passdup; 826 827 pam_std_option(&options, other_options, argc, argv); 828 829 PAM_LOG("Options processed"); 830 831 if (!(flags & PAM_UPDATE_AUTHTOK)) 832 PAM_RETURN(PAM_AUTHTOK_ERR); 833 834 retval = pam_get_item(pamh, PAM_USER, (const void **)&user); 835 if (retval != PAM_SUCCESS) 836 PAM_RETURN(retval); 837 838 PAM_LOG("Got user: %s", user); 839 840 krbret = krb5_init_context(&pam_context); 841 if (krbret != 0) { 842 PAM_LOG("Error krb5_init_context(): %s", error_message(krbret)); 843 PAM_RETURN(PAM_SERVICE_ERR); 844 } 845 846 PAM_LOG("Context initialised"); 847 848 krb5_get_init_creds_opt_init(&opts); 849 850 PAM_LOG("Credentials options initialised"); 851 852 /* Get principal name */ 853 krbret = krb5_parse_name(pam_context, user, &princ); 854 if (krbret != 0) { 855 PAM_LOG("Error krb5_parse_name(): %s", error_message(krbret)); 856 retval = PAM_USER_UNKNOWN; 857 goto cleanup3; 858 } 859 860 /* Now convert the principal name into something human readable */ 861 princ_name = NULL; 862 krbret = krb5_unparse_name(pam_context, princ, &princ_name); 863 if (krbret != 0) { 864 PAM_LOG("Error krb5_unparse_name(): %s", error_message(krbret)); 865 retval = PAM_SERVICE_ERR; 866 goto cleanup2; 867 } 868 869 PAM_LOG("Got principal: %s", princ_name); 870 871 /* Get password */ 872 retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass, PASSWORD_PROMPT); 873 if (retval != PAM_SUCCESS) 874 goto cleanup2; 875 876 PAM_LOG("Got password"); 877 878 memset(&creds, 0, sizeof(krb5_creds)); 879 krbret = krb5_get_init_creds_password(pam_context, &creds, princ, 880 pass, NULL, pamh, 0, "kadmin/changepw", &opts); 881 if (krbret != 0) { 882 PAM_LOG("Error krb5_get_init_creds_password()", 883 error_message(krbret)); 884 retval = PAM_AUTH_ERR; 885 goto cleanup2; 886 } 887 888 PAM_LOG("Credentials established"); 889 890 /* Now get the new password */ 891 for (;;) { 892 retval = pam_get_authtok(pamh, 893 PAM_AUTHTOK, &pass, NEW_PASSWORD_PROMPT); 894 if (retval != PAM_TRY_AGAIN) 895 break; 896 pam_error(pamh, "Mismatch; try again, EOF to quit."); 897 } 898 if (retval != PAM_SUCCESS) 899 goto cleanup; 900 901 PAM_LOG("Got new password"); 902 903 /* Change it */ 904 if ((passdup = strdup(pass)) == NULL) { 905 retval = PAM_BUF_ERR; 906 goto cleanup; 907 } 908 krbret = krb5_change_password(pam_context, &creds, passdup, 909 &result_code, &result_code_string, &result_string); 910 free(passdup); 911 if (krbret != 0) { 912 PAM_LOG("Error krb5_change_password(): %s", 913 error_message(krbret)); 914 retval = PAM_AUTHTOK_ERR; 915 goto cleanup; 916 } 917 if (result_code) { 918 PAM_LOG("Error krb5_change_password(): (result_code)"); 919 retval = PAM_AUTHTOK_ERR; 920 goto cleanup; 921 } 922 923 PAM_LOG("Password changed"); 924 925 if (result_string.data) 926 free(result_string.data); 927 if (result_code_string.data) 928 free(result_code_string.data); 929 930cleanup: 931 krb5_free_cred_contents(pam_context, &creds); 932 PAM_LOG("Done cleanup"); 933cleanup2: 934 krb5_free_principal(pam_context, princ); 935 PAM_LOG("Done cleanup2"); 936cleanup3: 937 if (princ_name) 938 free(princ_name); 939 940 krb5_free_context(pam_context); 941 942 PAM_LOG("Done cleanup3"); 943 944 PAM_RETURN(retval); 945} 946 947PAM_MODULE_ENTRY("pam_krb5"); 948 949/* 950 * This routine with some modification is from the MIT V5B6 appl/bsd/login.c 951 * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services 952 * for Debian. 953 * 954 * Verify the Kerberos ticket-granting ticket just retrieved for the 955 * user. If the Kerberos server doesn't respond, assume the user is 956 * trying to fake us out (since we DID just get a TGT from what is 957 * supposedly our KDC). If the host/<host> service is unknown (i.e., 958 * the local keytab doesn't have it), and we cannot find another 959 * service we do have, let her in. 960 * 961 * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. 962 */ 963static int 964verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, 965 char *pam_service, int debug) 966{ 967 krb5_error_code retval; 968 krb5_principal princ; 969 krb5_keyblock *keyblock; 970 krb5_data packet; 971 krb5_auth_context auth_context; 972 char phost[BUFSIZ]; 973 const char *services[3], **service; 974 975 packet.data = 0; 976 977 /* If possible we want to try and verify the ticket we have 978 * received against a keytab. We will try multiple service 979 * principals, including at least the host principal and the PAM 980 * service principal. The host principal is preferred because access 981 * to that key is generally sufficient to compromise root, while the 982 * service key for this PAM service may be less carefully guarded. 983 * It is important to check the keytab first before the KDC so we do 984 * not get spoofed by a fake KDC. 985 */ 986 services[0] = "host"; 987 services[1] = pam_service; 988 services[2] = NULL; 989 keyblock = 0; 990 retval = -1; 991 for (service = &services[0]; *service != NULL; service++) { 992 retval = krb5_sname_to_principal(context, NULL, *service, 993 KRB5_NT_SRV_HST, &princ); 994 if (retval != 0) { 995 if (debug) 996 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_sname_to_principal()", error_message(retval)); 997 return -1; 998 } 999 1000 /* Extract the name directly. */ 1001 strncpy(phost, compat_princ_component(context, princ, 1), 1002 BUFSIZ); 1003 phost[BUFSIZ - 1] = '\0'; 1004 1005 /* 1006 * Do we have service/<host> keys? 1007 * (use default/configured keytab, kvno IGNORE_VNO to get the 1008 * first match, and ignore enctype.) 1009 */ 1010 retval = krb5_kt_read_service_key(context, NULL, princ, 0, 0, 1011 &keyblock); 1012 if (retval != 0) 1013 continue; 1014 break; 1015 } 1016 if (retval != 0) { /* failed to find key */ 1017 /* Keytab or service key does not exist */ 1018 if (debug) 1019 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_kt_read_service_key()", error_message(retval)); 1020 retval = 0; 1021 goto cleanup; 1022 } 1023 if (keyblock) 1024 krb5_free_keyblock(context, keyblock); 1025 1026 /* Talk to the kdc and construct the ticket. */ 1027 auth_context = NULL; 1028 retval = krb5_mk_req(context, &auth_context, 0, *service, phost, 1029 NULL, ccache, &packet); 1030 if (auth_context) { 1031 krb5_auth_con_free(context, auth_context); 1032 auth_context = NULL; /* setup for rd_req */ 1033 } 1034 if (retval) { 1035 if (debug) 1036 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_mk_req()", error_message(retval)); 1037 retval = -1; 1038 goto cleanup; 1039 } 1040 1041 /* Try to use the ticket. */ 1042 retval = krb5_rd_req(context, &auth_context, &packet, princ, NULL, 1043 NULL, NULL); 1044 if (retval) { 1045 if (debug) 1046 syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_rd_req()", error_message(retval)); 1047 retval = -1; 1048 } 1049 else 1050 retval = 1; 1051 1052cleanup: 1053 if (packet.data) 1054 compat_free_data_contents(context, &packet); 1055 krb5_free_principal(context, princ); 1056 return retval; 1057} 1058 1059/* Free the memory for cache_name. Called by pam_end() */ 1060static void 1061cleanup_cache(pam_handle_t *pamh __unused, void *data, int pam_end_status __unused) 1062{ 1063 krb5_context pam_context; 1064 krb5_ccache ccache; 1065 1066 if (krb5_init_context(&pam_context)) 1067 return; 1068 1069 ccache = (krb5_ccache)data; 1070 krb5_cc_destroy(pam_context, ccache); 1071 krb5_free_context(pam_context); 1072} 1073 1074#ifdef COMPAT_HEIMDAL 1075#ifdef COMPAT_MIT 1076#error This cannot be MIT and Heimdal compatible! 1077#endif 1078#endif 1079 1080#ifndef COMPAT_HEIMDAL 1081#ifndef COMPAT_MIT 1082#error One of COMPAT_MIT and COMPAT_HEIMDAL must be specified! 1083#endif 1084#endif 1085 1086#ifdef COMPAT_HEIMDAL 1087static const char * 1088compat_princ_component(krb5_context context __unused, krb5_principal princ, int n) 1089{ 1090 return princ->name.name_string.val[n]; 1091} 1092 1093static void 1094compat_free_data_contents(krb5_context context __unused, krb5_data * data) 1095{ 1096 krb5_xfree(data->data); 1097} 1098#endif 1099 1100#ifdef COMPAT_MIT 1101static const char * 1102compat_princ_component(krb5_context context, krb5_principal princ, int n) 1103{ 1104 return krb5_princ_component(context, princ, n)->data; 1105} 1106 1107static void 1108compat_free_data_contents(krb5_context context, krb5_data * data) 1109{ 1110 krb5_free_data_contents(context, data); 1111} 1112#endif 1113