]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/kuserok.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / krb5 / kuserok.c
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 }