Rename krb5 locator plugin to "winbind_krb5_locator".
[tprouty/samba.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                 case AF_INET6:
94                         return "AF_INET6";
95                 default:
96                         break;
97         }
98         return "unknown";
99 }
100 #endif
101
102 /**
103  * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones
104  *
105  * @param svc
106  * @param realm string
107  * @param socktype integer
108  * @param family integer
109  *
110  * @return integer.
111  */
112
113 static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc,
114                                                 const char *realm,
115                                                 int socktype,
116                                                 int family)
117 {
118         if (!realm || strlen(realm) == 0) {
119                 return EINVAL;
120         }
121
122         switch (svc) {
123                 case locate_service_kdc:
124                 case locate_service_master_kdc:
125                 case locate_service_kpasswd:
126                         break;
127                 case locate_service_kadmin:
128                 case locate_service_krb524:
129                         return KRB5_PLUGIN_NO_HANDLE;
130                 default:
131                         return EINVAL;
132         }
133
134         switch (family) {
135                 case AF_UNSPEC:
136                 case AF_INET:
137                         break;
138                 case AF_INET6: /* not yet */
139                         return KRB5_PLUGIN_NO_HANDLE;
140                 default:
141                         return EINVAL;
142         }
143
144         switch (socktype) {
145                 case SOCK_STREAM:
146                 case SOCK_DGRAM:
147                 case 0: /* Heimdal uses that */
148                         break;
149                 default:
150                         return EINVAL;
151         }
152
153         return 0;
154 }
155
156 /**
157  * Try to get addrinfo for a given host and call the krb5 callback
158  *
159  * @param name string
160  * @param service string
161  * @param in struct addrinfo hint
162  * @param cbfunc krb5 callback function
163  * @param cbdata void pointer cbdata
164  *
165  * @return krb5_error_code.
166  */
167
168 static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name,
169                                                     const char *service,
170                                                     struct addrinfo *in,
171                                                     int (*cbfunc)(void *, int, struct sockaddr *),
172                                                     void *cbdata)
173 {
174         struct addrinfo *out;
175         int ret;
176         int count = 3;
177
178         while (count) {
179
180                 ret = getaddrinfo(name, service, in, &out);
181                 if (ret == 0) {
182                         break;
183                 }
184
185                 if (ret == EAI_AGAIN) {
186                         count--;
187                         continue;
188                 }
189
190 #ifdef DEBUG_KRB5
191                 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
192                         "getaddrinfo failed: %s (%d)\n",
193                         (unsigned int)getpid(), gai_strerror(ret), ret);
194 #endif
195
196                 return KRB5_PLUGIN_NO_HANDLE;
197         }
198
199         ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
200 #ifdef DEBUG_KRB5
201         if (ret) {
202                 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
203                         "failed to call callback: %s (%d)\n",
204                         (unsigned int)getpid(), error_message(ret), ret);
205         }
206 #endif
207
208         freeaddrinfo(out);
209
210         return ret;
211 }
212
213 /**
214  * PUBLIC INTERFACE: locate init
215  *
216  * @param context krb5_context
217  * @param privata_data pointer to private data pointer
218  *
219  * @return krb5_error_code.
220  */
221
222 krb5_error_code smb_krb5_locator_init(krb5_context context,
223                                       void **private_data)
224 {
225         return 0;
226 }
227
228 /**
229  * PUBLIC INTERFACE: close locate
230  *
231  * @param private_data pointer to private data
232  *
233  * @return void.
234  */
235
236 void smb_krb5_locator_close(void *private_data)
237 {
238         return;
239 }
240
241
242 static bool ask_winbind(const char *realm, char **dcname)
243 {
244         NSS_STATUS status;
245         struct winbindd_request request;
246         struct winbindd_response response;
247
248         ZERO_STRUCT(request);
249         ZERO_STRUCT(response);
250
251         request.flags = 0x40020600;
252                         /* DS_KDC_REQUIRED |
253                         DS_IS_DNS_NAME |
254                         DS_RETURN_DNS_NAME |
255                         DS_IP_REQUIRED */
256
257         strncpy(request.domain_name, realm,
258                 sizeof(request.domain_name)-1);
259
260         status = winbindd_request_response(WINBINDD_DSGETDCNAME,
261                                            &request, &response);
262         if (status != NSS_STATUS_SUCCESS) {
263 #ifdef DEBUG_KRB5
264                 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
265                         (unsigned int)getpid(), nss_err_str(status));
266 #endif
267                 return false;
268         }
269
270         *dcname = strdup(response.data.dc_name);
271         if (!*dcname) {
272                 return false;
273         }
274
275         return true;
276 }
277
278 /**
279  * PUBLIC INTERFACE: locate lookup
280  *
281  * @param private_data pointer to private data
282  * @param svc enum locate_service_type.
283  * @param realm string
284  * @param socktype integer
285  * @param family integer
286  * @param cbfunc callback function to send back entries
287  * @param cbdata void pointer to cbdata
288  *
289  * @return krb5_error_code.
290  */
291
292 krb5_error_code smb_krb5_locator_lookup(void *private_data,
293                                         enum locate_service_type svc,
294                                         const char *realm,
295                                         int socktype,
296                                         int family,
297                                         int (*cbfunc)(void *, int, struct sockaddr *),
298                                         void *cbdata)
299 {
300         krb5_error_code ret;
301         struct addrinfo aihints;
302         char *kdc_name = NULL;
303         const char *service = get_service_from_locate_service_type(svc);
304
305         ZERO_STRUCT(aihints);
306
307 #ifdef DEBUG_KRB5
308         fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' "
309                         "svc: '%s' (%d) "
310                         "socktype: '%s' (%d), family: '%s' (%d)\n",
311                         (unsigned int)getpid(), realm,
312                         locate_service_type_name(svc), svc,
313                         socktype_name(socktype), socktype,
314                         family_name(family), family);
315 #endif
316         ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype,
317                                                    family);
318         if (ret) {
319 #ifdef DEBUG_KRB5
320                 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
321                         "returning ret: %s (%d)\n",
322                         (unsigned int)getpid(), error_message(ret), ret);
323 #endif
324                 return ret;
325         }
326
327         if (!winbind_env_set()) {
328                 if (!ask_winbind(realm, &kdc_name)) {
329 #ifdef DEBUG_KRB5
330                         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
331                                 "failed to query winbindd\n",
332                                 (unsigned int)getpid());
333 #endif
334                         goto failed;
335                 }
336         } else {
337                 const char *env = NULL;
338                 char *var = NULL;
339                 if (asprintf(&var, "%s_%s",
340                              WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
341                         goto failed;
342                 }
343                 env = getenv(var);
344                 if (!env) {
345 #ifdef DEBUG_KRB5
346                         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
347                                 "failed to get kdc from env %s\n",
348                                 (unsigned int)getpid(), var);
349 #endif
350                         free(var);
351                         goto failed;
352                 }
353                 free(var);
354
355                 kdc_name = strdup(env);
356                 if (!kdc_name) {
357                         goto failed;
358                 }
359         }
360 #ifdef DEBUG_KRB5
361         fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
362                 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
363                 kdc_name, realm);
364 #endif
365
366         aihints.ai_family = family;
367         aihints.ai_socktype = socktype;
368
369         ret = smb_krb5_locator_call_cbfunc(kdc_name,
370                                            service,
371                                            &aihints,
372                                            cbfunc, cbdata);
373         SAFE_FREE(kdc_name);
374
375         return ret;
376
377  failed:
378         return KRB5_PLUGIN_NO_HANDLE;
379 }
380
381 #ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H
382 #define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */
383 #else
384 #define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */
385 #endif
386
387 const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = {
388         0, /* version */
389         smb_krb5_locator_init,
390         smb_krb5_locator_close,
391         smb_krb5_locator_lookup,
392 };
393
394 #endif