1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/localauth_k5login.c - k5login localauth module */
3 /*
4  * Copyright (C) 1990,1993,2007,2013 by the Massachusetts Institute
5  * of Technology.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "k5-int.h"
34 #include "os-proto.h"
35 #include <krb5/localauth_plugin.h>
36 
37 #if !defined(_WIN32)            /* Not yet for Windows */
38 #include <pwd.h>
39 
40 #if defined(__APPLE__) && defined(__MACH__)
41 #include <hfs/hfs_mount.h>
42 #define FILE_OWNER_OK(UID)  ((UID) == 0 || (UID) == UNKNOWNUID)
43 #else
44 #define FILE_OWNER_OK(UID)  ((UID) == 0)
45 #endif
46 
47 /*
48  * Find the k5login filename for luser, either in the user's homedir or in a
49  * configured directory under the username.
50  */
51 static krb5_error_code
get_k5login_filename(krb5_context context,const char * lname,const char * homedir,char ** filename_out)52 get_k5login_filename(krb5_context context, const char *lname,
53                      const char *homedir, char **filename_out)
54 {
55     krb5_error_code ret;
56     char *dir, *filename;
57 
58     *filename_out = NULL;
59     ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
60                              KRB5_CONF_K5LOGIN_DIRECTORY, NULL, NULL, &dir);
61     if (ret != 0)
62         return ret;
63 
64     if (dir == NULL) {
65         /* Look in the user's homedir. */
66         if (asprintf(&filename, "%s/.k5login", homedir) < 0)
67             return ENOMEM;
68     } else {
69         /* Look in the configured directory. */
70         if (asprintf(&filename, "%s/%s", dir, lname) < 0)
71             ret = ENOMEM;
72         profile_release_string(dir);
73         if (ret)
74             return ret;
75     }
76     *filename_out = filename;
77     return 0;
78 }
79 
80 /* Determine whether aname is authorized to log in as lname according to the
81  * user's k5login file. */
82 static krb5_error_code
userok_k5login(krb5_context context,krb5_localauth_moddata data,krb5_const_principal aname,const char * lname)83 userok_k5login(krb5_context context, krb5_localauth_moddata data,
84                krb5_const_principal aname, const char *lname)
85 {
86     krb5_error_code ret;
87     int authoritative = TRUE, gobble;
88     char *filename = NULL, *princname = NULL;
89     char *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ];
90     struct stat sbuf;
91     struct passwd pwx, *pwd;
92     FILE *fp = NULL;
93 
94     ret = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
95                               KRB5_CONF_K5LOGIN_AUTHORITATIVE, NULL, TRUE,
96                               &authoritative);
97     if (ret)
98         goto cleanup;
99 
100     /* Get the local user's .k5login filename. */
101     ret = k5_getpwnam_r(lname, &pwx, pwbuf, sizeof(pwbuf), &pwd);
102     if (ret) {
103         ret = EPERM;
104         goto cleanup;
105     }
106     ret = get_k5login_filename(context, lname, pwd->pw_dir, &filename);
107     if (ret)
108         goto cleanup;
109 
110     if (access(filename, F_OK) != 0) {
111         ret = KRB5_PLUGIN_NO_HANDLE;
112         goto cleanup;
113     }
114 
115     ret = krb5_unparse_name(context, aname, &princname);
116     if (ret)
117         goto cleanup;
118 
119     fp = fopen(filename, "r");
120     if (fp == NULL) {
121         ret = errno;
122         goto cleanup;
123     }
124     set_cloexec_file(fp);
125 
126     /* For security reasons, the .k5login file must be owned either by
127      * the user or by root. */
128     if (fstat(fileno(fp), &sbuf)) {
129         ret = errno;
130         goto cleanup;
131     }
132     if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) {
133         ret = EPERM;
134         goto cleanup;
135     }
136 
137     /* Check each line. */
138     while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
139         newline = strrchr(linebuf, '\n');
140         if (newline != NULL)
141             *newline = '\0';
142         if (strcmp(linebuf, princname) == 0) {
143             ret = 0;
144             goto cleanup;
145         }
146         /* Clean up the rest of the line if necessary. */
147         if (newline == NULL)
148             while ((gobble = getc(fp)) != EOF && gobble != '\n');
149     }
150 
151     /* We didn't find it. */
152     ret = EPERM;
153 
154 cleanup:
155     free(princname);
156     free(filename);
157     if (fp != NULL)
158         fclose(fp);
159     /* If k5login files are non-authoritative, never reject. */
160     return (!authoritative && ret) ? KRB5_PLUGIN_NO_HANDLE : ret;
161 }
162 
163 #else /* _WIN32 */
164 
165 static krb5_error_code
userok_k5login(krb5_context context,krb5_localauth_moddata data,krb5_const_principal aname,const char * lname)166 userok_k5login(krb5_context context, krb5_localauth_moddata data,
167                krb5_const_principal aname, const char *lname)
168 {
169     return KRB5_PLUGIN_NO_HANDLE;
170 }
171 
172 #endif
173 
174 krb5_error_code
localauth_k5login_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)175 localauth_k5login_initvt(krb5_context context, int maj_ver, int min_ver,
176                          krb5_plugin_vtable vtable)
177 {
178     krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
179 
180     vt->name = "k5login";
181     vt->userok = userok_k5login;
182     return 0;
183 }
184