s3:smb2_server: return OBJECT_NAME_INVALID if the path is terminated in SMB2_FIND...
[Samba.git] / nsswitch / winbind_krb5_locator.c
blobe921caea5939af95213435ddc780b0372b2f0fd6
1 /*
2 Unix SMB/CIFS implementation.
3 kerberos locator plugin
4 Copyright (C) Guenther Deschner 2007-2008
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.
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.
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/>.
20 #include "nsswitch/winbind_client.h"
21 #include "libwbclient/wbclient.h"
23 #ifndef DEBUG_KRB5
24 #undef DEBUG_KRB5
25 #endif
27 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
29 #if HAVE_COM_ERR_H
30 #include <com_err.h>
31 #endif
33 #include <krb5.h>
34 #include <krb5/locate_plugin.h>
36 #ifndef KRB5_PLUGIN_NO_HANDLE
37 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
38 #endif
40 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
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;
55 return NULL;
59 #ifdef DEBUG_KRB5
60 static const char *locate_service_type_name(enum locate_service_type svc)
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;
76 return NULL;
79 static const char *socktype_name(int socktype)
81 switch (socktype) {
82 case SOCK_STREAM:
83 return "SOCK_STREAM";
84 case SOCK_DGRAM:
85 return "SOCK_DGRAM";
86 default:
87 break;
89 return "unknown";
92 static const char *family_name(int family)
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;
106 return "unknown";
108 #endif
111 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
113 * @param svc
114 * @param realm string
115 * @param socktype integer
116 * @param family integer
118 * @return integer.
121 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
122 const char *realm,
123 int socktype,
124 int family)
126 if (!realm || strlen(realm) == 0) {
127 return EINVAL;
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;
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;
154 switch (socktype) {
155 case SOCK_STREAM:
156 case SOCK_DGRAM:
157 case 0: /* Heimdal uses that */
158 break;
159 default:
160 return EINVAL;
163 return 0;
167 * Try to get addrinfo for a given host and call the krb5 callback
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
175 * @return krb5_error_code.
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)
184 struct addrinfo *out = NULL;
185 int ret;
186 int count = 3;
188 while (count) {
190 ret = getaddrinfo(name, service, in, &out);
191 if (ret == 0) {
192 break;
195 if ((ret == EAI_AGAIN) && (count > 1)) {
196 count--;
197 continue;
200 #ifdef DEBUG_KRB5
201 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
202 "getaddrinfo failed: %s (%d)\n",
203 (unsigned int)getpid(), gai_strerror(ret), ret);
204 #endif
206 return KRB5_PLUGIN_NO_HANDLE;
209 ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
210 #ifdef DEBUG_KRB5
211 if (ret) {
212 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
213 "failed to call callback: %s (%d)\n",
214 (unsigned int)getpid(), error_message(ret), ret);
216 #endif
218 freeaddrinfo(out);
219 return ret;
223 * PUBLIC INTERFACE: locate init
225 * @param context krb5_context
226 * @param privata_data pointer to private data pointer
228 * @return krb5_error_code.
231 static krb5_error_code smb_krb5_locator_init(krb5_context context,
232 void **private_data)
234 return 0;
238 * PUBLIC INTERFACE: close locate
240 * @param private_data pointer to private data
242 * @return void.
245 static void smb_krb5_locator_close(void *private_data)
247 return;
251 static bool ask_winbind(const char *realm, char **dcname)
253 wbcErr wbc_status;
254 const char *dc = NULL;
255 struct wbcDomainControllerInfoEx *dc_info = NULL;
256 uint32_t flags;
258 flags = WBC_LOOKUP_DC_KDC_REQUIRED |
259 WBC_LOOKUP_DC_IS_DNS_NAME |
260 WBC_LOOKUP_DC_RETURN_DNS_NAME |
261 WBC_LOOKUP_DC_IP_REQUIRED;
263 wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
265 if (!WBC_ERROR_IS_OK(wbc_status)) {
266 #ifdef DEBUG_KRB5
267 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
268 (unsigned int)getpid(), wbcErrorString(wbc_status));
269 #endif
270 return false;
273 if (dc_info->dc_address) {
274 dc = dc_info->dc_address;
275 if (dc[0] == '\\') dc++;
276 if (dc[0] == '\\') dc++;
279 if (!dc && dc_info->dc_unc) {
280 dc = dc_info->dc_unc;
281 if (dc[0] == '\\') dc++;
282 if (dc[0] == '\\') dc++;
285 if (!dc) {
286 wbcFreeMemory(dc_info);
287 return false;
290 *dcname = strdup(dc);
291 if (!*dcname) {
292 wbcFreeMemory(dc_info);
293 return false;
296 wbcFreeMemory(dc_info);
297 return true;
301 * PUBLIC INTERFACE: locate lookup
303 * @param private_data pointer to private data
304 * @param svc enum locate_service_type.
305 * @param realm string
306 * @param socktype integer
307 * @param family integer
308 * @param cbfunc callback function to send back entries
309 * @param cbdata void pointer to cbdata
311 * @return krb5_error_code.
314 static krb5_error_code smb_krb5_locator_lookup(void *private_data,
315 enum locate_service_type svc,
316 const char *realm,
317 int socktype,
318 int family,
319 int (*cbfunc)(void *, int, struct sockaddr *),
320 void *cbdata)
322 krb5_error_code ret;
323 struct addrinfo aihints;
324 char *kdc_name = NULL;
325 const char *service = get_service_from_locate_service_type(svc);
327 ZERO_STRUCT(aihints);
329 #ifdef DEBUG_KRB5
330 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
331 "svc: '%s' (%d) "
332 "socktype: '%s' (%d), family: '%s' (%d)\n",
333 (unsigned int)getpid(), realm,
334 locate_service_type_name(svc), svc,
335 socktype_name(socktype), socktype,
336 family_name(family), family);
337 #endif
338 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
339 family);
340 if (ret) {
341 #ifdef DEBUG_KRB5
342 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
343 "returning ret: %s (%d)\n",
344 (unsigned int)getpid(), error_message(ret), ret);
345 #endif
346 return ret;
349 if (!winbind_env_set()) {
350 if (!ask_winbind(realm, &kdc_name)) {
351 #ifdef DEBUG_KRB5
352 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
353 "failed to query winbindd\n",
354 (unsigned int)getpid());
355 #endif
356 goto failed;
358 } else {
359 const char *env = NULL;
360 char *var = NULL;
361 if (asprintf(&var, "%s_%s",
362 WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
363 goto failed;
365 env = getenv(var);
366 if (!env) {
367 #ifdef DEBUG_KRB5
368 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
369 "failed to get kdc from env %s\n",
370 (unsigned int)getpid(), var);
371 #endif
372 free(var);
373 goto failed;
375 free(var);
377 kdc_name = strdup(env);
378 if (!kdc_name) {
379 goto failed;
382 #ifdef DEBUG_KRB5
383 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
384 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
385 kdc_name, realm);
386 #endif
388 aihints.ai_family = family;
389 aihints.ai_socktype = socktype;
391 ret = smb_krb5_locator_call_cbfunc(kdc_name,
392 service,
393 &aihints,
394 cbfunc, cbdata);
395 SAFE_FREE(kdc_name);
397 return ret;
399 failed:
400 return KRB5_PLUGIN_NO_HANDLE;
403 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
404 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
405 #else
406 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
407 #endif
409 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
410 0, /* version */
411 smb_krb5_locator_init,
412 smb_krb5_locator_close,
413 smb_krb5_locator_lookup,
416 #endif