r21855: Fix a memleak in the krb5 locator and comment out gfree_all() which doesn't
[jra/samba/.git] / source3 / libads / smb_krb5_locator.c
1 /* 
2    Unix SMB/CIFS implementation.
3    kerberos locator plugin
4    Copyright (C) Guenther Deschner 2007
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
24
25 #include <krb5/locate_plugin.h>
26
27 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
28 {
29         switch (svc) {
30                 case locate_service_kdc:
31                 case locate_service_master_kdc:
32                         return "88";
33                 case locate_service_kadmin:
34                 case locate_service_krb524:
35                         /* not supported */
36                         return NULL;
37                 case locate_service_kpasswd:
38                         return "464";
39                 default:
40                         break;
41         }
42         return NULL;
43
44 }
45
46 static const char *locate_service_type_name(enum locate_service_type svc)
47 {
48         switch (svc) {
49                 case locate_service_kdc:
50                         return "locate_service_kdc";
51                 case locate_service_master_kdc:
52                         return "locate_service_master_kdc";
53                 case locate_service_kadmin:
54                         return "locate_service_kadmin";
55                 case locate_service_krb524:
56                         return "locate_service_krb524";
57                 case locate_service_kpasswd:
58                         return "locate_service_kpasswd";
59                 default:
60                         break;
61         }
62         return NULL;
63 }
64
65 static const char *socktype_name(int socktype)
66 {
67         switch (socktype) {
68                 case SOCK_STREAM:
69                         return "SOCK_STREAM";
70                 case SOCK_DGRAM:
71                         return "SOCK_DGRAM";
72                 default:
73                         break;
74         }
75         return "unknown";
76 }
77
78 static const char *family_name(int family)
79 {
80         switch (family) {
81                 case AF_UNSPEC:
82                         return "AF_UNSPEC";
83                 case AF_INET:
84                         return "AF_INET";
85                 case AF_INET6:
86                         return "AF_INET6";
87                 default:
88                         break;
89         }
90         return "unknown";
91 }
92
93 /**
94  * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
95  *
96  * @param svc 
97  * @param realm string
98  * @param socktype integer
99  * @param family integer
100  *
101  * @return integer.
102  */
103
104 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
105                                                 const char *realm,
106                                                 int socktype,
107                                                 int family)
108 {
109         if (!realm || strlen(realm) == 0) {
110                 return EINVAL;
111         }
112
113         switch (svc) {
114                 case locate_service_kdc:
115                 case locate_service_master_kdc:
116                 case locate_service_kpasswd:
117                         break;
118                 case locate_service_kadmin:
119                 case locate_service_krb524:
120 #ifdef KRB5_PLUGIN_NO_HANDLE
121                         return KRB5_PLUGIN_NO_HANDLE;
122 #else
123                         return KRB5_KDC_UNREACH; /* Heimdal */
124 #endif
125                 default:
126                         return EINVAL;
127         }
128
129         switch (family) {
130                 case AF_UNSPEC:
131                 case AF_INET:
132                         break;
133                 case AF_INET6: /* not yet */
134 #ifdef KRB5_PLUGIN_NO_HANDLE
135                         return KRB5_PLUGIN_NO_HANDLE;
136 #else
137                         return KRB5_KDC_UNREACH; /* Heimdal */
138 #endif
139                 default:
140                         return EINVAL;
141         }
142
143         switch (socktype) {
144                 case SOCK_STREAM:
145                 case SOCK_DGRAM:
146                 case 0: /* Heimdal uses that */
147                         break;
148                 default:
149                         return EINVAL;
150         }
151
152         return 0;
153 }
154
155 /**
156  * Try to get addrinfo for a given host and call the krb5 callback
157  *
158  * @param name string
159  * @param service string
160  * @param in struct addrinfo hint
161  * @param cbfunc krb5 callback function
162  * @param cbdata void pointer cbdata
163  *
164  * @return krb5_error_code.
165  */
166
167 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name, 
168                                                     const char *service,
169                                                     struct addrinfo *in,
170                                                     int (*cbfunc)(void *, int, struct sockaddr *),
171                                                     void *cbdata)
172 {
173         struct addrinfo *out;
174         int ret;
175         int count = 3;
176
177         while (count) {
178
179                 ret = getaddrinfo(name, service, in, &out);
180                 if (ret == 0) {
181                         break;
182                 }
183
184                 if (ret == EAI_AGAIN) {
185                         count--;
186                         continue;
187                 }
188
189                 DEBUG(10,("smb_krb5_locator_lookup: got ret: %s (%d)\n", 
190                         gai_strerror(ret), ret));
191 #ifdef KRB5_PLUGIN_NO_HANDLE
192                 return KRB5_PLUGIN_NO_HANDLE;
193 #else
194                 return KRB5_KDC_UNREACH; /* Heimdal */
195 #endif
196         }
197
198         ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
199         if (ret) {
200                 DEBUG(10,("smb_krb5_locator_lookup: failed to call callback: %s (%d)\n", 
201                         error_message(ret), ret));
202         }
203
204         freeaddrinfo(out);
205
206         return ret;
207 }
208
209 /**
210  * PUBLIC INTERFACE: locate init
211  *
212  * @param context krb5_context
213  * @param privata_data pointer to private data pointer
214  *
215  * @return krb5_error_code.
216  */
217
218 krb5_error_code smb_krb5_locator_init(krb5_context context, 
219                                       void **private_data)
220 {
221         setup_logging("smb_krb5_locator", True);
222         load_case_tables();
223         lp_load(dyn_CONFIGFILE,True,False,False,True);
224
225         DEBUG(10,("smb_krb5_locator_init: called\n"));
226
227         return 0;
228 }
229
230 /**
231  * PUBLIC INTERFACE: close locate
232  *
233  * @param private_data pointer to private data
234  *
235  * @return void.
236  */
237
238 void smb_krb5_locator_close(void *private_data)
239 {
240         DEBUG(10,("smb_krb5_locator_close: called\n"));
241
242         /* gfree_all(); */
243 }
244
245 /**
246  * PUBLIC INTERFACE: locate lookup
247  *
248  * @param private_data pointer to private data
249  * @param svc enum locate_service_type.
250  * @param realm string
251  * @param socktype integer
252  * @param family integer
253  * @param cbfunc callback function to send back entries
254  * @param cbdata void pointer to cbdata
255  *
256  * @return krb5_error_code.
257  */
258
259 krb5_error_code smb_krb5_locator_lookup(void *private_data,
260                                         enum locate_service_type svc,
261                                         const char *realm,
262                                         int socktype,
263                                         int family,
264                                         int (*cbfunc)(void *, int, struct sockaddr *),
265                                         void *cbdata)
266 {
267         NTSTATUS status;
268         krb5_error_code ret;
269         char *sitename = NULL;
270         struct ip_service *ip_list;
271         int count = 0;
272         struct addrinfo aihints;
273         char *saf_name = NULL;
274         int i;
275
276         DEBUG(10,("smb_krb5_locator_lookup: called for\n"));
277         DEBUGADD(10,("\tsvc: %s (%d), realm: %s\n", 
278                 locate_service_type_name(svc), svc, realm));
279         DEBUGADD(10,("\tsocktype: %s (%d), family: %s (%d)\n", 
280                 socktype_name(socktype), socktype,
281                 family_name(family), family));
282
283         ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype, family);
284         if (ret) {
285                 DEBUG(10,("smb_krb5_locator_lookup: returning ret: %s (%d)\n", 
286                         error_message(ret), ret));
287                 return ret;
288         }
289
290         /* first try to fetch from SAF cache */
291
292         saf_name = saf_fetch(realm);
293         if (!saf_name || strlen(saf_name) == 0) {
294                 DEBUG(10,("smb_krb5_locator_lookup: no SAF name stored for %s\n", 
295                         realm));
296                 goto find_kdc;
297         }
298
299         DEBUG(10,("smb_krb5_locator_lookup: got %s for %s from SAF cache\n", 
300                 saf_name, realm));
301
302         ZERO_STRUCT(aihints);
303         
304         aihints.ai_family = family;
305         aihints.ai_socktype = socktype;
306
307         ret = smb_krb5_locator_call_cbfunc(saf_name, 
308                                           get_service_from_locate_service_type(svc), 
309                                           &aihints, 
310                                           cbfunc, cbdata);
311         if (ret) {
312                 return ret;
313         }
314
315         return 0;
316
317  find_kdc:
318
319         /* now try to find via site-aware DNS SRV query */
320
321         sitename = sitename_fetch(realm);
322         status = get_kdc_list(realm, sitename, &ip_list, &count);
323
324         /* if we didn't found any KDCs on our site go to the main list */
325
326         if (NT_STATUS_IS_OK(status) && sitename && (count == 0)) {
327                 SAFE_FREE(ip_list);
328                 SAFE_FREE(sitename);
329                 status = get_kdc_list(realm, NULL, &ip_list, &count);
330         }
331
332         SAFE_FREE(sitename);
333
334         if (!NT_STATUS_IS_OK(status)) {
335                 DEBUG(10,("smb_krb5_locator_lookup: got %s (%s)\n",
336                         nt_errstr(status), 
337                         error_message(nt_status_to_krb5(status))));
338 #ifdef KRB5_PLUGIN_NO_HANDLE
339                 return KRB5_PLUGIN_NO_HANDLE;
340 #else
341                 return KRB5_KDC_UNREACH; /* Heimdal */
342 #endif
343         }
344
345         for (i=0; i<count; i++) {
346
347                 const char *host = NULL;
348                 const char *port = NULL;
349
350                 ZERO_STRUCT(aihints);
351
352                 aihints.ai_family = family;
353                 aihints.ai_socktype = socktype;
354
355                 host = inet_ntoa(ip_list[i].ip);
356                 port = get_service_from_locate_service_type(svc);
357
358                 ret = smb_krb5_locator_call_cbfunc(host,
359                                                   port,
360                                                   &aihints, 
361                                                   cbfunc, cbdata);
362                 if (ret) {
363                         /* got error */
364                         break;
365                 }
366         }
367
368         SAFE_FREE(ip_list);
369
370         return ret;
371 }
372
373 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
374 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
375 #else
376 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
377 #endif
378
379 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
380         0, /* version */
381         smb_krb5_locator_init,
382         smb_krb5_locator_close,
383         smb_krb5_locator_lookup,
384 };
385
386 #endif