Remove remaining embedded JavaScript support.
[bbaumbach/samba-autobuild/.git] / source3 / nsswitch / winbind_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 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
22 #ifndef DEBUG_KRB5
23 #undef DEBUG_KRB5
24 #endif
25
26 #if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
27
28 #include <krb5/locate_plugin.h>
29
30 #ifndef KRB5_PLUGIN_NO_HANDLE
31 #define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
32 #endif
33
34 static const char *get_service_from_locate_service_type(enum locate_service_type svc)
35 {
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;
48         }
49         return NULL;
50
51 }
52
53 #ifdef DEBUG_KRB5
54 static const char *locate_service_type_name(enum locate_service_type svc)
55 {
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;
69         }
70         return NULL;
71 }
72
73 static const char *socktype_name(int socktype)
74 {
75         switch (socktype) {
76                 case SOCK_STREAM:
77                         return "SOCK_STREAM";
78                 case SOCK_DGRAM:
79                         return "SOCK_DGRAM";
80                 default:
81                         break;
82         }
83         return "unknown";
84 }
85
86 static const char *family_name(int family)
87 {
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;
99         }
100         return "unknown";
101 }
102 #endif
103
104 /**
105  * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
106  *
107  * @param svc
108  * @param realm string
109  * @param socktype integer
110  * @param family integer
111  *
112  * @return integer.
113  */
114
115 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
116                                                 const char *realm,
117                                                 int socktype,
118                                                 int family)
119 {
120         if (!realm || strlen(realm) == 0) {
121                 return EINVAL;
122         }
123
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;
134         }
135
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;
146         }
147
148         switch (socktype) {
149                 case SOCK_STREAM:
150                 case SOCK_DGRAM:
151                 case 0: /* Heimdal uses that */
152                         break;
153                 default:
154                         return EINVAL;
155         }
156
157         return 0;
158 }
159
160 /**
161  * Try to get addrinfo for a given host and call the krb5 callback
162  *
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
168  *
169  * @return krb5_error_code.
170  */
171
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)
177 {
178         struct addrinfo *out = NULL;
179         int ret;
180         int count = 3;
181
182         while (count) {
183
184                 ret = getaddrinfo(name, service, in, &out);
185                 if (ret == 0) {
186                         break;
187                 }
188
189                 if (ret == EAI_AGAIN) {
190                         count--;
191                         continue;
192                 }
193
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
199
200                 return KRB5_PLUGIN_NO_HANDLE;
201         }
202
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);
209         }
210 #endif
211
212         freeaddrinfo(out);
213         return ret;
214 }
215
216 /**
217  * PUBLIC INTERFACE: locate init
218  *
219  * @param context krb5_context
220  * @param privata_data pointer to private data pointer
221  *
222  * @return krb5_error_code.
223  */
224
225 static krb5_error_code smb_krb5_locator_init(krb5_context context,
226                                              void **private_data)
227 {
228         return 0;
229 }
230
231 /**
232  * PUBLIC INTERFACE: close locate
233  *
234  * @param private_data pointer to private data
235  *
236  * @return void.
237  */
238
239 static void smb_krb5_locator_close(void *private_data)
240 {
241         return;
242 }
243
244
245 static bool ask_winbind(const char *realm, char **dcname)
246 {
247         NSS_STATUS status;
248         struct winbindd_request request;
249         struct winbindd_response response;
250
251         ZERO_STRUCT(request);
252         ZERO_STRUCT(response);
253
254         request.flags = 0x40020600;
255                         /* DS_KDC_REQUIRED |
256                         DS_IS_DNS_NAME |
257                         DS_RETURN_DNS_NAME |
258                         DS_IP_REQUIRED */
259
260         strncpy(request.domain_name, realm,
261                 sizeof(request.domain_name)-1);
262
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;
271         }
272
273         *dcname = strdup(response.data.dc_name);
274         if (!*dcname) {
275                 return false;
276         }
277
278         return true;
279 }
280
281 /**
282  * PUBLIC INTERFACE: locate lookup
283  *
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
291  *
292  * @return krb5_error_code.
293  */
294
295 static 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)
302 {
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);
307
308         ZERO_STRUCT(aihints);
309
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;
328         }
329
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;
338                 }
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;
345                 }
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;
355                 }
356                 free(var);
357
358                 kdc_name = strdup(env);
359                 if (!kdc_name) {
360                         goto failed;
361                 }
362         }
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
368
369         aihints.ai_family = family;
370         aihints.ai_socktype = socktype;
371
372         ret = smb_krb5_locator_call_cbfunc(kdc_name,
373                                            service,
374                                            &aihints,
375                                            cbfunc, cbdata);
376         SAFE_FREE(kdc_name);
377
378         return ret;
379
380  failed:
381         return KRB5_PLUGIN_NO_HANDLE;
382 }
383
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
389
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,
395 };
396
397 #endif