xref: /freebsd/crypto/heimdal/lib/krb5/kuserok.c (revision 9a14aa017b21c292740c00ee098195cd46642730)
1 /*
2  * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 #include <dirent.h>
36 
37 RCSID("$Id: kuserok.c 16048 2005-09-09 10:33:33Z lha $");
38 
39 /* see if principal is mentioned in the filename access file, return
40    TRUE (in result) if so, FALSE otherwise */
41 
42 static krb5_error_code
43 check_one_file(krb5_context context,
44 	       const char *filename,
45 	       struct passwd *pwd,
46 	       krb5_principal principal,
47 	       krb5_boolean *result)
48 {
49     FILE *f;
50     char buf[BUFSIZ];
51     krb5_error_code ret;
52     struct stat st;
53 
54     *result = FALSE;
55 
56     f = fopen (filename, "r");
57     if (f == NULL)
58 	return errno;
59 
60     /* check type and mode of file */
61     if (fstat(fileno(f), &st) != 0) {
62 	fclose (f);
63 	return errno;
64     }
65     if (S_ISDIR(st.st_mode)) {
66 	fclose (f);
67 	return EISDIR;
68     }
69     if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
70 	fclose (f);
71 	return EACCES;
72     }
73     if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
74 	fclose (f);
75 	return EACCES;
76     }
77 
78     while (fgets (buf, sizeof(buf), f) != NULL) {
79 	krb5_principal tmp;
80 	char *newline = buf + strcspn(buf, "\n");
81 
82 	if(*newline != '\n') {
83 	    int c;
84 	    c = fgetc(f);
85 	    if(c != EOF) {
86 		while(c != EOF && c != '\n')
87 		    c = fgetc(f);
88 		/* line was too long, so ignore it */
89 		continue;
90 	    }
91 	}
92 	*newline = '\0';
93 	ret = krb5_parse_name (context, buf, &tmp);
94 	if (ret)
95 	    continue;
96 	*result = krb5_principal_compare (context, principal, tmp);
97 	krb5_free_principal (context, tmp);
98 	if (*result) {
99 	    fclose (f);
100 	    return 0;
101 	}
102     }
103     fclose (f);
104     return 0;
105 }
106 
107 static krb5_error_code
108 check_directory(krb5_context context,
109 		const char *dirname,
110 		struct passwd *pwd,
111 		krb5_principal principal,
112 		krb5_boolean *result)
113 {
114     DIR *d;
115     struct dirent *dent;
116     char filename[MAXPATHLEN];
117     krb5_error_code ret = 0;
118     struct stat st;
119 
120     *result = FALSE;
121 
122     if(lstat(dirname, &st) < 0)
123 	return errno;
124 
125     if (!S_ISDIR(st.st_mode))
126 	return ENOTDIR;
127 
128     if (st.st_uid != pwd->pw_uid && st.st_uid != 0)
129 	return EACCES;
130     if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
131 	return EACCES;
132 
133     if((d = opendir(dirname)) == NULL)
134 	return errno;
135 
136 #ifdef HAVE_DIRFD
137     {
138 	int fd;
139 	struct stat st2;
140 
141 	fd = dirfd(d);
142 	if(fstat(fd, &st2) < 0) {
143 	    closedir(d);
144 	    return errno;
145 	}
146 	if(st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) {
147 	    closedir(d);
148 	    return EACCES;
149 	}
150     }
151 #endif
152 
153     while((dent = readdir(d)) != NULL) {
154 	if(strcmp(dent->d_name, ".") == 0 ||
155 	   strcmp(dent->d_name, "..") == 0 ||
156 	   dent->d_name[0] == '#' ||			  /* emacs autosave */
157 	   dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
158 	    continue;
159 	snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
160 	ret = check_one_file(context, filename, pwd, principal, result);
161 	if(ret == 0 && *result == TRUE)
162 	    break;
163 	ret = 0; /* don't propagate errors upstream */
164     }
165     closedir(d);
166     return ret;
167 }
168 
169 static krb5_boolean
170 match_local_principals(krb5_context context,
171 		       krb5_principal principal,
172 		       const char *luser)
173 {
174     krb5_error_code ret;
175     krb5_realm *realms, *r;
176     krb5_boolean result = FALSE;
177 
178     /* multi-component principals can never match */
179     if(krb5_principal_get_comp_string(context, principal, 1) != NULL)
180 	return FALSE;
181 
182     ret = krb5_get_default_realms (context, &realms);
183     if (ret)
184 	return FALSE;
185 
186     for (r = realms; *r != NULL; ++r) {
187 	if(strcmp(krb5_principal_get_realm(context, principal),
188 		  *r) != 0)
189 	    continue;
190 	if(strcmp(krb5_principal_get_comp_string(context, principal, 0),
191 		  luser) == 0) {
192 	    result = TRUE;
193 	    break;
194 	}
195     }
196     krb5_free_host_realm (context, realms);
197     return result;
198 }
199 
200 /**
201  * Return TRUE iff `principal' is allowed to login as `luser'.
202  */
203 
204 krb5_boolean KRB5_LIB_FUNCTION
205 krb5_kuserok (krb5_context context,
206 	      krb5_principal principal,
207 	      const char *luser)
208 {
209     char *buf;
210     size_t buflen;
211     struct passwd *pwd;
212     krb5_error_code ret;
213     krb5_boolean result = FALSE;
214 
215     krb5_boolean found_file = FALSE;
216 
217 #ifdef POSIX_GETPWNAM_R
218     char pwbuf[2048];
219     struct passwd pw;
220 
221     if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
222 	return FALSE;
223 #else
224     pwd = getpwnam (luser);
225 #endif
226     if (pwd == NULL)
227 	return FALSE;
228 
229 #define KLOGIN "/.k5login"
230     buflen = strlen(pwd->pw_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */
231     buf = malloc(buflen);
232     if(buf == NULL)
233 	return FALSE;
234     /* check user's ~/.k5login */
235     strlcpy(buf, pwd->pw_dir, buflen);
236     strlcat(buf, KLOGIN, buflen);
237     ret = check_one_file(context, buf, pwd, principal, &result);
238 
239     if(ret == 0 && result == TRUE) {
240 	free(buf);
241 	return TRUE;
242     }
243 
244     if(ret != ENOENT)
245 	found_file = TRUE;
246 
247     strlcat(buf, ".d", buflen);
248     ret = check_directory(context, buf, pwd, principal, &result);
249     free(buf);
250     if(ret == 0 && result == TRUE)
251 	return TRUE;
252 
253     if(ret != ENOENT && ret != ENOTDIR)
254 	found_file = TRUE;
255 
256     /* finally if no files exist, allow all principals matching
257        <localuser>@<LOCALREALM> */
258     if(found_file == FALSE)
259 	return match_local_principals(context, principal, luser);
260 
261     return FALSE;
262 }
263