Move asn1_blob() to lib/util/asn1.c
[Samba.git] / nsswitch / winbind_krb5_locator.c
blobb9e35bdec5998bf7c6d31d57ab4b84a1ac88aa9b
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 #include <krb5/locate_plugin.h>
31 #ifndef KRB5_PLUGIN_NO_HANDLE
32 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
33 #endif
35 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
37 switch (svc) {
38 case locate_service_kdc:
39 case locate_service_master_kdc:
40 return "88";
41 case locate_service_kadmin:
42 case locate_service_krb524:
43 /* not supported */
44 return NULL;
45 case locate_service_kpasswd:
46 return "464";
47 default:
48 break;
50 return NULL;
54 #ifdef DEBUG_KRB5
55 static const char *locate_service_type_name(enum locate_service_type svc)
57 switch (svc) {
58 case locate_service_kdc:
59 return "locate_service_kdc";
60 case locate_service_master_kdc:
61 return "locate_service_master_kdc";
62 case locate_service_kadmin:
63 return "locate_service_kadmin";
64 case locate_service_krb524:
65 return "locate_service_krb524";
66 case locate_service_kpasswd:
67 return "locate_service_kpasswd";
68 default:
69 break;
71 return NULL;
74 static const char *socktype_name(int socktype)
76 switch (socktype) {
77 case SOCK_STREAM:
78 return "SOCK_STREAM";
79 case SOCK_DGRAM:
80 return "SOCK_DGRAM";
81 default:
82 break;
84 return "unknown";
87 static const char *family_name(int family)
89 switch (family) {
90 case AF_UNSPEC:
91 return "AF_UNSPEC";
92 case AF_INET:
93 return "AF_INET";
94 #if defined(HAVE_IPV6)
95 case AF_INET6:
96 return "AF_INET6";
97 #endif
98 default:
99 break;
101 return "unknown";
103 #endif
106 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
108 * @param svc
109 * @param realm string
110 * @param socktype integer
111 * @param family integer
113 * @return integer.
116 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
117 const char *realm,
118 int socktype,
119 int family)
121 if (!realm || strlen(realm) == 0) {
122 return EINVAL;
125 switch (svc) {
126 case locate_service_kdc:
127 case locate_service_master_kdc:
128 case locate_service_kpasswd:
129 break;
130 case locate_service_kadmin:
131 case locate_service_krb524:
132 return KRB5_PLUGIN_NO_HANDLE;
133 default:
134 return EINVAL;
137 switch (family) {
138 case AF_UNSPEC:
139 case AF_INET:
140 break;
141 #if defined(HAVE_IPV6)
142 case AF_INET6:
143 break;
144 #endif
145 default:
146 return EINVAL;
149 switch (socktype) {
150 case SOCK_STREAM:
151 case SOCK_DGRAM:
152 case 0: /* Heimdal uses that */
153 break;
154 default:
155 return EINVAL;
158 return 0;
162 * Try to get addrinfo for a given host and call the krb5 callback
164 * @param name string
165 * @param service string
166 * @param in struct addrinfo hint
167 * @param cbfunc krb5 callback function
168 * @param cbdata void pointer cbdata
170 * @return krb5_error_code.
173 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
174 const char *service,
175 struct addrinfo *in,
176 int (*cbfunc)(void *, int, struct sockaddr *),
177 void *cbdata)
179 struct addrinfo *out = NULL;
180 int ret;
181 int count = 3;
183 while (count) {
185 ret = getaddrinfo(name, service, in, &out);
186 if (ret == 0) {
187 break;
190 if (ret == EAI_AGAIN) {
191 count--;
192 continue;
195 #ifdef DEBUG_KRB5
196 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
197 "getaddrinfo failed: %s (%d)\n",
198 (unsigned int)getpid(), gai_strerror(ret), ret);
199 #endif
201 return KRB5_PLUGIN_NO_HANDLE;
204 ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
205 #ifdef DEBUG_KRB5
206 if (ret) {
207 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
208 "failed to call callback: %s (%d)\n",
209 (unsigned int)getpid(), error_message(ret), ret);
211 #endif
213 freeaddrinfo(out);
214 return ret;
218 * PUBLIC INTERFACE: locate init
220 * @param context krb5_context
221 * @param privata_data pointer to private data pointer
223 * @return krb5_error_code.
226 static krb5_error_code smb_krb5_locator_init(krb5_context context,
227 void **private_data)
229 return 0;
233 * PUBLIC INTERFACE: close locate
235 * @param private_data pointer to private data
237 * @return void.
240 static void smb_krb5_locator_close(void *private_data)
242 return;
246 static bool ask_winbind(const char *realm, char **dcname)
248 wbcErr wbc_status;
249 const char *dc = NULL;
250 struct wbcDomainControllerInfoEx *dc_info = NULL;
251 uint32_t flags;
253 flags = WBC_LOOKUP_DC_KDC_REQUIRED |
254 WBC_LOOKUP_DC_IS_DNS_NAME |
255 WBC_LOOKUP_DC_RETURN_DNS_NAME |
256 WBC_LOOKUP_DC_IP_REQUIRED;
258 wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
260 if (!WBC_ERROR_IS_OK(wbc_status)) {
261 #ifdef DEBUG_KRB5
262 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
263 (unsigned int)getpid(), wbcErrorString(wbc_status));
264 #endif
265 return false;
268 if (dc_info->dc_address) {
269 dc = dc_info->dc_address;
270 if (dc[0] == '\\') dc++;
271 if (dc[0] == '\\') dc++;
274 if (!dc && dc_info->dc_unc) {
275 dc = dc_info->dc_unc;
276 if (dc[0] == '\\') dc++;
277 if (dc[0] == '\\') dc++;
280 if (!dc) {
281 wbcFreeMemory(dc_info);
282 return false;
285 *dcname = strdup(dc);
286 if (!*dcname) {
287 wbcFreeMemory(dc_info);
288 return false;
291 wbcFreeMemory(dc_info);
292 return true;
296 * PUBLIC INTERFACE: locate lookup
298 * @param private_data pointer to private data
299 * @param svc enum locate_service_type.
300 * @param realm string
301 * @param socktype integer
302 * @param family integer
303 * @param cbfunc callback function to send back entries
304 * @param cbdata void pointer to cbdata
306 * @return krb5_error_code.
309 static krb5_error_code smb_krb5_locator_lookup(void *private_data,
310 enum locate_service_type svc,
311 const char *realm,
312 int socktype,
313 int family,
314 int (*cbfunc)(void *, int, struct sockaddr *),
315 void *cbdata)
317 krb5_error_code ret;
318 struct addrinfo aihints;
319 char *kdc_name = NULL;
320 const char *service = get_service_from_locate_service_type(svc);
322 ZERO_STRUCT(aihints);
324 #ifdef DEBUG_KRB5
325 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
326 "svc: '%s' (%d) "
327 "socktype: '%s' (%d), family: '%s' (%d)\n",
328 (unsigned int)getpid(), realm,
329 locate_service_type_name(svc), svc,
330 socktype_name(socktype), socktype,
331 family_name(family), family);
332 #endif
333 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
334 family);
335 if (ret) {
336 #ifdef DEBUG_KRB5
337 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
338 "returning ret: %s (%d)\n",
339 (unsigned int)getpid(), error_message(ret), ret);
340 #endif
341 return ret;
344 if (!winbind_env_set()) {
345 if (!ask_winbind(realm, &kdc_name)) {
346 #ifdef DEBUG_KRB5
347 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
348 "failed to query winbindd\n",
349 (unsigned int)getpid());
350 #endif
351 goto failed;
353 } else {
354 const char *env = NULL;
355 char *var = NULL;
356 if (asprintf(&var, "%s_%s",
357 WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
358 goto failed;
360 env = getenv(var);
361 if (!env) {
362 #ifdef DEBUG_KRB5
363 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
364 "failed to get kdc from env %s\n",
365 (unsigned int)getpid(), var);
366 #endif
367 free(var);
368 goto failed;
370 free(var);
372 kdc_name = strdup(env);
373 if (!kdc_name) {
374 goto failed;
377 #ifdef DEBUG_KRB5
378 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
379 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
380 kdc_name, realm);
381 #endif
383 aihints.ai_family = family;
384 aihints.ai_socktype = socktype;
386 ret = smb_krb5_locator_call_cbfunc(kdc_name,
387 service,
388 &aihints,
389 cbfunc, cbdata);
390 SAFE_FREE(kdc_name);
392 return ret;
394 failed:
395 return KRB5_PLUGIN_NO_HANDLE;
398 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
399 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
400 #else
401 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
402 #endif
404 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
405 0, /* version */
406 smb_krb5_locator_init,
407 smb_krb5_locator_close,
408 smb_krb5_locator_lookup,
411 #endif