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 #ifdef 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
get_service_from_locate_service_type(enum locate_service_type svc)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
locate_service_type_name(enum locate_service_type svc)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
socktype_name(int socktype)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
family_name(int family)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
smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,const char * realm,int socktype,int family)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 #if defined(HAVE_IPV6)
146 case AF_INET6:
147 #endif
148 break;
149 default:
150 return EINVAL;
151 }
152
153 switch (socktype) {
154 case SOCK_STREAM:
155 case SOCK_DGRAM:
156 case 0: /* Heimdal uses that */
157 break;
158 default:
159 return EINVAL;
160 }
161
162 return 0;
163 }
164
165 /**
166 * Try to get addrinfo for a given host and call the krb5 callback
167 *
168 * @param name string
169 * @param service string
170 * @param in struct addrinfo hint
171 * @param cbfunc krb5 callback function
172 * @param cbdata void pointer cbdata
173 *
174 * @return krb5_error_code.
175 */
176
smb_krb5_locator_call_cbfunc(const char * name,const char * service,struct addrinfo * in,int (* cbfunc)(void *,int,struct sockaddr *),void * cbdata)177 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
178 const char *service,
179 struct addrinfo *in,
180 int (*cbfunc)(void *, int, struct sockaddr *),
181 void *cbdata)
182 {
183 struct addrinfo *out = NULL;
184 int ret = 0;
185 struct addrinfo *res = NULL;
186 int count = 3;
187
188 while (count) {
189
190 ret = getaddrinfo(name, service, in, &out);
191 if (ret == 0) {
192 break;
193 }
194
195 if ((ret == EAI_AGAIN) && (count > 1)) {
196 count--;
197 continue;
198 }
199
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
205
206 return KRB5_PLUGIN_NO_HANDLE;
207 }
208
209 for (res = out; res; res = res->ai_next) {
210 if (!res->ai_addr || res->ai_addrlen == 0) {
211 continue;
212 }
213
214 ret = cbfunc(cbdata, res->ai_socktype, res->ai_addr);
215 if (ret) {
216 #ifdef DEBUG_KRB5
217 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
218 "failed to call callback: %s (%d)\n",
219 (unsigned int)getpid(), error_message(ret), ret);
220 #endif
221 break;
222 }
223 }
224
225 if (out) {
226 freeaddrinfo(out);
227 }
228 return ret;
229 }
230
231 /**
232 * PUBLIC INTERFACE: locate init
233 *
234 * @param context krb5_context
235 * @param privata_data pointer to private data pointer
236 *
237 * @return krb5_error_code.
238 */
239
smb_krb5_locator_init(krb5_context context,void ** private_data)240 static krb5_error_code smb_krb5_locator_init(krb5_context context,
241 void **private_data)
242 {
243 return 0;
244 }
245
246 /**
247 * PUBLIC INTERFACE: close locate
248 *
249 * @param private_data pointer to private data
250 *
251 * @return void.
252 */
253
smb_krb5_locator_close(void * private_data)254 static void smb_krb5_locator_close(void *private_data)
255 {
256 return;
257 }
258
259
ask_winbind(const char * realm,char ** dcname)260 static bool ask_winbind(const char *realm, char **dcname)
261 {
262 wbcErr wbc_status;
263 const char *dc = NULL;
264 struct wbcDomainControllerInfoEx *dc_info = NULL;
265 uint32_t flags;
266
267 flags = WBC_LOOKUP_DC_KDC_REQUIRED |
268 WBC_LOOKUP_DC_IS_DNS_NAME |
269 WBC_LOOKUP_DC_RETURN_DNS_NAME;
270
271 wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
272
273 if (!WBC_ERROR_IS_OK(wbc_status)) {
274 #ifdef DEBUG_KRB5
275 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
276 (unsigned int)getpid(), wbcErrorString(wbc_status));
277 #endif
278 return false;
279 }
280
281 if (!dc && dc_info->dc_unc) {
282 dc = dc_info->dc_unc;
283 if (dc[0] == '\\') dc++;
284 if (dc[0] == '\\') dc++;
285 }
286
287 if (!dc) {
288 wbcFreeMemory(dc_info);
289 return false;
290 }
291
292 *dcname = strdup(dc);
293 if (!*dcname) {
294 wbcFreeMemory(dc_info);
295 return false;
296 }
297
298 wbcFreeMemory(dc_info);
299 return true;
300 }
301
302 /**
303 * PUBLIC INTERFACE: locate lookup
304 *
305 * @param private_data pointer to private data
306 * @param svc enum locate_service_type.
307 * @param realm string
308 * @param socktype integer
309 * @param family integer
310 * @param cbfunc callback function to send back entries
311 * @param cbdata void pointer to cbdata
312 *
313 * @return krb5_error_code.
314 */
315
smb_krb5_locator_lookup(void * private_data,enum locate_service_type svc,const char * realm,int socktype,int family,int (* cbfunc)(void *,int,struct sockaddr *),void * cbdata)316 static krb5_error_code smb_krb5_locator_lookup(void *private_data,
317 enum locate_service_type svc,
318 const char *realm,
319 int socktype,
320 int family,
321 int (*cbfunc)(void *, int, struct sockaddr *),
322 void *cbdata)
323 {
324 krb5_error_code ret;
325 struct addrinfo aihints;
326 char *kdc_name = NULL;
327 const char *service = get_service_from_locate_service_type(svc);
328
329 ZERO_STRUCT(aihints);
330
331 #ifdef DEBUG_KRB5
332 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
333 "svc: '%s' (%d) "
334 "socktype: '%s' (%d), family: '%s' (%d)\n",
335 (unsigned int)getpid(), realm,
336 locate_service_type_name(svc), svc,
337 socktype_name(socktype), socktype,
338 family_name(family), family);
339 #endif
340 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
341 family);
342 if (ret) {
343 #ifdef DEBUG_KRB5
344 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
345 "returning ret: %s (%d)\n",
346 (unsigned int)getpid(), error_message(ret), ret);
347 #endif
348 return ret;
349 }
350
351 if (!winbind_env_set()) {
352 if (!ask_winbind(realm, &kdc_name)) {
353 #ifdef DEBUG_KRB5
354 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
355 "failed to query winbindd\n",
356 (unsigned int)getpid());
357 #endif
358 goto failed;
359 }
360 } else {
361 const char *env = NULL;
362 char *var = NULL;
363 if (asprintf(&var, "%s_%s",
364 WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
365 goto failed;
366 }
367 env = getenv(var);
368 if (!env) {
369 #ifdef DEBUG_KRB5
370 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
371 "failed to get kdc from env %s\n",
372 (unsigned int)getpid(), var);
373 #endif
374 free(var);
375 goto failed;
376 }
377 free(var);
378
379 kdc_name = strdup(env);
380 if (!kdc_name) {
381 goto failed;
382 }
383 }
384 #ifdef DEBUG_KRB5
385 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
386 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
387 kdc_name, realm);
388 #endif
389
390 aihints.ai_family = family;
391 aihints.ai_socktype = socktype;
392
393 ret = smb_krb5_locator_call_cbfunc(kdc_name,
394 service,
395 &aihints,
396 cbfunc, cbdata);
397 SAFE_FREE(kdc_name);
398
399 return ret;
400
401 failed:
402 return KRB5_PLUGIN_NO_HANDLE;
403 }
404
405 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
406 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
407 #else
408 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
409 #endif
410
411 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
412 .minor_version = 0,
413 .init = smb_krb5_locator_init,
414 .fini = smb_krb5_locator_close,
415 #ifdef KRB5_PLUGIN_LOCATE_VERSION_2
416 .old_lookup = smb_krb5_locator_lookup,
417 #else
418 .lookup = smb_krb5_locator_lookup,
419 #endif
420 };
421
422 #endif
423