155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "krb5_locl.h" 35178825Sdfr#include <dirent.h> 3655682Smarkm 37233294Sstas#ifndef _WIN32 3855682Smarkm 39178825Sdfr/* see if principal is mentioned in the filename access file, return 40178825Sdfr TRUE (in result) if so, FALSE otherwise */ 41178825Sdfr 42178825Sdfrstatic krb5_error_code 43233294Sstascheck_one_file(krb5_context context, 44233294Sstas const char *filename, 45178825Sdfr struct passwd *pwd, 46233294Sstas krb5_principal principal, 47178825Sdfr krb5_boolean *result) 48178825Sdfr{ 49178825Sdfr FILE *f; 50178825Sdfr char buf[BUFSIZ]; 51178825Sdfr krb5_error_code ret; 52178825Sdfr struct stat st; 53233294Sstas 54178825Sdfr *result = FALSE; 55178825Sdfr 56178825Sdfr f = fopen (filename, "r"); 57178825Sdfr if (f == NULL) 58178825Sdfr return errno; 59233294Sstas rk_cloexec_file(f); 60233294Sstas 61178825Sdfr /* check type and mode of file */ 62178825Sdfr if (fstat(fileno(f), &st) != 0) { 63178825Sdfr fclose (f); 64178825Sdfr return errno; 65178825Sdfr } 66178825Sdfr if (S_ISDIR(st.st_mode)) { 67178825Sdfr fclose (f); 68178825Sdfr return EISDIR; 69178825Sdfr } 70178825Sdfr if (st.st_uid != pwd->pw_uid && st.st_uid != 0) { 71178825Sdfr fclose (f); 72178825Sdfr return EACCES; 73178825Sdfr } 74178825Sdfr if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { 75178825Sdfr fclose (f); 76178825Sdfr return EACCES; 77178825Sdfr } 78178825Sdfr 79178825Sdfr while (fgets (buf, sizeof(buf), f) != NULL) { 80178825Sdfr krb5_principal tmp; 81178825Sdfr char *newline = buf + strcspn(buf, "\n"); 82178825Sdfr 83178825Sdfr if(*newline != '\n') { 84178825Sdfr int c; 85178825Sdfr c = fgetc(f); 86178825Sdfr if(c != EOF) { 87178825Sdfr while(c != EOF && c != '\n') 88178825Sdfr c = fgetc(f); 89178825Sdfr /* line was too long, so ignore it */ 90178825Sdfr continue; 91178825Sdfr } 92178825Sdfr } 93178825Sdfr *newline = '\0'; 94178825Sdfr ret = krb5_parse_name (context, buf, &tmp); 95178825Sdfr if (ret) 96178825Sdfr continue; 97178825Sdfr *result = krb5_principal_compare (context, principal, tmp); 98178825Sdfr krb5_free_principal (context, tmp); 99178825Sdfr if (*result) { 100178825Sdfr fclose (f); 101178825Sdfr return 0; 102178825Sdfr } 103178825Sdfr } 104178825Sdfr fclose (f); 105178825Sdfr return 0; 106178825Sdfr} 107178825Sdfr 108178825Sdfrstatic krb5_error_code 109233294Sstascheck_directory(krb5_context context, 110233294Sstas const char *dirname, 111178825Sdfr struct passwd *pwd, 112233294Sstas krb5_principal principal, 113178825Sdfr krb5_boolean *result) 114178825Sdfr{ 115178825Sdfr DIR *d; 116178825Sdfr struct dirent *dent; 117178825Sdfr char filename[MAXPATHLEN]; 118178825Sdfr krb5_error_code ret = 0; 119178825Sdfr struct stat st; 120178825Sdfr 121178825Sdfr *result = FALSE; 122178825Sdfr 123178825Sdfr if(lstat(dirname, &st) < 0) 124178825Sdfr return errno; 125178825Sdfr 126178825Sdfr if (!S_ISDIR(st.st_mode)) 127178825Sdfr return ENOTDIR; 128233294Sstas 129178825Sdfr if (st.st_uid != pwd->pw_uid && st.st_uid != 0) 130178825Sdfr return EACCES; 131178825Sdfr if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) 132178825Sdfr return EACCES; 133178825Sdfr 134233294Sstas if((d = opendir(dirname)) == NULL) 135178825Sdfr return errno; 136178825Sdfr 137178825Sdfr { 138178825Sdfr int fd; 139178825Sdfr struct stat st2; 140178825Sdfr 141178825Sdfr fd = dirfd(d); 142178825Sdfr if(fstat(fd, &st2) < 0) { 143178825Sdfr closedir(d); 144178825Sdfr return errno; 145178825Sdfr } 146178825Sdfr if(st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { 147178825Sdfr closedir(d); 148178825Sdfr return EACCES; 149178825Sdfr } 150178825Sdfr } 151178825Sdfr 152178825Sdfr while((dent = readdir(d)) != NULL) { 153178825Sdfr if(strcmp(dent->d_name, ".") == 0 || 154178825Sdfr strcmp(dent->d_name, "..") == 0 || 155178825Sdfr dent->d_name[0] == '#' || /* emacs autosave */ 156178825Sdfr dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */ 157178825Sdfr continue; 158178825Sdfr snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name); 159178825Sdfr ret = check_one_file(context, filename, pwd, principal, result); 160178825Sdfr if(ret == 0 && *result == TRUE) 161178825Sdfr break; 162178825Sdfr ret = 0; /* don't propagate errors upstream */ 163178825Sdfr } 164178825Sdfr closedir(d); 165178825Sdfr return ret; 166178825Sdfr} 167178825Sdfr 168233294Sstas#endif /* !_WIN32 */ 169233294Sstas 170178825Sdfrstatic krb5_boolean 171178825Sdfrmatch_local_principals(krb5_context context, 172178825Sdfr krb5_principal principal, 173178825Sdfr const char *luser) 174178825Sdfr{ 175178825Sdfr krb5_error_code ret; 176178825Sdfr krb5_realm *realms, *r; 177178825Sdfr krb5_boolean result = FALSE; 178233294Sstas 179178825Sdfr /* multi-component principals can never match */ 180178825Sdfr if(krb5_principal_get_comp_string(context, principal, 1) != NULL) 181178825Sdfr return FALSE; 182178825Sdfr 183178825Sdfr ret = krb5_get_default_realms (context, &realms); 184178825Sdfr if (ret) 185178825Sdfr return FALSE; 186233294Sstas 187178825Sdfr for (r = realms; *r != NULL; ++r) { 188178825Sdfr if(strcmp(krb5_principal_get_realm(context, principal), 189178825Sdfr *r) != 0) 190178825Sdfr continue; 191178825Sdfr if(strcmp(krb5_principal_get_comp_string(context, principal, 0), 192178825Sdfr luser) == 0) { 193178825Sdfr result = TRUE; 194178825Sdfr break; 195178825Sdfr } 196178825Sdfr } 197178825Sdfr krb5_free_host_realm (context, realms); 198178825Sdfr return result; 199178825Sdfr} 200178825Sdfr 201178825Sdfr/** 202233294Sstas * This function takes the name of a local user and checks if 203233294Sstas * principal is allowed to log in as that user. 204233294Sstas * 205233294Sstas * The user may have a ~/.k5login file listing principals that are 206233294Sstas * allowed to login as that user. If that file does not exist, all 207233294Sstas * principals with a first component identical to the username, and a 208233294Sstas * realm considered local, are allowed access. 209233294Sstas * 210233294Sstas * The .k5login file must contain one principal per line, be owned by 211233294Sstas * user and not be writable by group or other (but must be readable by 212233294Sstas * anyone). 213233294Sstas * 214233294Sstas * Note that if the file exists, no implicit access rights are given 215233294Sstas * to user@@LOCALREALM. 216233294Sstas * 217233294Sstas * Optionally, a set of files may be put in ~/.k5login.d (a 218233294Sstas * directory), in which case they will all be checked in the same 219233294Sstas * manner as .k5login. The files may be called anything, but files 220233294Sstas * starting with a hash (#) , or ending with a tilde (~) are 221233294Sstas * ignored. Subdirectories are not traversed. Note that this directory 222233294Sstas * may not be checked by other Kerberos implementations. 223233294Sstas * 224233294Sstas * If no configuration file exists, match user against local domains, 225233294Sstas * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES. 226233294Sstas * 227233294Sstas * @param context Kerberos 5 context. 228233294Sstas * @param principal principal to check if allowed to login 229233294Sstas * @param luser local user id 230233294Sstas * 231233294Sstas * @return returns TRUE if access should be granted, FALSE otherwise. 232233294Sstas * 233233294Sstas * @ingroup krb5_support 23455682Smarkm */ 23555682Smarkm 236233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 23755682Smarkmkrb5_kuserok (krb5_context context, 23855682Smarkm krb5_principal principal, 23955682Smarkm const char *luser) 24055682Smarkm{ 241233294Sstas#ifndef _WIN32 242178825Sdfr char *buf; 243178825Sdfr size_t buflen; 244233294Sstas struct passwd *pwd = NULL; 245233294Sstas char *profile_dir = NULL; 24655682Smarkm krb5_error_code ret; 247178825Sdfr krb5_boolean result = FALSE; 24855682Smarkm 249178825Sdfr krb5_boolean found_file = FALSE; 250178825Sdfr 251178825Sdfr#ifdef POSIX_GETPWNAM_R 252178825Sdfr char pwbuf[2048]; 253178825Sdfr struct passwd pw; 254178825Sdfr 255178825Sdfr if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) 256178825Sdfr return FALSE; 257178825Sdfr#else 258178825Sdfr pwd = getpwnam (luser); 259178825Sdfr#endif 260120945Snectar if (pwd == NULL) 261120945Snectar return FALSE; 262233294Sstas profile_dir = pwd->pw_dir; 263120945Snectar 264178825Sdfr#define KLOGIN "/.k5login" 265233294Sstas buflen = strlen(profile_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */ 266178825Sdfr buf = malloc(buflen); 267178825Sdfr if(buf == NULL) 26855682Smarkm return FALSE; 269178825Sdfr /* check user's ~/.k5login */ 270233294Sstas strlcpy(buf, profile_dir, buflen); 271178825Sdfr strlcat(buf, KLOGIN, buflen); 272178825Sdfr ret = check_one_file(context, buf, pwd, principal, &result); 27355682Smarkm 274178825Sdfr if(ret == 0 && result == TRUE) { 275178825Sdfr free(buf); 276178825Sdfr return TRUE; 277178825Sdfr } 27855682Smarkm 279233294Sstas if(ret != ENOENT) 280178825Sdfr found_file = TRUE; 28155682Smarkm 282178825Sdfr strlcat(buf, ".d", buflen); 283178825Sdfr ret = check_directory(context, buf, pwd, principal, &result); 284178825Sdfr free(buf); 285178825Sdfr if(ret == 0 && result == TRUE) 286178825Sdfr return TRUE; 28755682Smarkm 288233294Sstas if(ret != ENOENT && ret != ENOTDIR) 289178825Sdfr found_file = TRUE; 29055682Smarkm 291178825Sdfr /* finally if no files exist, allow all principals matching 292178825Sdfr <localuser>@<LOCALREALM> */ 293178825Sdfr if(found_file == FALSE) 294178825Sdfr return match_local_principals(context, principal, luser); 295178825Sdfr 29655682Smarkm return FALSE; 297233294Sstas#else 298233294Sstas /* The .k5login file may be on a remote profile and we don't have 299233294Sstas access to the profile until we have a token handle for the 300233294Sstas user's credentials. */ 301233294Sstas return match_local_principals(context, principal, luser); 302233294Sstas#endif 30355682Smarkm} 304