155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2005 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
35178825Sdfr#include <dirent.h>
3655682Smarkm
37178825SdfrRCSID("$Id: kuserok.c 16048 2005-09-09 10:33:33Z lha $");
3855682Smarkm
39178825Sdfr/* see if principal is mentioned in the filename access file, return
40178825Sdfr   TRUE (in result) if so, FALSE otherwise */
41178825Sdfr
42178825Sdfrstatic krb5_error_code
43178825Sdfrcheck_one_file(krb5_context context,
44178825Sdfr	       const char *filename,
45178825Sdfr	       struct passwd *pwd,
46178825Sdfr	       krb5_principal principal,
47178825Sdfr	       krb5_boolean *result)
48178825Sdfr{
49178825Sdfr    FILE *f;
50178825Sdfr    char buf[BUFSIZ];
51178825Sdfr    krb5_error_code ret;
52178825Sdfr    struct stat st;
53178825Sdfr
54178825Sdfr    *result = FALSE;
55178825Sdfr
56178825Sdfr    f = fopen (filename, "r");
57178825Sdfr    if (f == NULL)
58178825Sdfr	return errno;
59178825Sdfr
60178825Sdfr    /* check type and mode of file */
61178825Sdfr    if (fstat(fileno(f), &st) != 0) {
62178825Sdfr	fclose (f);
63178825Sdfr	return errno;
64178825Sdfr    }
65178825Sdfr    if (S_ISDIR(st.st_mode)) {
66178825Sdfr	fclose (f);
67178825Sdfr	return EISDIR;
68178825Sdfr    }
69178825Sdfr    if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
70178825Sdfr	fclose (f);
71178825Sdfr	return EACCES;
72178825Sdfr    }
73178825Sdfr    if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
74178825Sdfr	fclose (f);
75178825Sdfr	return EACCES;
76178825Sdfr    }
77178825Sdfr
78178825Sdfr    while (fgets (buf, sizeof(buf), f) != NULL) {
79178825Sdfr	krb5_principal tmp;
80178825Sdfr	char *newline = buf + strcspn(buf, "\n");
81178825Sdfr
82178825Sdfr	if(*newline != '\n') {
83178825Sdfr	    int c;
84178825Sdfr	    c = fgetc(f);
85178825Sdfr	    if(c != EOF) {
86178825Sdfr		while(c != EOF && c != '\n')
87178825Sdfr		    c = fgetc(f);
88178825Sdfr		/* line was too long, so ignore it */
89178825Sdfr		continue;
90178825Sdfr	    }
91178825Sdfr	}
92178825Sdfr	*newline = '\0';
93178825Sdfr	ret = krb5_parse_name (context, buf, &tmp);
94178825Sdfr	if (ret)
95178825Sdfr	    continue;
96178825Sdfr	*result = krb5_principal_compare (context, principal, tmp);
97178825Sdfr	krb5_free_principal (context, tmp);
98178825Sdfr	if (*result) {
99178825Sdfr	    fclose (f);
100178825Sdfr	    return 0;
101178825Sdfr	}
102178825Sdfr    }
103178825Sdfr    fclose (f);
104178825Sdfr    return 0;
105178825Sdfr}
106178825Sdfr
107178825Sdfrstatic krb5_error_code
108178825Sdfrcheck_directory(krb5_context context,
109178825Sdfr		const char *dirname,
110178825Sdfr		struct passwd *pwd,
111178825Sdfr		krb5_principal principal,
112178825Sdfr		krb5_boolean *result)
113178825Sdfr{
114178825Sdfr    DIR *d;
115178825Sdfr    struct dirent *dent;
116178825Sdfr    char filename[MAXPATHLEN];
117178825Sdfr    krb5_error_code ret = 0;
118178825Sdfr    struct stat st;
119178825Sdfr
120178825Sdfr    *result = FALSE;
121178825Sdfr
122178825Sdfr    if(lstat(dirname, &st) < 0)
123178825Sdfr	return errno;
124178825Sdfr
125178825Sdfr    if (!S_ISDIR(st.st_mode))
126178825Sdfr	return ENOTDIR;
127178825Sdfr
128178825Sdfr    if (st.st_uid != pwd->pw_uid && st.st_uid != 0)
129178825Sdfr	return EACCES;
130178825Sdfr    if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
131178825Sdfr	return EACCES;
132178825Sdfr
133178825Sdfr    if((d = opendir(dirname)) == NULL)
134178825Sdfr	return errno;
135178825Sdfr
136178825Sdfr#ifdef HAVE_DIRFD
137178825Sdfr    {
138178825Sdfr	int fd;
139178825Sdfr	struct stat st2;
140178825Sdfr
141178825Sdfr	fd = dirfd(d);
142178825Sdfr	if(fstat(fd, &st2) < 0) {
143178825Sdfr	    closedir(d);
144178825Sdfr	    return errno;
145178825Sdfr	}
146178825Sdfr	if(st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) {
147178825Sdfr	    closedir(d);
148178825Sdfr	    return EACCES;
149178825Sdfr	}
150178825Sdfr    }
151178825Sdfr#endif
152178825Sdfr
153178825Sdfr    while((dent = readdir(d)) != NULL) {
154178825Sdfr	if(strcmp(dent->d_name, ".") == 0 ||
155178825Sdfr	   strcmp(dent->d_name, "..") == 0 ||
156178825Sdfr	   dent->d_name[0] == '#' ||			  /* emacs autosave */
157178825Sdfr	   dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
158178825Sdfr	    continue;
159178825Sdfr	snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
160178825Sdfr	ret = check_one_file(context, filename, pwd, principal, result);
161178825Sdfr	if(ret == 0 && *result == TRUE)
162178825Sdfr	    break;
163178825Sdfr	ret = 0; /* don't propagate errors upstream */
164178825Sdfr    }
165178825Sdfr    closedir(d);
166178825Sdfr    return ret;
167178825Sdfr}
168178825Sdfr
169178825Sdfrstatic krb5_boolean
170178825Sdfrmatch_local_principals(krb5_context context,
171178825Sdfr		       krb5_principal principal,
172178825Sdfr		       const char *luser)
173178825Sdfr{
174178825Sdfr    krb5_error_code ret;
175178825Sdfr    krb5_realm *realms, *r;
176178825Sdfr    krb5_boolean result = FALSE;
177178825Sdfr
178178825Sdfr    /* multi-component principals can never match */
179178825Sdfr    if(krb5_principal_get_comp_string(context, principal, 1) != NULL)
180178825Sdfr	return FALSE;
181178825Sdfr
182178825Sdfr    ret = krb5_get_default_realms (context, &realms);
183178825Sdfr    if (ret)
184178825Sdfr	return FALSE;
185178825Sdfr
186178825Sdfr    for (r = realms; *r != NULL; ++r) {
187178825Sdfr	if(strcmp(krb5_principal_get_realm(context, principal),
188178825Sdfr		  *r) != 0)
189178825Sdfr	    continue;
190178825Sdfr	if(strcmp(krb5_principal_get_comp_string(context, principal, 0),
191178825Sdfr		  luser) == 0) {
192178825Sdfr	    result = TRUE;
193178825Sdfr	    break;
194178825Sdfr	}
195178825Sdfr    }
196178825Sdfr    krb5_free_host_realm (context, realms);
197178825Sdfr    return result;
198178825Sdfr}
199178825Sdfr
200178825Sdfr/**
20155682Smarkm * Return TRUE iff `principal' is allowed to login as `luser'.
20255682Smarkm */
20355682Smarkm
204178825Sdfrkrb5_boolean KRB5_LIB_FUNCTION
20555682Smarkmkrb5_kuserok (krb5_context context,
20655682Smarkm	      krb5_principal principal,
20755682Smarkm	      const char *luser)
20855682Smarkm{
209178825Sdfr    char *buf;
210178825Sdfr    size_t buflen;
21155682Smarkm    struct passwd *pwd;
21255682Smarkm    krb5_error_code ret;
213178825Sdfr    krb5_boolean result = FALSE;
21455682Smarkm
215178825Sdfr    krb5_boolean found_file = FALSE;
216178825Sdfr
217178825Sdfr#ifdef POSIX_GETPWNAM_R
218178825Sdfr    char pwbuf[2048];
219178825Sdfr    struct passwd pw;
220178825Sdfr
221178825Sdfr    if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
222178825Sdfr	return FALSE;
223178825Sdfr#else
224178825Sdfr    pwd = getpwnam (luser);
225178825Sdfr#endif
226120945Snectar    if (pwd == NULL)
227120945Snectar	return FALSE;
228120945Snectar
229178825Sdfr#define KLOGIN "/.k5login"
230178825Sdfr    buflen = strlen(pwd->pw_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */
231178825Sdfr    buf = malloc(buflen);
232178825Sdfr    if(buf == NULL)
23355682Smarkm	return FALSE;
234178825Sdfr    /* check user's ~/.k5login */
235178825Sdfr    strlcpy(buf, pwd->pw_dir, buflen);
236178825Sdfr    strlcat(buf, KLOGIN, buflen);
237178825Sdfr    ret = check_one_file(context, buf, pwd, principal, &result);
23855682Smarkm
239178825Sdfr    if(ret == 0 && result == TRUE) {
240178825Sdfr	free(buf);
241178825Sdfr	return TRUE;
242178825Sdfr    }
24355682Smarkm
244178825Sdfr    if(ret != ENOENT)
245178825Sdfr	found_file = TRUE;
24655682Smarkm
247178825Sdfr    strlcat(buf, ".d", buflen);
248178825Sdfr    ret = check_directory(context, buf, pwd, principal, &result);
249178825Sdfr    free(buf);
250178825Sdfr    if(ret == 0 && result == TRUE)
251178825Sdfr	return TRUE;
25255682Smarkm
253178825Sdfr    if(ret != ENOENT && ret != ENOTDIR)
254178825Sdfr	found_file = TRUE;
25555682Smarkm
256178825Sdfr    /* finally if no files exist, allow all principals matching
257178825Sdfr       <localuser>@<LOCALREALM> */
258178825Sdfr    if(found_file == FALSE)
259178825Sdfr	return match_local_principals(context, principal, luser);
260178825Sdfr
26155682Smarkm    return FALSE;
26255682Smarkm}
263