155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
35178825Sdfr#include <dirent.h>
3655682Smarkm
37233294Sstas#ifndef _WIN32
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
43233294Sstascheck_one_file(krb5_context context,
44233294Sstas	       const char *filename,
45178825Sdfr	       struct passwd *pwd,
46233294Sstas	       krb5_principal principal,
47178825Sdfr	       krb5_boolean *result)
48178825Sdfr{
49178825Sdfr    FILE *f;
50178825Sdfr    char buf[BUFSIZ];
51178825Sdfr    krb5_error_code ret;
52178825Sdfr    struct stat st;
53233294Sstas
54178825Sdfr    *result = FALSE;
55178825Sdfr
56178825Sdfr    f = fopen (filename, "r");
57178825Sdfr    if (f == NULL)
58178825Sdfr	return errno;
59233294Sstas    rk_cloexec_file(f);
60233294Sstas
61178825Sdfr    /* check type and mode of file */
62178825Sdfr    if (fstat(fileno(f), &st) != 0) {
63178825Sdfr	fclose (f);
64178825Sdfr	return errno;
65178825Sdfr    }
66178825Sdfr    if (S_ISDIR(st.st_mode)) {
67178825Sdfr	fclose (f);
68178825Sdfr	return EISDIR;
69178825Sdfr    }
70178825Sdfr    if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
71178825Sdfr	fclose (f);
72178825Sdfr	return EACCES;
73178825Sdfr    }
74178825Sdfr    if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
75178825Sdfr	fclose (f);
76178825Sdfr	return EACCES;
77178825Sdfr    }
78178825Sdfr
79178825Sdfr    while (fgets (buf, sizeof(buf), f) != NULL) {
80178825Sdfr	krb5_principal tmp;
81178825Sdfr	char *newline = buf + strcspn(buf, "\n");
82178825Sdfr
83178825Sdfr	if(*newline != '\n') {
84178825Sdfr	    int c;
85178825Sdfr	    c = fgetc(f);
86178825Sdfr	    if(c != EOF) {
87178825Sdfr		while(c != EOF && c != '\n')
88178825Sdfr		    c = fgetc(f);
89178825Sdfr		/* line was too long, so ignore it */
90178825Sdfr		continue;
91178825Sdfr	    }
92178825Sdfr	}
93178825Sdfr	*newline = '\0';
94178825Sdfr	ret = krb5_parse_name (context, buf, &tmp);
95178825Sdfr	if (ret)
96178825Sdfr	    continue;
97178825Sdfr	*result = krb5_principal_compare (context, principal, tmp);
98178825Sdfr	krb5_free_principal (context, tmp);
99178825Sdfr	if (*result) {
100178825Sdfr	    fclose (f);
101178825Sdfr	    return 0;
102178825Sdfr	}
103178825Sdfr    }
104178825Sdfr    fclose (f);
105178825Sdfr    return 0;
106178825Sdfr}
107178825Sdfr
108178825Sdfrstatic krb5_error_code
109233294Sstascheck_directory(krb5_context context,
110233294Sstas		const char *dirname,
111178825Sdfr		struct passwd *pwd,
112233294Sstas		krb5_principal principal,
113178825Sdfr		krb5_boolean *result)
114178825Sdfr{
115178825Sdfr    DIR *d;
116178825Sdfr    struct dirent *dent;
117178825Sdfr    char filename[MAXPATHLEN];
118178825Sdfr    krb5_error_code ret = 0;
119178825Sdfr    struct stat st;
120178825Sdfr
121178825Sdfr    *result = FALSE;
122178825Sdfr
123178825Sdfr    if(lstat(dirname, &st) < 0)
124178825Sdfr	return errno;
125178825Sdfr
126178825Sdfr    if (!S_ISDIR(st.st_mode))
127178825Sdfr	return ENOTDIR;
128233294Sstas
129178825Sdfr    if (st.st_uid != pwd->pw_uid && st.st_uid != 0)
130178825Sdfr	return EACCES;
131178825Sdfr    if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
132178825Sdfr	return EACCES;
133178825Sdfr
134233294Sstas    if((d = opendir(dirname)) == NULL)
135178825Sdfr	return errno;
136178825Sdfr
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
152178825Sdfr    while((dent = readdir(d)) != NULL) {
153178825Sdfr	if(strcmp(dent->d_name, ".") == 0 ||
154178825Sdfr	   strcmp(dent->d_name, "..") == 0 ||
155178825Sdfr	   dent->d_name[0] == '#' ||			  /* emacs autosave */
156178825Sdfr	   dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
157178825Sdfr	    continue;
158178825Sdfr	snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
159178825Sdfr	ret = check_one_file(context, filename, pwd, principal, result);
160178825Sdfr	if(ret == 0 && *result == TRUE)
161178825Sdfr	    break;
162178825Sdfr	ret = 0; /* don't propagate errors upstream */
163178825Sdfr    }
164178825Sdfr    closedir(d);
165178825Sdfr    return ret;
166178825Sdfr}
167178825Sdfr
168233294Sstas#endif  /* !_WIN32 */
169233294Sstas
170178825Sdfrstatic krb5_boolean
171178825Sdfrmatch_local_principals(krb5_context context,
172178825Sdfr		       krb5_principal principal,
173178825Sdfr		       const char *luser)
174178825Sdfr{
175178825Sdfr    krb5_error_code ret;
176178825Sdfr    krb5_realm *realms, *r;
177178825Sdfr    krb5_boolean result = FALSE;
178233294Sstas
179178825Sdfr    /* multi-component principals can never match */
180178825Sdfr    if(krb5_principal_get_comp_string(context, principal, 1) != NULL)
181178825Sdfr	return FALSE;
182178825Sdfr
183178825Sdfr    ret = krb5_get_default_realms (context, &realms);
184178825Sdfr    if (ret)
185178825Sdfr	return FALSE;
186233294Sstas
187178825Sdfr    for (r = realms; *r != NULL; ++r) {
188178825Sdfr	if(strcmp(krb5_principal_get_realm(context, principal),
189178825Sdfr		  *r) != 0)
190178825Sdfr	    continue;
191178825Sdfr	if(strcmp(krb5_principal_get_comp_string(context, principal, 0),
192178825Sdfr		  luser) == 0) {
193178825Sdfr	    result = TRUE;
194178825Sdfr	    break;
195178825Sdfr	}
196178825Sdfr    }
197178825Sdfr    krb5_free_host_realm (context, realms);
198178825Sdfr    return result;
199178825Sdfr}
200178825Sdfr
201178825Sdfr/**
202233294Sstas * This function takes the name of a local user and checks if
203233294Sstas * principal is allowed to log in as that user.
204233294Sstas *
205233294Sstas * The user may have a ~/.k5login file listing principals that are
206233294Sstas * allowed to login as that user. If that file does not exist, all
207233294Sstas * principals with a first component identical to the username, and a
208233294Sstas * realm considered local, are allowed access.
209233294Sstas *
210233294Sstas * The .k5login file must contain one principal per line, be owned by
211233294Sstas * user and not be writable by group or other (but must be readable by
212233294Sstas * anyone).
213233294Sstas *
214233294Sstas * Note that if the file exists, no implicit access rights are given
215233294Sstas * to user@@LOCALREALM.
216233294Sstas *
217233294Sstas * Optionally, a set of files may be put in ~/.k5login.d (a
218233294Sstas * directory), in which case they will all be checked in the same
219233294Sstas * manner as .k5login.  The files may be called anything, but files
220233294Sstas * starting with a hash (#) , or ending with a tilde (~) are
221233294Sstas * ignored. Subdirectories are not traversed. Note that this directory
222233294Sstas * may not be checked by other Kerberos implementations.
223233294Sstas *
224233294Sstas * If no configuration file exists, match user against local domains,
225233294Sstas * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES.
226233294Sstas *
227233294Sstas * @param context Kerberos 5 context.
228233294Sstas * @param principal principal to check if allowed to login
229233294Sstas * @param luser local user id
230233294Sstas *
231233294Sstas * @return returns TRUE if access should be granted, FALSE otherwise.
232233294Sstas *
233233294Sstas * @ingroup krb5_support
23455682Smarkm */
23555682Smarkm
236233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
23755682Smarkmkrb5_kuserok (krb5_context context,
23855682Smarkm	      krb5_principal principal,
23955682Smarkm	      const char *luser)
24055682Smarkm{
241233294Sstas#ifndef _WIN32
242178825Sdfr    char *buf;
243178825Sdfr    size_t buflen;
244233294Sstas    struct passwd *pwd = NULL;
245233294Sstas    char *profile_dir = NULL;
24655682Smarkm    krb5_error_code ret;
247178825Sdfr    krb5_boolean result = FALSE;
24855682Smarkm
249178825Sdfr    krb5_boolean found_file = FALSE;
250178825Sdfr
251178825Sdfr#ifdef POSIX_GETPWNAM_R
252178825Sdfr    char pwbuf[2048];
253178825Sdfr    struct passwd pw;
254178825Sdfr
255178825Sdfr    if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
256178825Sdfr	return FALSE;
257178825Sdfr#else
258178825Sdfr    pwd = getpwnam (luser);
259178825Sdfr#endif
260120945Snectar    if (pwd == NULL)
261120945Snectar	return FALSE;
262233294Sstas    profile_dir = pwd->pw_dir;
263120945Snectar
264178825Sdfr#define KLOGIN "/.k5login"
265233294Sstas    buflen = strlen(profile_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */
266178825Sdfr    buf = malloc(buflen);
267178825Sdfr    if(buf == NULL)
26855682Smarkm	return FALSE;
269178825Sdfr    /* check user's ~/.k5login */
270233294Sstas    strlcpy(buf, profile_dir, buflen);
271178825Sdfr    strlcat(buf, KLOGIN, buflen);
272178825Sdfr    ret = check_one_file(context, buf, pwd, principal, &result);
27355682Smarkm
274178825Sdfr    if(ret == 0 && result == TRUE) {
275178825Sdfr	free(buf);
276178825Sdfr	return TRUE;
277178825Sdfr    }
27855682Smarkm
279233294Sstas    if(ret != ENOENT)
280178825Sdfr	found_file = TRUE;
28155682Smarkm
282178825Sdfr    strlcat(buf, ".d", buflen);
283178825Sdfr    ret = check_directory(context, buf, pwd, principal, &result);
284178825Sdfr    free(buf);
285178825Sdfr    if(ret == 0 && result == TRUE)
286178825Sdfr	return TRUE;
28755682Smarkm
288233294Sstas    if(ret != ENOENT && ret != ENOTDIR)
289178825Sdfr	found_file = TRUE;
29055682Smarkm
291178825Sdfr    /* finally if no files exist, allow all principals matching
292178825Sdfr       <localuser>@<LOCALREALM> */
293178825Sdfr    if(found_file == FALSE)
294178825Sdfr	return match_local_principals(context, principal, luser);
295178825Sdfr
29655682Smarkm    return FALSE;
297233294Sstas#else
298233294Sstas    /* The .k5login file may be on a remote profile and we don't have
299233294Sstas       access to the profile until we have a token handle for the
300233294Sstas       user's credentials. */
301233294Sstas    return match_local_principals(context, principal, luser);
302233294Sstas#endif
30355682Smarkm}
304