s3/smbd: add new toplevel vfs_fstreaminfo wrapper
[Samba.git] / nsswitch / krb5_plugin / async_dns_krb5_locator.c
blob959d730a954cade3bedb56219f4d71a24c27260b
1 /*
2 Unix SMB/CIFS implementation.
3 Async DNS kerberos locator plugin
4 Copyright (C) Guenther Deschner 2007-2008
5 Copyright (C) Jeremy Allison 2020.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "../../source3/include/includes.h"
22 #include "../../source3/libsmb/namequery.h"
24 #ifndef DEBUG_KRB5
25 #undef DEBUG_KRB5
26 #endif
28 /* Uncomment to debug. */
29 /* #define DEBUG_KRB5 1 */
31 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
33 #ifdef HAVE_COM_ERR_H
34 #include <com_err.h>
35 #endif
37 #include <krb5.h>
38 #include <krb5/locate_plugin.h>
40 #ifndef KRB5_PLUGIN_NO_HANDLE
41 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
42 #endif
44 struct singleton_realm_kdc_list_cache {
45 char *realm;
46 struct samba_sockaddr *kdc_list;
47 size_t num_kdcs;
50 static struct singleton_realm_kdc_list_cache *scache;
52 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
54 switch (svc) {
55 case locate_service_kdc:
56 case locate_service_master_kdc:
57 return "88";
58 case locate_service_kadmin:
59 case locate_service_krb524:
60 /* not supported */
61 return NULL;
62 case locate_service_kpasswd:
63 return "464";
64 default:
65 break;
67 return NULL;
71 #ifdef DEBUG_KRB5
72 static const char *locate_service_type_name(enum locate_service_type svc)
74 switch (svc) {
75 case locate_service_kdc:
76 return "locate_service_kdc";
77 case locate_service_master_kdc:
78 return "locate_service_master_kdc";
79 case locate_service_kadmin:
80 return "locate_service_kadmin";
81 case locate_service_krb524:
82 return "locate_service_krb524";
83 case locate_service_kpasswd:
84 return "locate_service_kpasswd";
85 default:
86 break;
88 return NULL;
91 static const char *socktype_name(int socktype)
93 switch (socktype) {
94 case SOCK_STREAM:
95 return "SOCK_STREAM";
96 case SOCK_DGRAM:
97 return "SOCK_DGRAM";
98 default:
99 break;
101 return "unknown";
104 static const char *family_name(int family)
106 switch (family) {
107 case AF_UNSPEC:
108 return "AF_UNSPEC";
109 case AF_INET:
110 return "AF_INET";
111 #if defined(HAVE_IPV6)
112 case AF_INET6:
113 return "AF_INET6";
114 #endif
115 default:
116 break;
118 return "unknown";
120 #endif
123 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
125 * @param svc
126 * @param realm string
127 * @param socktype integer
128 * @param family integer
130 * @return integer.
133 static int smb_krb5_adns_locator_lookup_sanity_check(
134 enum locate_service_type svc,
135 const char *realm,
136 int socktype,
137 int family)
139 if (!realm || strlen(realm) == 0) {
140 return EINVAL;
143 switch (svc) {
144 case locate_service_kdc:
145 case locate_service_master_kdc:
146 break;
147 case locate_service_kadmin:
148 case locate_service_krb524:
149 case locate_service_kpasswd:
150 return KRB5_PLUGIN_NO_HANDLE;
151 default:
152 return EINVAL;
155 switch (family) {
156 case AF_UNSPEC:
157 case AF_INET:
158 #if defined(HAVE_IPV6)
159 case AF_INET6:
160 #endif
161 break;
162 default:
163 return EINVAL;
166 switch (socktype) {
167 case SOCK_STREAM:
168 case SOCK_DGRAM:
169 case 0: /* Heimdal uses that */
170 break;
171 default:
172 return EINVAL;
175 return 0;
179 * Call back into the MIT libraries with each address
180 * we found. Assume AD-DC's always support both UDP and
181 * TCP port 88 for KDC service.
184 static krb5_error_code smb_krb5_adns_locator_call_cbfunc(
185 struct samba_sockaddr *kdcs,
186 size_t num_kdcs,
187 const char *service,
188 int socktype,
189 int (*cbfunc)(void *, int, struct sockaddr *),
190 void *cbdata)
192 int ret = 0;
193 size_t i;
195 for (i = 0; i < num_kdcs; i++) {
196 struct sockaddr *sa = NULL;
198 if (kdcs[i].u.ss.ss_family == AF_INET) {
199 struct sockaddr_in *sin = &kdcs[i].u.in;
200 sin->sin_family = AF_INET;
201 sin->sin_port = htons(88);
202 sa = &kdcs[i].u.sa;
204 #if defined(HAVE_IPV6)
205 if (kdcs[i].u.ss.ss_family == AF_INET6) {
206 struct sockaddr_in6 *sin6 = &kdcs[i].u.in6;
207 sin6->sin6_family = AF_INET6;
208 sin6->sin6_port = htons(88);
209 sa = &kdcs[i].u.sa;
211 #else
212 else {
213 return KRB5_PLUGIN_NO_HANDLE;
215 #endif
217 #ifdef DEBUG_KRB5
219 char addr[INET6_ADDRSTRLEN];
220 fprintf(stderr, "[%5u]: "
221 "smb_krb5_adns_locator_call_cbfunc: "
222 "IP[%zu] %s\n",
223 (unsigned int)getpid(),
225 print_sockaddr(addr,
226 sizeof(addr),
227 &kdcs[i].u.ss));
229 #endif
231 /* Assume all AD-DC's do both UDP and TCP on port 88. */
232 ret = cbfunc(cbdata, socktype, sa);
233 if (ret) {
234 #ifdef DEBUG_KRB5
235 fprintf(stderr, "[%5u]: "
236 "smb_krb5_adns_locator_call_cbfunc: "
237 "failed to call callback: %s (%d)\n",
238 (unsigned int)getpid(),
239 error_message(ret),
240 ret);
241 #endif
242 break;
245 return ret;
249 * PUBLIC INTERFACE: locate init
251 * @param context krb5_context
252 * @param privata_data pointer to private data pointer
254 * @return krb5_error_code.
257 static krb5_error_code smb_krb5_adns_locator_init(krb5_context context,
258 void **private_data)
260 static bool loaded_config;
261 if (!loaded_config) {
262 lp_load_global(get_dyn_CONFIGFILE());
263 loaded_config = true;
265 #ifdef DEBUG_KRB5
266 fprintf(stderr,"[%5u]: smb_krb5_adns_locator_init\n",
267 (unsigned int)getpid());
268 #endif
269 return 0;
273 * PUBLIC INTERFACE: close locate
275 * @param private_data pointer to private data
277 * @return void.
280 static void smb_krb5_adns_locator_close(void *private_data)
282 #ifdef DEBUG_KRB5
283 fprintf(stderr,"[%5u]: smb_krb5_adns_locator_close\n",
284 (unsigned int)getpid());
285 #endif
286 return;
290 * PUBLIC INTERFACE: locate lookup
292 * @param private_data pointer to private data
293 * @param svc enum locate_service_type.
294 * @param realm string
295 * @param socktype integer
296 * @param family integer
297 * @param cbfunc callback function to send back entries
298 * @param cbdata void pointer to cbdata
300 * @return krb5_error_code.
303 static krb5_error_code smb_krb5_adns_locator_lookup(void *private_data,
304 enum locate_service_type svc,
305 const char *realm,
306 int socktype,
307 int family,
308 int (*cbfunc)(void *, int, struct sockaddr *),
309 void *cbdata)
311 krb5_error_code ret;
312 const char *service = get_service_from_locate_service_type(svc);
314 #ifdef DEBUG_KRB5
315 fprintf(stderr,"[%5u]: smb_krb5_adns_locator_lookup: called for '%s' "
316 "svc: '%s' (%d) "
317 "socktype: '%s' (%d), family: '%s' (%d)\n",
318 (unsigned int)getpid(),
319 realm,
320 locate_service_type_name(svc),
321 svc,
322 socktype_name(socktype),
323 socktype,
324 family_name(family),
325 family);
326 #endif
327 ret = smb_krb5_adns_locator_lookup_sanity_check(svc,
328 realm,
329 socktype,
330 family);
331 if (ret) {
332 #ifdef DEBUG_KRB5
333 fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: "
334 "returning ret: %s (%d)\n",
335 (unsigned int)getpid(),
336 error_message(ret),
337 ret);
338 #endif
339 return ret;
343 * If is a subsequent lookup for the same realm
344 * and we have a cache for this already, don't re-do
345 * the DNS SRV -> A/AAAA lookups.
347 * kinit does this a lot, it looks for UDP then TCP.
350 if ((scache == NULL) || strcmp(realm, scache->realm) != 0) {
351 /* Cache is NULL or a different realm lookup. */
352 NTSTATUS status;
355 * We have a new lookup to do. As it's a singleton
356 * cache make sure we have no old cache.
358 TALLOC_FREE(scache);
360 scache = talloc_zero(NULL,
361 struct singleton_realm_kdc_list_cache);
362 if (scache == NULL) {
363 return KRB5_PLUGIN_NO_HANDLE;
365 scache->realm = talloc_strdup(scache, realm);
366 if (scache->realm == NULL) {
367 TALLOC_FREE(scache);
368 return KRB5_PLUGIN_NO_HANDLE;
371 status = get_kdc_list(scache,
372 realm,
373 NULL,
374 &scache->kdc_list,
375 &scache->num_kdcs);
376 if (!NT_STATUS_IS_OK(status)) {
377 #ifdef DEBUG_KRB5
378 fprintf(stderr, "[%5u]: "
379 "smb_krb5_adns_locator_lookup: "
380 "get_kdc_list() for realm %s failed "
381 "with %s\n",
382 (unsigned int)getpid(),
383 realm,
384 nt_errstr(status));
385 #endif
386 TALLOC_FREE(scache);
387 return KRB5_PLUGIN_NO_HANDLE;
389 if (scache->num_kdcs == 0) {
390 TALLOC_FREE(scache);
391 return KRB5_PLUGIN_NO_HANDLE;
394 #ifdef DEBUG_KRB5
395 else {
396 fprintf(stderr, "[%5u]: "
397 "smb_krb5_adns_locator_lookup: "
398 "returning cached data for realm %s\n",
399 (unsigned int)getpid(),
400 realm);
402 #endif
404 * If we get here we know scache contains the right
405 * realm and non-null address list.
408 #ifdef DEBUG_KRB5
409 fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: "
410 "got %zu IP addresses for realm %s\n",
411 (unsigned int)getpid(),
412 scache->num_kdcs,
413 scache->realm);
414 #endif
417 * Don't free kdc list on success, we're
418 * always returning from the cache.
420 return smb_krb5_adns_locator_call_cbfunc(scache->kdc_list,
421 scache->num_kdcs,
422 service,
423 socktype,
424 cbfunc,
425 cbdata);
428 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
429 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
430 #else
431 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
432 #endif
434 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
435 .minor_version = 0,
436 .init = smb_krb5_adns_locator_init,
437 .fini = smb_krb5_adns_locator_close,
438 #ifdef KRB5_PLUGIN_LOCATE_VERSION_2
439 .old_lookup = smb_krb5_adns_locator_lookup,
440 #else
441 .lookup = smb_krb5_adns_locator_lookup,
442 #endif
445 #endif