Home | History | Annotate | Line # | Download | only in krb5
kuserok.c revision 1.1.1.1
      1 /*	$NetBSD: kuserok.c,v 1.1.1.1 2011/04/13 18:15:36 elric Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2005 Kungliga Tekniska Hgskolan
      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