1 /* $NetBSD: kuserok.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "krb5_locl.h" 37 #include <dirent.h> 38 39 #ifndef _WIN32 40 41 /* see if principal is mentioned in the filename access file, return 42 TRUE (in result) if so, FALSE otherwise */ 43 44 static krb5_error_code 45 check_one_file(krb5_context context, 46 const char *filename, 47 struct passwd *pwd, 48 krb5_principal principal, 49 krb5_boolean *result) 50 { 51 FILE *f; 52 char buf[BUFSIZ]; 53 krb5_error_code ret; 54 struct stat st; 55 56 *result = FALSE; 57 58 f = fopen (filename, "r"); 59 if (f == NULL) 60 return errno; 61 rk_cloexec_file(f); 62 63 /* check type and mode of file */ 64 if (fstat(fileno(f), &st) != 0) { 65 fclose (f); 66 return errno; 67 } 68 if (S_ISDIR(st.st_mode)) { 69 fclose (f); 70 return EISDIR; 71 } 72 if (st.st_uid != pwd->pw_uid && st.st_uid != 0) { 73 fclose (f); 74 return EACCES; 75 } 76 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { 77 fclose (f); 78 return EACCES; 79 } 80 81 while (fgets (buf, sizeof(buf), f) != NULL) { 82 krb5_principal tmp; 83 char *newline = buf + strcspn(buf, "\n"); 84 85 if(*newline != '\n') { 86 int c; 87 c = fgetc(f); 88 if(c != EOF) { 89 while(c != EOF && c != '\n') 90 c = fgetc(f); 91 /* line was too long, so ignore it */ 92 continue; 93 } 94 } 95 *newline = '\0'; 96 ret = krb5_parse_name (context, buf, &tmp); 97 if (ret) 98 continue; 99 *result = krb5_principal_compare (context, principal, tmp); 100 krb5_free_principal (context, tmp); 101 if (*result) { 102 fclose (f); 103 return 0; 104 } 105 } 106 fclose (f); 107 return 0; 108 } 109 110 static krb5_error_code 111 check_directory(krb5_context context, 112 const char *dirname, 113 struct passwd *pwd, 114 krb5_principal principal, 115 krb5_boolean *result) 116 { 117 DIR *d; 118 struct dirent *dent; 119 char filename[MAXPATHLEN]; 120 krb5_error_code ret = 0; 121 struct stat st; 122 123 *result = FALSE; 124 125 if(lstat(dirname, &st) < 0) 126 return errno; 127 128 if (!S_ISDIR(st.st_mode)) 129 return ENOTDIR; 130 131 if (st.st_uid != pwd->pw_uid && st.st_uid != 0) 132 return EACCES; 133 if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) 134 return EACCES; 135 136 if((d = opendir(dirname)) == NULL) 137 return errno; 138 139 { 140 int fd; 141 struct stat st2; 142 143 fd = dirfd(d); 144 if(fstat(fd, &st2) < 0) { 145 closedir(d); 146 return errno; 147 } 148 if(st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) { 149 closedir(d); 150 return EACCES; 151 } 152 } 153 154 while((dent = readdir(d)) != NULL) { 155 if(strcmp(dent->d_name, ".") == 0 || 156 strcmp(dent->d_name, "..") == 0 || 157 dent->d_name[0] == '#' || /* emacs autosave */ 158 dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */ 159 continue; 160 snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name); 161 ret = check_one_file(context, filename, pwd, principal, result); 162 if(ret == 0 && *result == TRUE) 163 break; 164 ret = 0; /* don't propagate errors upstream */ 165 } 166 closedir(d); 167 return ret; 168 } 169 170 #endif /* !_WIN32 */ 171 172 static krb5_boolean 173 match_local_principals(krb5_context context, 174 krb5_principal principal, 175 const char *luser) 176 { 177 krb5_error_code ret; 178 krb5_realm *realms, *r; 179 krb5_boolean result = FALSE; 180 181 /* multi-component principals can never match */ 182 if(krb5_principal_get_comp_string(context, principal, 1) != NULL) 183 return FALSE; 184 185 ret = krb5_get_default_realms (context, &realms); 186 if (ret) 187 return FALSE; 188 189 for (r = realms; *r != NULL; ++r) { 190 if(strcmp(krb5_principal_get_realm(context, principal), 191 *r) != 0) 192 continue; 193 if(strcmp(krb5_principal_get_comp_string(context, principal, 0), 194 luser) == 0) { 195 result = TRUE; 196 break; 197 } 198 } 199 krb5_free_host_realm (context, realms); 200 return result; 201 } 202 203 /** 204 * This function takes the name of a local user and checks if 205 * principal is allowed to log in as that user. 206 * 207 * The user may have a ~/.k5login file listing principals that are 208 * allowed to login as that user. If that file does not exist, all 209 * principals with a first component identical to the username, and a 210 * realm considered local, are allowed access. 211 * 212 * The .k5login file must contain one principal per line, be owned by 213 * user and not be writable by group or other (but must be readable by 214 * anyone). 215 * 216 * Note that if the file exists, no implicit access rights are given 217 * to user@@LOCALREALM. 218 * 219 * Optionally, a set of files may be put in ~/.k5login.d (a 220 * directory), in which case they will all be checked in the same 221 * manner as .k5login. The files may be called anything, but files 222 * starting with a hash (#) , or ending with a tilde (~) are 223 * ignored. Subdirectories are not traversed. Note that this directory 224 * may not be checked by other Kerberos implementations. 225 * 226 * If no configuration file exists, match user against local domains, 227 * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES. 228 * 229 * @param context Kerberos 5 context. 230 * @param principal principal to check if allowed to login 231 * @param luser local user id 232 * 233 * @return returns TRUE if access should be granted, FALSE otherwise. 234 * 235 * @ingroup krb5_support 236 */ 237 238 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 239 krb5_kuserok (krb5_context context, 240 krb5_principal principal, 241 const char *luser) 242 { 243 #ifndef _WIN32 244 char *buf; 245 size_t buflen; 246 struct passwd *pwd = NULL; 247 char *profile_dir = NULL; 248 krb5_error_code ret; 249 krb5_boolean result = FALSE; 250 251 krb5_boolean found_file = FALSE; 252 253 #ifdef POSIX_GETPWNAM_R 254 char pwbuf[2048]; 255 struct passwd pw; 256 257 if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) 258 return FALSE; 259 #else 260 pwd = getpwnam (luser); 261 #endif 262 if (pwd == NULL) 263 return FALSE; 264 profile_dir = pwd->pw_dir; 265 266 #define KLOGIN "/.k5login" 267 buflen = strlen(profile_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */ 268 buf = malloc(buflen); 269 if(buf == NULL) 270 return FALSE; 271 /* check user's ~/.k5login */ 272 strlcpy(buf, profile_dir, buflen); 273 strlcat(buf, KLOGIN, buflen); 274 ret = check_one_file(context, buf, pwd, principal, &result); 275 276 if(ret == 0 && result == TRUE) { 277 free(buf); 278 return TRUE; 279 } 280 281 if(ret != ENOENT) 282 found_file = TRUE; 283 284 strlcat(buf, ".d", buflen); 285 ret = check_directory(context, buf, pwd, principal, &result); 286 free(buf); 287 if(ret == 0 && result == TRUE) 288 return TRUE; 289 290 if(ret != ENOENT && ret != ENOTDIR) 291 found_file = TRUE; 292 293 /* finally if no files exist, allow all principals matching 294 <localuser>@<LOCALREALM> */ 295 if(found_file == FALSE) 296 return match_local_principals(context, principal, luser); 297 298 return FALSE; 299 #else 300 /* The .k5login file may be on a remote profile and we don't have 301 access to the profile until we have a token handle for the 302 user's credentials. */ 303 return match_local_principals(context, principal, luser); 304 #endif 305 } 306