Add missing end tags
[Samba/bjacke.git] / source / nsswitch / winbind_krb5_locator.c
blob33a68f0cdc25c603acff94674876b3bccdb1cc36
1 /*
2 Unix SMB/CIFS implementation.
3 kerberos locator plugin
4 Copyright (C) Guenther Deschner 2007
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"
22 #ifndef DEBUG_KRB5
23 #undef DEBUG_KRB5
24 #endif
26 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
28 #include <krb5/locate_plugin.h>
30 #ifndef KRB5_PLUGIN_NO_HANDLE
31 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
32 #endif
34 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
36 switch (svc) {
37 case locate_service_kdc:
38 case locate_service_master_kdc:
39 return "88";
40 case locate_service_kadmin:
41 case locate_service_krb524:
42 /* not supported */
43 return NULL;
44 case locate_service_kpasswd:
45 return "464";
46 default:
47 break;
49 return NULL;
53 #ifdef DEBUG_KRB5
54 static const char *locate_service_type_name(enum locate_service_type svc)
56 switch (svc) {
57 case locate_service_kdc:
58 return "locate_service_kdc";
59 case locate_service_master_kdc:
60 return "locate_service_master_kdc";
61 case locate_service_kadmin:
62 return "locate_service_kadmin";
63 case locate_service_krb524:
64 return "locate_service_krb524";
65 case locate_service_kpasswd:
66 return "locate_service_kpasswd";
67 default:
68 break;
70 return NULL;
73 static const char *socktype_name(int socktype)
75 switch (socktype) {
76 case SOCK_STREAM:
77 return "SOCK_STREAM";
78 case SOCK_DGRAM:
79 return "SOCK_DGRAM";
80 default:
81 break;
83 return "unknown";
86 static const char *family_name(int family)
88 switch (family) {
89 case AF_UNSPEC:
90 return "AF_UNSPEC";
91 case AF_INET:
92 return "AF_INET";
93 #if defined(HAVE_IPV6)
94 case AF_INET6:
95 return "AF_INET6";
96 #endif
97 default:
98 break;
100 return "unknown";
102 #endif
105 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
107 * @param svc
108 * @param realm string
109 * @param socktype integer
110 * @param family integer
112 * @return integer.
115 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
116 const char *realm,
117 int socktype,
118 int family)
120 if (!realm || strlen(realm) == 0) {
121 return EINVAL;
124 switch (svc) {
125 case locate_service_kdc:
126 case locate_service_master_kdc:
127 case locate_service_kpasswd:
128 break;
129 case locate_service_kadmin:
130 case locate_service_krb524:
131 return KRB5_PLUGIN_NO_HANDLE;
132 default:
133 return EINVAL;
136 switch (family) {
137 case AF_UNSPEC:
138 case AF_INET:
139 break;
140 #if defined(HAVE_IPV6)
141 case AF_INET6:
142 break;
143 #endif
144 default:
145 return EINVAL;
148 switch (socktype) {
149 case SOCK_STREAM:
150 case SOCK_DGRAM:
151 case 0: /* Heimdal uses that */
152 break;
153 default:
154 return EINVAL;
157 return 0;
161 * Try to get addrinfo for a given host and call the krb5 callback
163 * @param name string
164 * @param service string
165 * @param in struct addrinfo hint
166 * @param cbfunc krb5 callback function
167 * @param cbdata void pointer cbdata
169 * @return krb5_error_code.
172 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
173 const char *service,
174 struct addrinfo *in,
175 int (*cbfunc)(void *, int, struct sockaddr *),
176 void *cbdata)
178 struct addrinfo *out = NULL;
179 int ret;
180 int count = 3;
182 while (count) {
184 ret = getaddrinfo(name, service, in, &out);
185 if (ret == 0) {
186 break;
189 if (ret == EAI_AGAIN) {
190 count--;
191 continue;
194 #ifdef DEBUG_KRB5
195 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
196 "getaddrinfo failed: %s (%d)\n",
197 (unsigned int)getpid(), gai_strerror(ret), ret);
198 #endif
200 return KRB5_PLUGIN_NO_HANDLE;
203 ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
204 #ifdef DEBUG_KRB5
205 if (ret) {
206 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
207 "failed to call callback: %s (%d)\n",
208 (unsigned int)getpid(), error_message(ret), ret);
210 #endif
212 freeaddrinfo(out);
213 return ret;
217 * PUBLIC INTERFACE: locate init
219 * @param context krb5_context
220 * @param privata_data pointer to private data pointer
222 * @return krb5_error_code.
225 krb5_error_code smb_krb5_locator_init(krb5_context context,
226 void **private_data)
228 return 0;
232 * PUBLIC INTERFACE: close locate
234 * @param private_data pointer to private data
236 * @return void.
239 void smb_krb5_locator_close(void *private_data)
241 return;
245 static bool ask_winbind(const char *realm, char **dcname)
247 NSS_STATUS status;
248 struct winbindd_request request;
249 struct winbindd_response response;
251 ZERO_STRUCT(request);
252 ZERO_STRUCT(response);
254 request.flags = 0x40020600;
255 /* DS_KDC_REQUIRED |
256 DS_IS_DNS_NAME |
257 DS_RETURN_DNS_NAME |
258 DS_IP_REQUIRED */
260 strncpy(request.domain_name, realm,
261 sizeof(request.domain_name)-1);
263 status = winbindd_request_response(WINBINDD_DSGETDCNAME,
264 &request, &response);
265 if (status != NSS_STATUS_SUCCESS) {
266 #ifdef DEBUG_KRB5
267 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
268 (unsigned int)getpid(), nss_err_str(status));
269 #endif
270 return false;
273 *dcname = strdup(response.data.dc_name);
274 if (!*dcname) {
275 return false;
278 return true;
282 * PUBLIC INTERFACE: locate lookup
284 * @param private_data pointer to private data
285 * @param svc enum locate_service_type.
286 * @param realm string
287 * @param socktype integer
288 * @param family integer
289 * @param cbfunc callback function to send back entries
290 * @param cbdata void pointer to cbdata
292 * @return krb5_error_code.
295 krb5_error_code smb_krb5_locator_lookup(void *private_data,
296 enum locate_service_type svc,
297 const char *realm,
298 int socktype,
299 int family,
300 int (*cbfunc)(void *, int, struct sockaddr *),
301 void *cbdata)
303 krb5_error_code ret;
304 struct addrinfo aihints;
305 char *kdc_name = NULL;
306 const char *service = get_service_from_locate_service_type(svc);
308 ZERO_STRUCT(aihints);
310 #ifdef DEBUG_KRB5
311 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
312 "svc: '%s' (%d) "
313 "socktype: '%s' (%d), family: '%s' (%d)\n",
314 (unsigned int)getpid(), realm,
315 locate_service_type_name(svc), svc,
316 socktype_name(socktype), socktype,
317 family_name(family), family);
318 #endif
319 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
320 family);
321 if (ret) {
322 #ifdef DEBUG_KRB5
323 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
324 "returning ret: %s (%d)\n",
325 (unsigned int)getpid(), error_message(ret), ret);
326 #endif
327 return ret;
330 if (!winbind_env_set()) {
331 if (!ask_winbind(realm, &kdc_name)) {
332 #ifdef DEBUG_KRB5
333 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
334 "failed to query winbindd\n",
335 (unsigned int)getpid());
336 #endif
337 goto failed;
339 } else {
340 const char *env = NULL;
341 char *var = NULL;
342 if (asprintf(&var, "%s_%s",
343 WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
344 goto failed;
346 env = getenv(var);
347 if (!env) {
348 #ifdef DEBUG_KRB5
349 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
350 "failed to get kdc from env %s\n",
351 (unsigned int)getpid(), var);
352 #endif
353 free(var);
354 goto failed;
356 free(var);
358 kdc_name = strdup(env);
359 if (!kdc_name) {
360 goto failed;
363 #ifdef DEBUG_KRB5
364 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
365 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
366 kdc_name, realm);
367 #endif
369 aihints.ai_family = family;
370 aihints.ai_socktype = socktype;
372 ret = smb_krb5_locator_call_cbfunc(kdc_name,
373 service,
374 &aihints,
375 cbfunc, cbdata);
376 SAFE_FREE(kdc_name);
378 return ret;
380 failed:
381 return KRB5_PLUGIN_NO_HANDLE;
384 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
385 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
386 #else
387 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
388 #endif
390 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
391 0, /* version */
392 smb_krb5_locator_init,
393 smb_krb5_locator_close,
394 smb_krb5_locator_lookup,
397 #endif