s4:kerberos: adapt the heimdal send_to_kdc hooks to the send_to_kdc/realm plugin...
[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                 /* Ths is equivilant 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         struct db_record *rec = NULL;
496         NTSTATUS status;
497
498         rec = dbwrap_fetch_locked(smb_krb5_plugin_db, state, key);
499         if (rec == NULL) {
500                 return 0;
501         }
502
503         status = dbwrap_record_delete(rec);
504         TALLOC_FREE(rec);
505         if (!NT_STATUS_IS_OK(status)) {
506                 return -1;
507         }
508
509         state->smb_krb5_context = NULL;
510         return 0;
511 }
512
513 krb5_error_code smb_krb5_set_send_to_kdc_func(struct smb_krb5_context *smb_krb5_context,
514                                               smb_krb5_send_to_realm_func send_to_realm,
515                                               smb_krb5_send_to_kdc_func send_to_kdc,
516                                               void *private_data)
517 {
518         intptr_t key_ptr = (intptr_t)smb_krb5_context->krb5_context;
519         TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
520         intptr_t value_ptr = (intptr_t)NULL;
521         TDB_DATA value = make_tdb_data(NULL, 0);
522         struct db_record *rec = NULL;
523         struct smb_krb5_send_to_kdc_state *state = NULL;
524         NTSTATUS status;
525
526         rec = dbwrap_fetch_locked(smb_krb5_plugin_db, smb_krb5_context, key);
527         if (rec == NULL) {
528                 return ENOMEM;
529         }
530
531         value = dbwrap_record_get_value(rec);
532         if (value.dsize != 0) {
533                 SMB_ASSERT(value.dsize == sizeof(value_ptr));
534                 memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
535                 state = talloc_get_type_abort((const void *)value_ptr,
536                                               struct smb_krb5_send_to_kdc_state);
537                 if (send_to_realm == NULL && send_to_kdc == NULL) {
538                         status = dbwrap_record_delete(rec);
539                         TALLOC_FREE(rec);
540                         if (!NT_STATUS_IS_OK(status)) {
541                                 return EINVAL;
542                         }
543                         return 0;
544                 }
545                 state->send_to_realm = send_to_realm;
546                 state->send_to_kdc = send_to_kdc;
547                 state->private_data = private_data;
548                 TALLOC_FREE(rec);
549                 return 0;
550         }
551
552         if (send_to_kdc == NULL && send_to_realm == NULL) {
553                 TALLOC_FREE(rec);
554                 return 0;
555         }
556
557         state = talloc_zero(smb_krb5_context,
558                             struct smb_krb5_send_to_kdc_state);
559         if (state == NULL) {
560                 TALLOC_FREE(rec);
561                 return ENOMEM;
562         }
563         state->key_ptr = key_ptr;
564         state->smb_krb5_context = smb_krb5_context;
565         state->send_to_realm = send_to_realm;
566         state->send_to_kdc = send_to_kdc;
567         state->private_data = private_data;
568
569         value_ptr = (intptr_t)state;
570         value = make_tdb_data((uint8_t *)&value_ptr, sizeof(value_ptr));
571
572         status = dbwrap_record_store(rec, value, TDB_INSERT);
573         TALLOC_FREE(rec);
574         if (!NT_STATUS_IS_OK(status)) {
575                 return EINVAL;
576         }
577         talloc_set_destructor(state, smb_krb5_send_to_kdc_state_destructor);
578
579         return 0;
580 }
581
582 static krb5_error_code smb_krb5_plugin_init(krb5_context context, void **pctx)
583 {
584         *pctx = NULL;
585         return 0;
586 }
587
588 static void smb_krb5_plugin_fini(void *ctx)
589 {
590 }
591
592 static void smb_krb5_send_to_kdc_state_parser(TDB_DATA key, TDB_DATA value,
593                                               void *private_data)
594 {
595         struct smb_krb5_send_to_kdc_state **state =
596                 (struct smb_krb5_send_to_kdc_state **)private_data;
597         intptr_t value_ptr;
598
599         SMB_ASSERT(value.dsize == sizeof(value_ptr));
600         memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
601         *state = talloc_get_type_abort((const void *)value_ptr,
602                                        struct smb_krb5_send_to_kdc_state);
603 }
604
605 static struct smb_krb5_send_to_kdc_state *
606 smb_krb5_send_to_kdc_get_state(krb5_context context)
607 {
608         intptr_t key_ptr = (intptr_t)context;
609         TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
610         struct smb_krb5_send_to_kdc_state *state = NULL;
611         NTSTATUS status;
612
613         status = dbwrap_parse_record(smb_krb5_plugin_db, key,
614                                      smb_krb5_send_to_kdc_state_parser,
615                                      &state);
616         if (!NT_STATUS_IS_OK(status)) {
617                 return NULL;
618         }
619
620         return state;
621 }
622
623 static krb5_error_code smb_krb5_plugin_send_to_kdc(krb5_context context,
624                                                    void *ctx,
625                                                    krb5_krbhst_info *ho,
626                                                    time_t timeout,
627                                                    const krb5_data *in,
628                                                    krb5_data *out)
629 {
630         struct smb_krb5_send_to_kdc_state *state = NULL;
631
632         state = smb_krb5_send_to_kdc_get_state(context);
633         if (state == NULL) {
634                 return KRB5_PLUGIN_NO_HANDLE;
635         }
636
637         if (state->send_to_kdc == NULL) {
638                 return KRB5_PLUGIN_NO_HANDLE;
639         }
640
641         return state->send_to_kdc(state->smb_krb5_context,
642                                   state->private_data,
643                                   ho, timeout, in, out);
644 }
645
646 static krb5_error_code smb_krb5_plugin_send_to_realm(krb5_context context,
647                                                      void *ctx,
648                                                      krb5_const_realm realm,
649                                                      time_t timeout,
650                                                      const krb5_data *in,
651                                                      krb5_data *out)
652 {
653         struct smb_krb5_send_to_kdc_state *state = NULL;
654
655         state = smb_krb5_send_to_kdc_get_state(context);
656         if (state == NULL) {
657                 return KRB5_PLUGIN_NO_HANDLE;
658         }
659
660         if (state->send_to_realm == NULL) {
661                 return KRB5_PLUGIN_NO_HANDLE;
662         }
663
664         return state->send_to_realm(state->smb_krb5_context,
665                                     state->private_data,
666                                     realm, timeout, in, out);
667 }
668
669 static krb5plugin_send_to_kdc_ftable smb_krb5_plugin_ftable = {
670         KRB5_PLUGIN_SEND_TO_KDC_VERSION_2,
671         smb_krb5_plugin_init,
672         smb_krb5_plugin_fini,
673         smb_krb5_plugin_send_to_kdc,
674         smb_krb5_plugin_send_to_realm
675 };
676 #endif
677
678 krb5_error_code
679 smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
680                             struct loadparm_context *lp_ctx,
681                             krb5_context *_krb5_context)
682 {
683         krb5_error_code ret;
684 #ifdef SAMBA4_USES_HEIMDAL
685         char **config_files;
686         const char *config_file, *realm;
687 #endif
688         krb5_context krb5_ctx;
689
690         ret = smb_krb5_init_context_common(&krb5_ctx);
691         if (ret) {
692                 return ret;
693         }
694
695         /* The MIT Kerberos build relies on using the system krb5.conf file.
696          * If you really want to use another file please set KRB5_CONFIG
697          * accordingly. */
698 #ifdef SAMBA4_USES_HEIMDAL
699         config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf");
700         if (!config_file) {
701                 krb5_free_context(krb5_ctx);
702                 return ENOMEM;
703         }
704
705         /* Use our local krb5.conf file by default */
706         ret = krb5_prepend_config_files_default(config_file, &config_files);
707         if (ret) {
708                 DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
709                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
710                 krb5_free_context(krb5_ctx);
711                 return ret;
712         }
713
714         ret = krb5_set_config_files(krb5_ctx, config_files);
715         krb5_free_config_files(config_files);
716         if (ret) {
717                 DEBUG(1,("krb5_set_config_files failed (%s)\n",
718                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
719                 krb5_free_context(krb5_ctx);
720                 return ret;
721         }
722
723         /*
724          * This is already called in smb_krb5_init_context_common(),
725          * but krb5_set_config_files() may resets it.
726          */
727         krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
728
729         realm = lpcfg_realm(lp_ctx);
730         if (realm != NULL) {
731                 ret = krb5_set_default_realm(krb5_ctx, realm);
732                 if (ret) {
733                         DEBUG(1,("krb5_set_default_realm failed (%s)\n",
734                                  smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
735                         krb5_free_context(krb5_ctx);
736                         return ret;
737                 }
738         }
739
740         if (smb_krb5_plugin_db == NULL) {
741                 /*
742                  * while krb5_plugin_register() takes a krb5_context,
743                  * plugins are registered into a global list, so
744                  * we only do that once
745                  *
746                  * We maintain a separate dispatch table for per
747                  * krb5_context state.
748                  */
749                 ret = krb5_plugin_register(krb5_ctx, PLUGIN_TYPE_DATA,
750                                      KRB5_PLUGIN_SEND_TO_KDC,
751                                      &smb_krb5_plugin_ftable);
752                 if (ret) {
753                         DEBUG(1,("krb5_plugin_register(KRB5_PLUGIN_SEND_TO_KDC) failed (%s)\n",
754                                  smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
755                         krb5_free_context(krb5_ctx);
756                         return ret;
757                 }
758                 smb_krb5_plugin_db = db_open_rbt(NULL);
759                 if (smb_krb5_plugin_db == NULL) {
760                         DEBUG(1,("db_open_rbt() failed\n"));
761                         krb5_free_context(krb5_ctx);
762                         return ENOMEM;
763                 }
764         }
765 #endif
766         *_krb5_context = krb5_ctx;
767         return 0;
768 }
769
770 krb5_error_code smb_krb5_init_context(void *parent_ctx,
771                                       struct loadparm_context *lp_ctx,
772                                       struct smb_krb5_context **smb_krb5_context)
773 {
774         krb5_error_code ret;
775         TALLOC_CTX *tmp_ctx;
776         krb5_context kctx;
777 #ifdef SAMBA4_USES_HEIMDAL
778         krb5_log_facility *logf;
779 #endif
780
781         tmp_ctx = talloc_new(parent_ctx);
782         *smb_krb5_context = talloc_zero(tmp_ctx, struct smb_krb5_context);
783
784         if (!*smb_krb5_context || !tmp_ctx) {
785                 talloc_free(tmp_ctx);
786                 return ENOMEM;
787         }
788
789         ret = smb_krb5_init_context_basic(tmp_ctx, lp_ctx, &kctx);
790         if (ret) {
791                 DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
792                          error_message(ret)));
793                 talloc_free(tmp_ctx);
794                 return ret;
795         }
796         (*smb_krb5_context)->krb5_context = kctx;
797
798         talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);
799
800 #ifdef SAMBA4_USES_HEIMDAL
801         /* TODO: Should we have a different name here? */
802         ret = krb5_initlog(kctx, "Samba", &logf);
803
804         if (ret) {
805                 DEBUG(1,("krb5_initlog failed (%s)\n",
806                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
807                 talloc_free(tmp_ctx);
808                 return ret;
809         }
810         (*smb_krb5_context)->pvt_log_data = logf;
811
812         ret = krb5_addlog_func(kctx, logf, 0 /* min */, -1 /* max */,
813                                smb_krb5_debug_wrapper,
814                                 smb_krb5_debug_close, NULL);
815         if (ret) {
816                 DEBUG(1,("krb5_addlog_func failed (%s)\n",
817                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
818                 talloc_free(tmp_ctx);
819                 return ret;
820         }
821         krb5_set_warn_dest(kctx, logf);
822 #endif
823         talloc_steal(parent_ctx, *smb_krb5_context);
824         talloc_free(tmp_ctx);
825
826         return 0;
827 }
828
829 #ifdef SAMBA4_USES_HEIMDAL
830 krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context,
831                                                struct tevent_context *ev,
832                                                struct tevent_context **previous_ev)
833 {
834         int ret;
835         if (!ev) {
836                 return EINVAL;
837         }
838
839         *previous_ev = smb_krb5_context->current_ev;
840
841         smb_krb5_context->current_ev = talloc_reference(smb_krb5_context, ev);
842         if (!smb_krb5_context->current_ev) {
843                 return ENOMEM;
844         }
845
846         /* Set use of our socket lib */
847         ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
848                                             NULL, /* send_to_realm */
849                                             smb_krb5_send_and_recv_func,
850                                             ev);
851         if (ret) {
852                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
853                 DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
854                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
855                 talloc_free(tmp_ctx);
856                 talloc_unlink(smb_krb5_context, smb_krb5_context->current_ev);
857                 smb_krb5_context->current_ev = NULL;
858                 return ret;
859         }
860         return 0;
861 }
862
863 krb5_error_code smb_krb5_context_remove_event_ctx(struct smb_krb5_context *smb_krb5_context,
864                                                   struct tevent_context *previous_ev,
865                                                   struct tevent_context *ev)
866 {
867         int ret;
868         talloc_unlink(smb_krb5_context, ev);
869         /* If there was a mismatch with things happening on a stack, then don't wipe things */
870         smb_krb5_context->current_ev = previous_ev;
871         /* Set use of our socket lib */
872         ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
873                                             NULL, /* send_to_realm */
874                                             smb_krb5_send_and_recv_func,
875                                             previous_ev);
876         if (ret) {
877                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
878                 DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
879                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
880                 talloc_free(tmp_ctx);
881                 return ret;
882         }
883         return 0;
884 }
885 #endif