file_server: Use samba4 auth module for guest logins as well
[nivanova/samba-autobuild/.git] / nsswitch / winbind_krb5_locator.c
1 /*
2    Unix SMB/CIFS implementation.
3    kerberos locator plugin
4    Copyright (C) Guenther Deschner 2007-2008
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "nsswitch/winbind_client.h"
21 #include "libwbclient/wbclient.h"
22
23 #ifndef DEBUG_KRB5
24 #undef DEBUG_KRB5
25 #endif
26
27 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
28
29 #if HAVE_COM_ERR_H
30 #include <com_err.h>
31 #endif
32
33 #include <krb5.h>
34 #include <krb5/locate_plugin.h>
35
36 #ifndef KRB5_PLUGIN_NO_HANDLE
37 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
38 #endif
39
40 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
41 {
42         switch (svc) {
43                 case locate_service_kdc:
44                 case locate_service_master_kdc:
45                         return "88";
46                 case locate_service_kadmin:
47                 case locate_service_krb524:
48                         /* not supported */
49                         return NULL;
50                 case locate_service_kpasswd:
51                         return "464";
52                 default:
53                         break;
54         }
55         return NULL;
56
57 }
58
59 #ifdef DEBUG_KRB5
60 static const char *locate_service_type_name(enum locate_service_type svc)
61 {
62         switch (svc) {
63                 case locate_service_kdc:
64                         return "locate_service_kdc";
65                 case locate_service_master_kdc:
66                         return "locate_service_master_kdc";
67                 case locate_service_kadmin:
68                         return "locate_service_kadmin";
69                 case locate_service_krb524:
70                         return "locate_service_krb524";
71                 case locate_service_kpasswd:
72                         return "locate_service_kpasswd";
73                 default:
74                         break;
75         }
76         return NULL;
77 }
78
79 static const char *socktype_name(int socktype)
80 {
81         switch (socktype) {
82                 case SOCK_STREAM:
83                         return "SOCK_STREAM";
84                 case SOCK_DGRAM:
85                         return "SOCK_DGRAM";
86                 default:
87                         break;
88         }
89         return "unknown";
90 }
91
92 static const char *family_name(int family)
93 {
94         switch (family) {
95                 case AF_UNSPEC:
96                         return "AF_UNSPEC";
97                 case AF_INET:
98                         return "AF_INET";
99 #if defined(HAVE_IPV6)
100                 case AF_INET6:
101                         return "AF_INET6";
102 #endif
103                 default:
104                         break;
105         }
106         return "unknown";
107 }
108 #endif
109
110 /**
111  * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
112  *
113  * @param svc
114  * @param realm string
115  * @param socktype integer
116  * @param family integer
117  *
118  * @return integer.
119  */
120
121 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
122                                                 const char *realm,
123                                                 int socktype,
124                                                 int family)
125 {
126         if (!realm || strlen(realm) == 0) {
127                 return EINVAL;
128         }
129
130         switch (svc) {
131                 case locate_service_kdc:
132                 case locate_service_master_kdc:
133                 case locate_service_kpasswd:
134                         break;
135                 case locate_service_kadmin:
136                 case locate_service_krb524:
137                         return KRB5_PLUGIN_NO_HANDLE;
138                 default:
139                         return EINVAL;
140         }
141
142         switch (family) {
143                 case AF_UNSPEC:
144                 case AF_INET:
145                         break;
146 #if defined(HAVE_IPV6)
147                 case AF_INET6:
148                         break;
149 #endif
150                 default:
151                         return EINVAL;
152         }
153
154         switch (socktype) {
155                 case SOCK_STREAM:
156                 case SOCK_DGRAM:
157                 case 0: /* Heimdal uses that */
158                         break;
159                 default:
160                         return EINVAL;
161         }
162
163         return 0;
164 }
165
166 /**
167  * Try to get addrinfo for a given host and call the krb5 callback
168  *
169  * @param name string
170  * @param service string
171  * @param in struct addrinfo hint
172  * @param cbfunc krb5 callback function
173  * @param cbdata void pointer cbdata
174  *
175  * @return krb5_error_code.
176  */
177
178 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
179                                                     const char *service,
180                                                     struct addrinfo *in,
181                                                     int (*cbfunc)(void *, int, struct sockaddr *),
182                                                     void *cbdata)
183 {
184         struct addrinfo *out = NULL;
185         int ret = 0;
186         struct addrinfo *res = NULL;
187         int count = 3;
188
189         while (count) {
190
191                 ret = getaddrinfo(name, service, in, &out);
192                 if (ret == 0) {
193                         break;
194                 }
195
196                 if ((ret == EAI_AGAIN) && (count > 1)) {
197                         count--;
198                         continue;
199                 }
200
201 #ifdef DEBUG_KRB5
202                 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
203                         "getaddrinfo failed: %s (%d)\n",
204                         (unsigned int)getpid(), gai_strerror(ret), ret);
205 #endif
206
207                 return KRB5_PLUGIN_NO_HANDLE;
208         }
209
210         for (res = out; res; res = res->ai_next) {
211                 if (!res->ai_addr || res->ai_addrlen == 0) {
212                         continue;
213                 }
214
215                 ret = cbfunc(cbdata, res->ai_socktype, res->ai_addr);
216                 if (ret) {
217 #ifdef DEBUG_KRB5
218                         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
219                                 "failed to call callback: %s (%d)\n",
220                                 (unsigned int)getpid(), error_message(ret), ret);
221 #endif
222                         break;
223                 }
224         }
225
226         if (out) {
227                 freeaddrinfo(out);
228         }
229         return ret;
230 }
231
232 /**
233  * PUBLIC INTERFACE: locate init
234  *
235  * @param context krb5_context
236  * @param privata_data pointer to private data pointer
237  *
238  * @return krb5_error_code.
239  */
240
241 static krb5_error_code smb_krb5_locator_init(krb5_context context,
242                                              void **private_data)
243 {
244         return 0;
245 }
246
247 /**
248  * PUBLIC INTERFACE: close locate
249  *
250  * @param private_data pointer to private data
251  *
252  * @return void.
253  */
254
255 static void smb_krb5_locator_close(void *private_data)
256 {
257         return;
258 }
259
260
261 static bool ask_winbind(const char *realm, char **dcname)
262 {
263         wbcErr wbc_status;
264         const char *dc = NULL;
265         struct wbcDomainControllerInfoEx *dc_info = NULL;
266         uint32_t flags;
267
268         flags = WBC_LOOKUP_DC_KDC_REQUIRED |
269                 WBC_LOOKUP_DC_IS_DNS_NAME |
270                 WBC_LOOKUP_DC_RETURN_DNS_NAME;
271
272         wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
273
274         if (!WBC_ERROR_IS_OK(wbc_status)) {
275 #ifdef DEBUG_KRB5
276                 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
277                         (unsigned int)getpid(), wbcErrorString(wbc_status));
278 #endif
279                 return false;
280         }
281
282         if (!dc && dc_info->dc_unc) {
283                 dc = dc_info->dc_unc;
284                 if (dc[0] == '\\') dc++;
285                 if (dc[0] == '\\') dc++;
286         }
287
288         if (!dc) {
289                 wbcFreeMemory(dc_info);
290                 return false;
291         }
292
293         *dcname = strdup(dc);
294         if (!*dcname) {
295                 wbcFreeMemory(dc_info);
296                 return false;
297         }
298
299         wbcFreeMemory(dc_info);
300         return true;
301 }
302
303 /**
304  * PUBLIC INTERFACE: locate lookup
305  *
306  * @param private_data pointer to private data
307  * @param svc enum locate_service_type.
308  * @param realm string
309  * @param socktype integer
310  * @param family integer
311  * @param cbfunc callback function to send back entries
312  * @param cbdata void pointer to cbdata
313  *
314  * @return krb5_error_code.
315  */
316
317 static krb5_error_code smb_krb5_locator_lookup(void *private_data,
318                                                enum locate_service_type svc,
319                                                const char *realm,
320                                                int socktype,
321                                                int family,
322                                                int (*cbfunc)(void *, int, struct sockaddr *),
323                                                         void *cbdata)
324 {
325         krb5_error_code ret;
326         struct addrinfo aihints;
327         char *kdc_name = NULL;
328         const char *service = get_service_from_locate_service_type(svc);
329
330         ZERO_STRUCT(aihints);
331
332 #ifdef DEBUG_KRB5
333         fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
334                         "svc: '%s' (%d) "
335                         "socktype: '%s' (%d), family: '%s' (%d)\n",
336                         (unsigned int)getpid(), realm,
337                         locate_service_type_name(svc), svc,
338                         socktype_name(socktype), socktype,
339                         family_name(family), family);
340 #endif
341         ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
342                                                    family);
343         if (ret) {
344 #ifdef DEBUG_KRB5
345                 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
346                         "returning ret: %s (%d)\n",
347                         (unsigned int)getpid(), error_message(ret), ret);
348 #endif
349                 return ret;
350         }
351
352         if (!winbind_env_set()) {
353                 if (!ask_winbind(realm, &kdc_name)) {
354 #ifdef DEBUG_KRB5
355                         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
356                                 "failed to query winbindd\n",
357                                 (unsigned int)getpid());
358 #endif
359                         goto failed;
360                 }
361         } else {
362                 const char *env = NULL;
363                 char *var = NULL;
364                 if (asprintf(&var, "%s_%s",
365                              WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
366                         goto failed;
367                 }
368                 env = getenv(var);
369                 if (!env) {
370 #ifdef DEBUG_KRB5
371                         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
372                                 "failed to get kdc from env %s\n",
373                                 (unsigned int)getpid(), var);
374 #endif
375                         free(var);
376                         goto failed;
377                 }
378                 free(var);
379
380                 kdc_name = strdup(env);
381                 if (!kdc_name) {
382                         goto failed;
383                 }
384         }
385 #ifdef DEBUG_KRB5
386         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
387                 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
388                 kdc_name, realm);
389 #endif
390
391         aihints.ai_family = family;
392         aihints.ai_socktype = socktype;
393
394         ret = smb_krb5_locator_call_cbfunc(kdc_name,
395                                            service,
396                                            &aihints,
397                                            cbfunc, cbdata);
398         SAFE_FREE(kdc_name);
399
400         return ret;
401
402  failed:
403         return KRB5_PLUGIN_NO_HANDLE;
404 }
405
406 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
407 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
408 #else
409 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
410 #endif
411
412 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
413         0, /* version */
414         smb_krb5_locator_init,
415         smb_krb5_locator_close,
416         smb_krb5_locator_lookup,
417 };
418
419 #endif