s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[samba.git] / source4 / auth / kerberos / krb5_init_context.c
1 /*
2    Unix SMB/CIFS implementation.
3    Wrapper for krb5_init_context
4
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Stefan Metzmacher 2004
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/kerberos.h"
25 #include <tevent.h>
26 #include "auth/kerberos/kerberos.h"
27 #include "lib/socket/socket.h"
28 #include "lib/stream/packet.h"
29 #include "system/network.h"
30 #include "param/param.h"
31 #include "libcli/resolve/resolve.h"
32 #include "../lib/tsocket/tsocket.h"
33 #include "krb5_init_context.h"
34 #ifdef SAMBA4_USES_HEIMDAL
35 #include "../lib/dbwrap/dbwrap.h"
36 #include "../lib/dbwrap/dbwrap_rbt.h"
37 #include "../lib/util/util_tdb.h"
38 #include <krb5/send_to_kdc_plugin.h>
39 #endif
40
41 /*
42   context structure for operations on cldap packets
43 */
44 struct smb_krb5_socket {
45         struct socket_context *sock;
46
47         /* the fd event */
48         struct tevent_fd *fde;
49
50         NTSTATUS status;
51         DATA_BLOB request, reply;
52
53         struct packet_context *packet;
54
55         size_t partial_read;
56 #ifdef SAMBA4_USES_HEIMDAL
57         krb5_krbhst_info *hi;
58 #endif
59 };
60
61 static krb5_error_code smb_krb5_context_destroy(struct smb_krb5_context *ctx)
62 {
63 #ifdef SAMBA4_USES_HEIMDAL
64         if (ctx->pvt_log_data) {
65                 /* Otherwise krb5_free_context will try and close what we
66                  * have already free()ed */
67                 krb5_set_warn_dest(ctx->krb5_context, NULL);
68                 krb5_closelog(ctx->krb5_context,
69                                 (krb5_log_facility *)ctx->pvt_log_data);
70         }
71 #endif
72         krb5_free_context(ctx->krb5_context);
73         return 0;
74 }
75
76 #ifdef SAMBA4_USES_HEIMDAL
77 /* We never close down the DEBUG system, and no need to unreference the use */
78 static void smb_krb5_debug_close(void *private_data) {
79         return;
80 }
81 #endif
82
83 #ifdef SAMBA4_USES_HEIMDAL
84 static void smb_krb5_debug_wrapper(
85 #ifdef HAVE_KRB5_ADDLOG_FUNC_NEED_CONTEXT
86                 krb5_context ctx,
87 #endif /* HAVE_KRB5_ADDLOG_FUNC_NEED_CONTEXT */
88                 const char *timestr, const char *msg, void *private_data)
89 {
90         DEBUGC(DBGC_KERBEROS, 3, ("Kerberos: %s\n", msg));
91 }
92 #endif
93
94 #ifdef SAMBA4_USES_HEIMDAL
95 /*
96   handle recv events on a smb_krb5 socket
97 */
98 static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
99 {
100         TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
101         DATA_BLOB blob;
102         size_t nread, dsize;
103
104         smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
105         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
106                 talloc_free(tmp_ctx);
107                 return;
108         }
109
110         blob = data_blob_talloc(tmp_ctx, NULL, dsize);
111         if (blob.data == NULL && dsize != 0) {
112                 smb_krb5->status = NT_STATUS_NO_MEMORY;
113                 talloc_free(tmp_ctx);
114                 return;
115         }
116
117         smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
118         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
119                 talloc_free(tmp_ctx);
120                 return;
121         }
122         blob.length = nread;
123
124         if (nread == 0) {
125                 smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
126                 talloc_free(tmp_ctx);
127                 return;
128         }
129
130         DEBUG(4,("Received smb_krb5 packet of length %d\n",
131                  (int)blob.length));
132
133         talloc_steal(smb_krb5, blob.data);
134         smb_krb5->reply = blob;
135         talloc_free(tmp_ctx);
136 }
137
138 static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
139 {
140         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
141         talloc_steal(smb_krb5, data.data);
142         smb_krb5->reply = data;
143         smb_krb5->reply.length -= 4;
144         smb_krb5->reply.data += 4;
145         return NT_STATUS_OK;
146 }
147
148 /*
149   handle request timeouts
150 */
151 static void smb_krb5_request_timeout(struct tevent_context *event_ctx,
152                                   struct tevent_timer *te, struct timeval t,
153                                   void *private_data)
154 {
155         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
156         DEBUG(5,("Timed out smb_krb5 packet\n"));
157         smb_krb5->status = NT_STATUS_IO_TIMEOUT;
158 }
159
160 static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
161 {
162         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
163         smb_krb5->status = status;
164 }
165
166 /*
167   handle send events on a smb_krb5 socket
168 */
169 static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
170 {
171         NTSTATUS status;
172
173         size_t len;
174
175         len = smb_krb5->request.length;
176         status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
177
178         if (!NT_STATUS_IS_OK(status)) return;
179
180         TEVENT_FD_READABLE(smb_krb5->fde);
181
182         TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
183         return;
184 }
185
186
187 /*
188   handle fd events on a smb_krb5_socket
189 */
190 static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
191                                  uint16_t flags, void *private_data)
192 {
193         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
194         switch (smb_krb5->hi->proto) {
195         case KRB5_KRBHST_UDP:
196                 if (flags & TEVENT_FD_READ) {
197                         smb_krb5_socket_recv(smb_krb5);
198                         return;
199                 }
200                 if (flags & TEVENT_FD_WRITE) {
201                         smb_krb5_socket_send(smb_krb5);
202                         return;
203                 }
204                 /* not reached */
205                 return;
206         case KRB5_KRBHST_TCP:
207                 if (flags & TEVENT_FD_READ) {
208                         packet_recv(smb_krb5->packet);
209                         return;
210                 }
211                 if (flags & TEVENT_FD_WRITE) {
212                         packet_queue_run(smb_krb5->packet);
213                         return;
214                 }
215                 /* not reached */
216                 return;
217         case KRB5_KRBHST_HTTP:
218                 /* can't happen */
219                 break;
220         }
221 }
222
223 static krb5_error_code smb_krb5_send_and_recv_func_int(struct smb_krb5_context *smb_krb5_context,
224                                                        struct tevent_context *ev,
225                                                        krb5_krbhst_info *hi,
226                                                        struct addrinfo *ai,
227                                                        smb_krb5_send_to_kdc_func func,
228                                                        void *data,
229                                                        time_t timeout,
230                                                        const krb5_data *send_buf,
231                                                        krb5_data *recv_buf)
232 {
233         krb5_error_code ret;
234         NTSTATUS status;
235         const char *name;
236         struct addrinfo *a;
237         struct smb_krb5_socket *smb_krb5;
238
239         DATA_BLOB send_blob;
240
241         TALLOC_CTX *frame = talloc_stackframe();
242         if (frame == NULL) {
243                 return ENOMEM;
244         }
245
246         send_blob = data_blob_const(send_buf->data, send_buf->length);
247
248         for (a = ai; a; a = a->ai_next) {
249                 struct socket_address *remote_addr;
250                 smb_krb5 = talloc(frame, struct smb_krb5_socket);
251                 if (!smb_krb5) {
252                         TALLOC_FREE(frame);
253                         return ENOMEM;
254                 }
255                 smb_krb5->hi = hi;
256
257                 switch (a->ai_family) {
258                 case PF_INET:
259                         name = "ipv4";
260                         break;
261 #ifdef HAVE_IPV6
262                 case PF_INET6:
263                         name = "ipv6";
264                         break;
265 #endif
266                 default:
267                         TALLOC_FREE(frame);
268                         return EINVAL;
269                 }
270
271                 status = NT_STATUS_INVALID_PARAMETER;
272                 switch (hi->proto) {
273                 case KRB5_KRBHST_UDP:
274                         status = socket_create(smb_krb5, name,
275                                                SOCKET_TYPE_DGRAM,
276                                                &smb_krb5->sock, 0);
277                         break;
278                 case KRB5_KRBHST_TCP:
279                         status = socket_create(smb_krb5, name,
280                                                SOCKET_TYPE_STREAM,
281                                                &smb_krb5->sock, 0);
282                         break;
283                 case KRB5_KRBHST_HTTP:
284                         TALLOC_FREE(frame);
285                         return EINVAL;
286                 }
287                 if (!NT_STATUS_IS_OK(status)) {
288                         talloc_free(smb_krb5);
289                         continue;
290                 }
291
292                 remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
293                 if (!remote_addr) {
294                         talloc_free(smb_krb5);
295                         continue;
296                 }
297
298                 status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
299                 if (!NT_STATUS_IS_OK(status)) {
300                         talloc_free(smb_krb5);
301                         continue;
302                 }
303
304                 /* Setup the FDE, start listening for read events
305                  * from the start (otherwise we may miss a socket
306                  * drop) and mark as AUTOCLOSE along with the fde */
307
308                 /* This is equivalent to EVENT_FD_READABLE(smb_krb5->fde) */
309                 smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
310                                               socket_get_fd(smb_krb5->sock),
311                                               TEVENT_FD_READ,
312                                               smb_krb5_socket_handler, smb_krb5);
313                 /* its now the job of the event layer to close the socket */
314                 tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
315                 socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
316
317                 tevent_add_timer(ev, smb_krb5,
318                                  timeval_current_ofs(timeout, 0),
319                                  smb_krb5_request_timeout, smb_krb5);
320
321                 smb_krb5->status = NT_STATUS_OK;
322                 smb_krb5->reply = data_blob(NULL, 0);
323
324                 switch (hi->proto) {
325                 case KRB5_KRBHST_UDP:
326                         TEVENT_FD_WRITEABLE(smb_krb5->fde);
327                         smb_krb5->request = send_blob;
328                         break;
329                 case KRB5_KRBHST_TCP:
330
331                         smb_krb5->packet = packet_init(smb_krb5);
332                         if (smb_krb5->packet == NULL) {
333                                 talloc_free(smb_krb5);
334                                 return ENOMEM;
335                         }
336                         packet_set_private(smb_krb5->packet, smb_krb5);
337                         packet_set_socket(smb_krb5->packet, smb_krb5->sock);
338                         packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
339                         packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
340                         packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
341                         packet_set_event_context(smb_krb5->packet, ev);
342                         packet_set_fde(smb_krb5->packet, smb_krb5->fde);
343
344                         smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
345                         RSIVAL(smb_krb5->request.data, 0, send_blob.length);
346                         memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
347                         packet_send(smb_krb5->packet, smb_krb5->request);
348                         break;
349                 case KRB5_KRBHST_HTTP:
350                         TALLOC_FREE(frame);
351                         return EINVAL;
352                 }
353                 while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
354                         if (tevent_loop_once(ev) != 0) {
355                                 TALLOC_FREE(frame);
356                                 return EINVAL;
357                         }
358
359                         if (func) {
360                                 /* After each and every event loop, reset the
361                                  * send_to_kdc pointers to what they were when
362                                  * we entered this loop.  That way, if a
363                                  * nested event has invalidated them, we put
364                                  * it back before we return to the heimdal
365                                  * code */
366                                 ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
367                                                                     NULL, /* send_to_realm */
368                                                                     func,
369                                                                     data);
370                                 if (ret != 0) {
371                                         TALLOC_FREE(frame);
372                                         return ret;
373                                 }
374                         }
375                 }
376                 if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
377                         talloc_free(smb_krb5);
378                         continue;
379                 }
380
381                 if (!NT_STATUS_IS_OK(smb_krb5->status)) {
382                         struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr);
383                         const char *addr_string = NULL;
384                         if (addr) {
385                                 addr_string = tsocket_address_inet_addr_string(addr, smb_krb5);
386                         } else {
387                                 addr_string = NULL;
388                         }
389                         DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status),
390                                  addr_string));
391                         talloc_free(smb_krb5);
392                         continue;
393                 }
394
395                 ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
396                 if (ret) {
397                         TALLOC_FREE(frame);
398                         return ret;
399                 }
400                 talloc_free(smb_krb5);
401
402                 break;
403         }
404         TALLOC_FREE(frame);
405         if (a) {
406                 return 0;
407         }
408         return KRB5_KDC_UNREACH;
409 }
410
411 krb5_error_code smb_krb5_send_and_recv_func(struct smb_krb5_context *smb_krb5_context,
412                                             void *data,
413                                             krb5_krbhst_info *hi,
414                                             time_t timeout,
415                                             const krb5_data *send_buf,
416                                             krb5_data *recv_buf)
417 {
418         krb5_error_code ret;
419         struct addrinfo *ai;
420
421         struct tevent_context *ev;
422         TALLOC_CTX *frame = talloc_stackframe();
423         if (frame == NULL) {
424                 return ENOMEM;
425         }
426
427         if (data == NULL) {
428                 /* If no event context was available, then create one for this loop */
429                 ev = samba_tevent_context_init(frame);
430                 if (ev == NULL) {
431                         TALLOC_FREE(frame);
432                         return ENOMEM;
433                 }
434         } else {
435                 ev = talloc_get_type_abort(data, struct tevent_context);
436         }
437
438         ret = krb5_krbhst_get_addrinfo(smb_krb5_context->krb5_context, hi, &ai);
439         if (ret) {
440                 TALLOC_FREE(frame);
441                 return ret;
442         }
443
444         ret = smb_krb5_send_and_recv_func_int(smb_krb5_context,
445                                               ev, hi, ai,
446                                               smb_krb5_send_and_recv_func,
447                                               data, timeout, send_buf, recv_buf);
448         TALLOC_FREE(frame);
449         return ret;
450 }
451
452 krb5_error_code smb_krb5_send_and_recv_func_forced_tcp(struct smb_krb5_context *smb_krb5_context,
453                                                        struct addrinfo *ai,
454                                                        time_t timeout,
455                                                        const krb5_data *send_buf,
456                                                        krb5_data *recv_buf)
457 {
458         krb5_error_code k5ret;
459         krb5_krbhst_info hi = {
460                 .proto = KRB5_KRBHST_TCP,
461         };
462         struct tevent_context *ev;
463         TALLOC_CTX *frame = talloc_stackframe();
464         if (frame == NULL) {
465                 return ENOMEM;
466         }
467
468         /* no event context is passed in, create one for this loop */
469         ev = samba_tevent_context_init(frame);
470         if (ev == NULL) {
471                 TALLOC_FREE(frame);
472                 return ENOMEM;
473         }
474
475         /* No need to pass in send_and_recv functions, we won't nest on this private event loop */
476         k5ret = smb_krb5_send_and_recv_func_int(smb_krb5_context, ev, &hi, ai, NULL, NULL,
477                                                 timeout, send_buf, recv_buf);
478         TALLOC_FREE(frame);
479         return k5ret;
480 }
481
482 static struct db_context *smb_krb5_plugin_db;
483
484 struct smb_krb5_send_to_kdc_state {
485         intptr_t key_ptr;
486         struct smb_krb5_context *smb_krb5_context;
487         smb_krb5_send_to_realm_func send_to_realm;
488         smb_krb5_send_to_kdc_func send_to_kdc;
489         void *private_data;
490 };
491
492 static int smb_krb5_send_to_kdc_state_destructor(struct smb_krb5_send_to_kdc_state *state)
493 {
494         TDB_DATA key = make_tdb_data((uint8_t *)&state->key_ptr, sizeof(state->key_ptr));
495         NTSTATUS status;
496
497         status = dbwrap_delete(smb_krb5_plugin_db, key);
498         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
499                 status = NT_STATUS_OK;
500         }
501         if (!NT_STATUS_IS_OK(status)) {
502                 return -1;
503         }
504
505         state->smb_krb5_context = NULL;
506         return 0;
507 }
508
509 krb5_error_code smb_krb5_set_send_to_kdc_func(struct smb_krb5_context *smb_krb5_context,
510                                               smb_krb5_send_to_realm_func send_to_realm,
511                                               smb_krb5_send_to_kdc_func send_to_kdc,
512                                               void *private_data)
513 {
514         intptr_t key_ptr = (intptr_t)smb_krb5_context->krb5_context;
515         TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
516         intptr_t value_ptr = (intptr_t)NULL;
517         TDB_DATA value = make_tdb_data(NULL, 0);
518         struct db_record *rec = NULL;
519         struct smb_krb5_send_to_kdc_state *state = NULL;
520         NTSTATUS status;
521
522         rec = dbwrap_fetch_locked(smb_krb5_plugin_db, smb_krb5_context, key);
523         if (rec == NULL) {
524                 return ENOMEM;
525         }
526
527         value = dbwrap_record_get_value(rec);
528         if (value.dsize != 0) {
529                 SMB_ASSERT(value.dsize == sizeof(value_ptr));
530                 memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
531                 state = talloc_get_type_abort((const void *)value_ptr,
532                                               struct smb_krb5_send_to_kdc_state);
533                 if (send_to_realm == NULL && send_to_kdc == NULL) {
534                         status = dbwrap_record_delete(rec);
535                         TALLOC_FREE(rec);
536                         if (!NT_STATUS_IS_OK(status)) {
537                                 return EINVAL;
538                         }
539                         return 0;
540                 }
541                 state->send_to_realm = send_to_realm;
542                 state->send_to_kdc = send_to_kdc;
543                 state->private_data = private_data;
544                 TALLOC_FREE(rec);
545                 return 0;
546         }
547
548         if (send_to_kdc == NULL && send_to_realm == NULL) {
549                 TALLOC_FREE(rec);
550                 return 0;
551         }
552
553         state = talloc_zero(smb_krb5_context,
554                             struct smb_krb5_send_to_kdc_state);
555         if (state == NULL) {
556                 TALLOC_FREE(rec);
557                 return ENOMEM;
558         }
559         state->key_ptr = key_ptr;
560         state->smb_krb5_context = smb_krb5_context;
561         state->send_to_realm = send_to_realm;
562         state->send_to_kdc = send_to_kdc;
563         state->private_data = private_data;
564
565         value_ptr = (intptr_t)state;
566         value = make_tdb_data((uint8_t *)&value_ptr, sizeof(value_ptr));
567
568         status = dbwrap_record_store(rec, value, TDB_INSERT);
569         TALLOC_FREE(rec);
570         if (!NT_STATUS_IS_OK(status)) {
571                 return EINVAL;
572         }
573         talloc_set_destructor(state, smb_krb5_send_to_kdc_state_destructor);
574
575         return 0;
576 }
577
578 static krb5_error_code smb_krb5_plugin_init(krb5_context context, void **pctx)
579 {
580         *pctx = NULL;
581         return 0;
582 }
583
584 static void smb_krb5_plugin_fini(void *ctx)
585 {
586 }
587
588 static void smb_krb5_send_to_kdc_state_parser(TDB_DATA key, TDB_DATA value,
589                                               void *private_data)
590 {
591         struct smb_krb5_send_to_kdc_state **state =
592                 (struct smb_krb5_send_to_kdc_state **)private_data;
593         intptr_t value_ptr;
594
595         SMB_ASSERT(value.dsize == sizeof(value_ptr));
596         memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
597         *state = talloc_get_type_abort((const void *)value_ptr,
598                                        struct smb_krb5_send_to_kdc_state);
599 }
600
601 static struct smb_krb5_send_to_kdc_state *
602 smb_krb5_send_to_kdc_get_state(krb5_context context)
603 {
604         intptr_t key_ptr = (intptr_t)context;
605         TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
606         struct smb_krb5_send_to_kdc_state *state = NULL;
607         NTSTATUS status;
608
609         status = dbwrap_parse_record(smb_krb5_plugin_db, key,
610                                      smb_krb5_send_to_kdc_state_parser,
611                                      &state);
612         if (!NT_STATUS_IS_OK(status)) {
613                 return NULL;
614         }
615
616         return state;
617 }
618
619 static krb5_error_code smb_krb5_plugin_send_to_kdc(krb5_context context,
620                                                    void *ctx,
621                                                    krb5_krbhst_info *ho,
622                                                    time_t timeout,
623                                                    const krb5_data *in,
624                                                    krb5_data *out)
625 {
626         struct smb_krb5_send_to_kdc_state *state = NULL;
627
628         state = smb_krb5_send_to_kdc_get_state(context);
629         if (state == NULL) {
630                 return KRB5_PLUGIN_NO_HANDLE;
631         }
632
633         if (state->send_to_kdc == NULL) {
634                 return KRB5_PLUGIN_NO_HANDLE;
635         }
636
637         return state->send_to_kdc(state->smb_krb5_context,
638                                   state->private_data,
639                                   ho, timeout, in, out);
640 }
641
642 static krb5_error_code smb_krb5_plugin_send_to_realm(krb5_context context,
643                                                      void *ctx,
644                                                      krb5_const_realm realm,
645                                                      time_t timeout,
646                                                      const krb5_data *in,
647                                                      krb5_data *out)
648 {
649         struct smb_krb5_send_to_kdc_state *state = NULL;
650
651         state = smb_krb5_send_to_kdc_get_state(context);
652         if (state == NULL) {
653                 return KRB5_PLUGIN_NO_HANDLE;
654         }
655
656         if (state->send_to_realm == NULL) {
657                 return KRB5_PLUGIN_NO_HANDLE;
658         }
659
660         return state->send_to_realm(state->smb_krb5_context,
661                                     state->private_data,
662                                     realm, timeout, in, out);
663 }
664
665 static krb5plugin_send_to_kdc_ftable smb_krb5_plugin_ftable = {
666         KRB5_PLUGIN_SEND_TO_KDC_VERSION_2,
667         smb_krb5_plugin_init,
668         smb_krb5_plugin_fini,
669         smb_krb5_plugin_send_to_kdc,
670         smb_krb5_plugin_send_to_realm
671 };
672 #endif
673
674 krb5_error_code
675 smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
676                             struct loadparm_context *lp_ctx,
677                             krb5_context *_krb5_context)
678 {
679         krb5_error_code ret;
680 #ifdef SAMBA4_USES_HEIMDAL
681         char **config_files;
682         const char *config_file, *realm;
683 #endif
684         krb5_context krb5_ctx;
685
686         ret = smb_krb5_init_context_common(&krb5_ctx);
687         if (ret) {
688                 return ret;
689         }
690
691         /* The MIT Kerberos build relies on using the system krb5.conf file.
692          * If you really want to use another file please set KRB5_CONFIG
693          * accordingly. */
694 #ifdef SAMBA4_USES_HEIMDAL
695         config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf");
696         if (!config_file) {
697                 krb5_free_context(krb5_ctx);
698                 return ENOMEM;
699         }
700
701         /* Use our local krb5.conf file by default */
702         ret = krb5_prepend_config_files_default(config_file, &config_files);
703         if (ret) {
704                 DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
705                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
706                 krb5_free_context(krb5_ctx);
707                 return ret;
708         }
709
710         ret = krb5_set_config_files(krb5_ctx, config_files);
711         krb5_free_config_files(config_files);
712         if (ret) {
713                 DEBUG(1,("krb5_set_config_files failed (%s)\n",
714                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
715                 krb5_free_context(krb5_ctx);
716                 return ret;
717         }
718
719         /*
720          * This is already called in smb_krb5_init_context_common(),
721          * but krb5_set_config_files() may resets it.
722          */
723         krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
724
725         realm = lpcfg_realm(lp_ctx);
726         if (realm != NULL) {
727                 ret = krb5_set_default_realm(krb5_ctx, realm);
728                 if (ret) {
729                         DEBUG(1,("krb5_set_default_realm failed (%s)\n",
730                                  smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
731                         krb5_free_context(krb5_ctx);
732                         return ret;
733                 }
734         }
735
736         if (smb_krb5_plugin_db == NULL) {
737                 /*
738                  * while krb5_plugin_register() takes a krb5_context,
739                  * plugins are registered into a global list, so
740                  * we only do that once
741                  *
742                  * We maintain a separate dispatch table for per
743                  * krb5_context state.
744                  */
745                 ret = krb5_plugin_register(krb5_ctx, PLUGIN_TYPE_DATA,
746                                      KRB5_PLUGIN_SEND_TO_KDC,
747                                      &smb_krb5_plugin_ftable);
748                 if (ret) {
749                         DEBUG(1,("krb5_plugin_register(KRB5_PLUGIN_SEND_TO_KDC) failed (%s)\n",
750                                  smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
751                         krb5_free_context(krb5_ctx);
752                         return ret;
753                 }
754                 smb_krb5_plugin_db = db_open_rbt(NULL);
755                 if (smb_krb5_plugin_db == NULL) {
756                         DEBUG(1,("db_open_rbt() failed\n"));
757                         krb5_free_context(krb5_ctx);
758                         return ENOMEM;
759                 }
760         }
761 #endif
762         *_krb5_context = krb5_ctx;
763         return 0;
764 }
765
766 krb5_error_code smb_krb5_init_context(void *parent_ctx,
767                                       struct loadparm_context *lp_ctx,
768                                       struct smb_krb5_context **smb_krb5_context)
769 {
770         krb5_error_code ret;
771         TALLOC_CTX *tmp_ctx;
772         krb5_context kctx;
773 #ifdef SAMBA4_USES_HEIMDAL
774         krb5_log_facility *logf;
775 #endif
776
777         tmp_ctx = talloc_new(parent_ctx);
778         *smb_krb5_context = talloc_zero(tmp_ctx, struct smb_krb5_context);
779
780         if (!*smb_krb5_context || !tmp_ctx) {
781                 talloc_free(tmp_ctx);
782                 return ENOMEM;
783         }
784
785         ret = smb_krb5_init_context_basic(tmp_ctx, lp_ctx, &kctx);
786         if (ret) {
787                 DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
788                          error_message(ret)));
789                 talloc_free(tmp_ctx);
790                 return ret;
791         }
792         (*smb_krb5_context)->krb5_context = kctx;
793
794         talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);
795
796 #ifdef SAMBA4_USES_HEIMDAL
797         /* TODO: Should we have a different name here? */
798         ret = krb5_initlog(kctx, "Samba", &logf);
799
800         if (ret) {
801                 DEBUG(1,("krb5_initlog failed (%s)\n",
802                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
803                 talloc_free(tmp_ctx);
804                 return ret;
805         }
806         (*smb_krb5_context)->pvt_log_data = logf;
807
808         ret = krb5_addlog_func(kctx, logf, 0 /* min */, -1 /* max */,
809                                smb_krb5_debug_wrapper,
810                                 smb_krb5_debug_close, NULL);
811         if (ret) {
812                 DEBUG(1,("krb5_addlog_func failed (%s)\n",
813                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
814                 talloc_free(tmp_ctx);
815                 return ret;
816         }
817         krb5_set_warn_dest(kctx, logf);
818 #endif
819         talloc_steal(parent_ctx, *smb_krb5_context);
820         talloc_free(tmp_ctx);
821
822         return 0;
823 }
824
825 #ifdef SAMBA4_USES_HEIMDAL
826 krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context,
827                                                struct tevent_context *ev,
828                                                struct tevent_context **previous_ev)
829 {
830         int ret;
831         if (!ev) {
832                 return EINVAL;
833         }
834
835         *previous_ev = smb_krb5_context->current_ev;
836
837         smb_krb5_context->current_ev = talloc_reference(smb_krb5_context, ev);
838         if (!smb_krb5_context->current_ev) {
839                 return ENOMEM;
840         }
841
842         /* Set use of our socket lib */
843         ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
844                                             NULL, /* send_to_realm */
845                                             smb_krb5_send_and_recv_func,
846                                             ev);
847         if (ret) {
848                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
849                 DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
850                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
851                 talloc_free(tmp_ctx);
852                 talloc_unlink(smb_krb5_context, smb_krb5_context->current_ev);
853                 smb_krb5_context->current_ev = NULL;
854                 return ret;
855         }
856         return 0;
857 }
858
859 krb5_error_code smb_krb5_context_remove_event_ctx(struct smb_krb5_context *smb_krb5_context,
860                                                   struct tevent_context *previous_ev,
861                                                   struct tevent_context *ev)
862 {
863         int ret;
864         talloc_unlink(smb_krb5_context, ev);
865         /* If there was a mismatch with things happening on a stack, then don't wipe things */
866         smb_krb5_context->current_ev = previous_ev;
867         /* Set use of our socket lib */
868         ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
869                                             NULL, /* send_to_realm */
870                                             smb_krb5_send_and_recv_func,
871                                             previous_ev);
872         if (ret) {
873                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
874                 DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
875                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
876                 talloc_free(tmp_ctx);
877                 return ret;
878         }
879         return 0;
880 }
881 #endif