1 /* 2 * Replacement for krb5_kuserok for testing. 3 * 4 * This is a reimplementation of krb5_kuserok that uses the replacement 5 * getpwnam function and the special passwd struct internal to the fake PAM 6 * module to locate .k5login. The default Kerberos krb5_kuserok always calls 7 * the system getpwnam, which we may not be able to intercept, and will 8 * therefore fail because it can't locate the .k5login file for the test user 9 * (or succeed oddly because it finds some random file on the testing system). 10 * 11 * This implementation is drastically simplified from the Kerberos library 12 * version, and much less secure (which shouldn't matter since it's only 13 * acting on test data). 14 * 15 * This is an optional part of the fake PAM library and can be omitted when 16 * testing modules that don't use Kerberos. 17 * 18 * The canonical version of this file is maintained in the rra-c-util package, 19 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>. 20 * 21 * Written by Russ Allbery <eagle@eyrie.org> 22 * Copyright 2011 23 * The Board of Trustees of the Leland Stanford Junior University 24 * 25 * Permission is hereby granted, free of charge, to any person obtaining a 26 * copy of this software and associated documentation files (the "Software"), 27 * to deal in the Software without restriction, including without limitation 28 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 29 * and/or sell copies of the Software, and to permit persons to whom the 30 * Software is furnished to do so, subject to the following conditions: 31 * 32 * The above copyright notice and this permission notice shall be included in 33 * all copies or substantial portions of the Software. 34 * 35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 38 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 * DEALINGS IN THE SOFTWARE. 42 * 43 * SPDX-License-Identifier: MIT 44 */ 45 46 #include <config.h> 47 #include <portable/krb5.h> 48 #include <portable/pam.h> 49 #include <portable/system.h> 50 51 #include <pwd.h> 52 53 #include <tests/fakepam/pam.h> 54 #include <tests/tap/string.h> 55 56 57 /* 58 * Given a Kerberos principal representing the authenticated identity and the 59 * username of the local account, return true if that principal is authorized 60 * to log on to that account. The principal is authorized if the .k5login 61 * file does not exist and the user matches the localname form of the 62 * principal, or if the file does exist and the principal is listed in it. 63 * 64 * This version retrieves the home directory from the internal fake PAM 65 * library path. 66 */ 67 krb5_boolean 68 krb5_kuserok(krb5_context ctx, krb5_principal princ, const char *user) 69 { 70 char *principal, *path; 71 struct passwd *pwd; 72 FILE *file; 73 krb5_error_code code; 74 char buffer[BUFSIZ]; 75 bool found = false; 76 #ifdef HAVE_PAM_MODUTIL_GETPWNAM 77 struct pam_handle pamh; 78 #endif 79 80 /* 81 * Find .k5login and confirm if it exists. If it doesn't, fall back on 82 * krb5_aname_to_localname. 83 */ 84 #ifdef HAVE_PAM_MODUTIL_GETPWNAM 85 memset(&pamh, 0, sizeof(pamh)); 86 pwd = pam_modutil_getpwnam(&pamh, user); 87 #else 88 pwd = getpwnam(user); 89 #endif 90 if (pwd == NULL) 91 return false; 92 basprintf(&path, "%s/.k5login", pwd->pw_dir); 93 if (access(path, R_OK) < 0) { 94 free(path); 95 code = krb5_aname_to_localname(ctx, princ, sizeof(buffer), buffer); 96 return (code == 0 && strcmp(buffer, user) == 0); 97 } 98 file = fopen(path, "r"); 99 if (file == NULL) { 100 free(path); 101 return false; 102 } 103 free(path); 104 105 /* .k5login exists. Scan it for the principal. */ 106 if (krb5_unparse_name(ctx, princ, &principal) != 0) { 107 fclose(file); 108 return false; 109 } 110 while (!found && (fgets(buffer, sizeof(buffer), file) != NULL)) { 111 if (buffer[strlen(buffer) - 1] == '\n') 112 buffer[strlen(buffer) - 1] = '\0'; 113 if (strcmp(buffer, principal) == 0) 114 found = true; 115 } 116 fclose(file); 117 krb5_free_unparsed_name(ctx, principal); 118 return found; 119 } 120