nssswitch: Log user access to kerberos
[gd/samba-autobuild/.git] / nsswitch / krb5_plugin / winbind_krb5_localauth.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    A localauth plugin for MIT Kerberos
5
6    Copyright (C) 2018      Andreas Schneider <asn@samba.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "replace.h"
23 #include <krb5/localauth_plugin.h>
24 #include <wbclient.h>
25 #ifdef HAVE_COM_ERR_H
26 #include <com_err.h>
27 #endif
28
29 struct krb5_localauth_moddata_st {
30         struct wbcContext *wbc_ctx;
31 };
32
33 /*
34  * Initialize the module data.
35  *
36  * This creates the wbclient context.
37  */
38 static krb5_error_code winbind_init(krb5_context context,
39                                     krb5_localauth_moddata *data)
40 {
41         krb5_localauth_moddata d;
42
43         *data = NULL;
44         d = malloc(sizeof(struct krb5_localauth_moddata_st));
45         if (d == NULL) {
46                 return ENOMEM;
47         }
48
49         d->wbc_ctx = wbcCtxCreate();
50         if (d->wbc_ctx == NULL) {
51                 free(d);
52                 return ENOMEM;
53         }
54
55         wbcSetClientProcessName("krb5_localauth_plugin");
56
57         *data = d;
58
59         return 0;
60 }
61
62 /*
63  * Release resources used by module data.
64  */
65 static void winbind_fini(krb5_context context, krb5_localauth_moddata data)
66 {
67         wbcCtxFree(data->wbc_ctx);
68         free(data);
69         data = NULL;
70 }
71
72 /*
73  * Determine whether aname is authorized to log in as the local account lname.
74  *
75  * Return 0 if aname is authorized, EPERM if aname is authoritatively not
76  * authorized, KRB5_PLUGIN_NO_HANDLE if the module cannot determine whether
77  * aname is authorized, and any other error code for a serious failure to
78  * process the request.  aname will be considered authorized if at least one
79  * module returns 0 and all other modules return KRB5_PLUGIN_NO_HANDLE.
80  */
81 static krb5_error_code winbind_userok(krb5_context context,
82                                       krb5_localauth_moddata data,
83                                       krb5_const_principal aname,
84                                       const char *lname)
85 {
86         krb5_error_code code = 0;
87         char *princ_str = NULL;
88         struct passwd *pwd = NULL;
89         uid_t princ_uid = (uid_t)-1;
90         uid_t lname_uid = (uid_t)-1;
91         wbcErr wbc_status;
92         int cmp;
93
94         code = krb5_unparse_name(context, aname, &princ_str);
95         if (code != 0) {
96                 return code;
97         }
98
99         cmp = strcasecmp(princ_str, lname);
100         if (cmp == 0) {
101                 goto out;
102         }
103
104         wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
105                                     princ_str,
106                                     &pwd);
107         switch (wbc_status) {
108         case WBC_ERR_SUCCESS:
109                 princ_uid = pwd->pw_uid;
110                 code = 0;
111                 break;
112         case WBC_ERR_UNKNOWN_USER:
113         /* match other insane libwbclient return codes */
114         case WBC_ERR_WINBIND_NOT_AVAILABLE:
115         case WBC_ERR_DOMAIN_NOT_FOUND:
116                 code = KRB5_PLUGIN_NO_HANDLE;
117                 break;
118         default:
119                 code = EIO;
120                 break;
121         }
122         wbcFreeMemory(pwd);
123         if (code != 0) {
124                 goto out;
125         }
126
127         wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
128                                     lname,
129                                     &pwd);
130         switch (wbc_status) {
131         case WBC_ERR_SUCCESS:
132                 lname_uid = pwd->pw_uid;
133                 break;
134         case WBC_ERR_UNKNOWN_USER:
135         /* match other insane libwbclient return codes */
136         case WBC_ERR_WINBIND_NOT_AVAILABLE:
137         case WBC_ERR_DOMAIN_NOT_FOUND:
138                 code = KRB5_PLUGIN_NO_HANDLE;
139                 break;
140         default:
141                 code = EIO;
142                 break;
143         }
144         wbcFreeMemory(pwd);
145         if (code != 0) {
146                 goto out;
147         }
148
149         if (princ_uid != lname_uid) {
150                 code = EPERM;
151         }
152
153         com_err("winbind_localauth",
154                 code,
155                 "Access %s: %s (uid=%u) %sequal to %s (uid=%u)",
156                 code == 0 ? "granted" : "denied",
157                 princ_str,
158                 (unsigned int)princ_uid,
159                 code == 0 ? "" : "not ",
160                 lname,
161                 (unsigned int)lname_uid);
162
163 out:
164         krb5_free_unparsed_name(context, princ_str);
165
166         return code;
167 }
168
169 /*
170  * Determine the local account name corresponding to aname.
171  *
172  * Return 0 and set *lname_out if a mapping can be determined; the contents of
173  * *lname_out will later be released with a call to the module's free_string
174  * method.  Return KRB5_LNAME_NOTRANS if no mapping can be determined.  Return
175  * any other error code for a serious failure to process the request; this will
176  * halt the krb5_aname_to_localname operation.
177  *
178  * If the module's an2ln_types field is set, this method will only be invoked
179  * when a profile "auth_to_local" value references one of the module's types.
180  * type and residual will be set to the type and residual of the auth_to_local
181  * value.
182  *
183  * If the module's an2ln_types field is not set but the an2ln method is
184  * implemented, this method will be invoked independently of the profile's
185  * auth_to_local settings, with type and residual set to NULL.  If multiple
186  * modules are registered with an2ln methods but no an2ln_types field, the
187  * order of invocation is not defined, but all such modules will be consulted
188  * before the built-in mechanisms are tried.
189  */
190 static krb5_error_code winbind_an2ln(krb5_context context,
191                                      krb5_localauth_moddata data,
192                                      const char *type,
193                                      const char *residual,
194                                      krb5_const_principal aname,
195                                      char **lname_out)
196 {
197         krb5_error_code code = 0;
198         char *princ_str = NULL;
199         char *name = NULL;
200         struct passwd *pwd = NULL;
201         wbcErr wbc_status;
202
203         code = krb5_unparse_name(context, aname, &princ_str);
204         if (code != 0) {
205                 return code;
206         }
207
208         wbc_status = wbcCtxGetpwnam(data->wbc_ctx,
209                                     princ_str,
210                                     &pwd);
211         krb5_free_unparsed_name(context, princ_str);
212         switch (wbc_status) {
213         case WBC_ERR_SUCCESS:
214                 name = strdup(pwd->pw_name);
215                 code = 0;
216                 break;
217         case WBC_ERR_UNKNOWN_USER:
218         /* match other insane libwbclient return codes */
219         case WBC_ERR_WINBIND_NOT_AVAILABLE:
220         case WBC_ERR_DOMAIN_NOT_FOUND:
221                 code = KRB5_LNAME_NOTRANS;
222                 break;
223         default:
224                 code = EIO;
225                 break;
226         }
227         wbcFreeMemory(pwd);
228         if (code != 0) {
229                 return code;
230         }
231
232         if (name == NULL) {
233                 return ENOMEM;
234         }
235
236         *lname_out = name;
237
238         return code;
239 }
240
241 /*
242  * Release the memory returned by an invocation of an2ln.
243  */
244 static void winbind_free_string(krb5_context context,
245                                 krb5_localauth_moddata data,
246                                 char *str)
247 {
248         free(str);
249 }
250
251 krb5_error_code
252 localauth_winbind_initvt(krb5_context context,
253                          int maj_ver,
254                          int min_ver,
255                          krb5_plugin_vtable vtable);
256
257 krb5_error_code
258 localauth_winbind_initvt(krb5_context context,
259                          int maj_ver,
260                          int min_ver,
261                          krb5_plugin_vtable vtable)
262 {
263         krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
264
265         if (maj_ver != 1) {
266                 com_err("winbind_localauth",
267                         EINVAL,
268                         "Failed to load, plugin API changed.");
269                 return KRB5_PLUGIN_VER_NOTSUPP;
270         }
271
272         vt->init = winbind_init;
273         vt->fini = winbind_fini;
274         vt->name = "winbind";
275         vt->an2ln = winbind_an2ln;
276         vt->userok = winbind_userok;
277         vt->free_string = winbind_free_string;
278
279         return 0;
280 }