xref: /minix3/crypto/external/bsd/heimdal/dist/lib/krb5/kuserok.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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
check_one_file(krb5_context context,const char * filename,struct passwd * pwd,krb5_principal principal,krb5_boolean * result)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
check_directory(krb5_context context,const char * dirname,struct passwd * pwd,krb5_principal principal,krb5_boolean * result)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
match_local_principals(krb5_context context,krb5_principal principal,const char * luser)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
krb5_kuserok(krb5_context context,krb5_principal principal,const char * luser)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