2 Unix SMB/CIFS implementation.
3 Wrapper for krb5_init_context
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2004
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.
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.
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/>.
24 #include "system/kerberos.h"
25 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "lib/socket/socket.h"
29 #include "lib/stream/packet.h"
30 #include "system/network.h"
31 #include "param/param.h"
32 #include "libcli/resolve/resolve.h"
33 #include "../lib/tsocket/tsocket.h"
34 #include "krb5_init_context.h"
36 context structure for operations on cldap packets
38 struct smb_krb5_socket
{
39 struct socket_context
*sock
;
42 struct tevent_fd
*fde
;
45 DATA_BLOB request
, reply
;
47 struct packet_context
*packet
;
50 #ifdef SAMBA4_USES_HEIMDAL
55 static krb5_error_code
smb_krb5_context_destroy(struct smb_krb5_context
*ctx
)
57 #ifdef SAMBA4_USES_HEIMDAL
58 if (ctx
->pvt_log_data
) {
59 /* Otherwise krb5_free_context will try and close what we
60 * have already free()ed */
61 krb5_set_warn_dest(ctx
->krb5_context
, NULL
);
62 krb5_closelog(ctx
->krb5_context
,
63 (krb5_log_facility
*)ctx
->pvt_log_data
);
66 krb5_set_trace_callback(ctx
->krb5_context
, NULL
, NULL
);
68 krb5_free_context(ctx
->krb5_context
);
72 #ifdef SAMBA4_USES_HEIMDAL
73 /* We never close down the DEBUG system, and no need to unreference the use */
74 static void smb_krb5_debug_close(void *private_data
) {
79 #ifdef SAMBA4_USES_HEIMDAL
80 static void smb_krb5_debug_wrapper(const char *timestr
, const char *msg
, void *private_data
)
82 DEBUG(3, ("Kerberos: %s\n", msg
));
85 static void smb_krb5_debug_wrapper(krb5_context context
,
86 const struct krb5_trace_info
*info
,
89 DEBUG(3, ("Kerberos: %s\n", info
->message
));
93 #ifdef SAMBA4_USES_HEIMDAL
95 handle recv events on a smb_krb5 socket
97 static void smb_krb5_socket_recv(struct smb_krb5_socket
*smb_krb5
)
99 TALLOC_CTX
*tmp_ctx
= talloc_new(smb_krb5
);
103 smb_krb5
->status
= socket_pending(smb_krb5
->sock
, &dsize
);
104 if (!NT_STATUS_IS_OK(smb_krb5
->status
)) {
105 talloc_free(tmp_ctx
);
109 blob
= data_blob_talloc(tmp_ctx
, NULL
, dsize
);
110 if (blob
.data
== NULL
&& dsize
!= 0) {
111 smb_krb5
->status
= NT_STATUS_NO_MEMORY
;
112 talloc_free(tmp_ctx
);
116 smb_krb5
->status
= socket_recv(smb_krb5
->sock
, blob
.data
, blob
.length
, &nread
);
117 if (!NT_STATUS_IS_OK(smb_krb5
->status
)) {
118 talloc_free(tmp_ctx
);
124 smb_krb5
->status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
125 talloc_free(tmp_ctx
);
129 DEBUG(4,("Received smb_krb5 packet of length %d\n",
132 talloc_steal(smb_krb5
, blob
.data
);
133 smb_krb5
->reply
= blob
;
134 talloc_free(tmp_ctx
);
137 static NTSTATUS
smb_krb5_full_packet(void *private_data
, DATA_BLOB data
)
139 struct smb_krb5_socket
*smb_krb5
= talloc_get_type(private_data
, struct smb_krb5_socket
);
140 talloc_steal(smb_krb5
, data
.data
);
141 smb_krb5
->reply
= data
;
142 smb_krb5
->reply
.length
-= 4;
143 smb_krb5
->reply
.data
+= 4;
148 handle request timeouts
150 static void smb_krb5_request_timeout(struct tevent_context
*event_ctx
,
151 struct tevent_timer
*te
, struct timeval t
,
154 struct smb_krb5_socket
*smb_krb5
= talloc_get_type(private_data
, struct smb_krb5_socket
);
155 DEBUG(5,("Timed out smb_krb5 packet\n"));
156 smb_krb5
->status
= NT_STATUS_IO_TIMEOUT
;
159 static void smb_krb5_error_handler(void *private_data
, NTSTATUS status
)
161 struct smb_krb5_socket
*smb_krb5
= talloc_get_type(private_data
, struct smb_krb5_socket
);
162 smb_krb5
->status
= status
;
166 handle send events on a smb_krb5 socket
168 static void smb_krb5_socket_send(struct smb_krb5_socket
*smb_krb5
)
174 len
= smb_krb5
->request
.length
;
175 status
= socket_send(smb_krb5
->sock
, &smb_krb5
->request
, &len
);
177 if (!NT_STATUS_IS_OK(status
)) return;
179 TEVENT_FD_READABLE(smb_krb5
->fde
);
181 TEVENT_FD_NOT_WRITEABLE(smb_krb5
->fde
);
187 handle fd events on a smb_krb5_socket
189 static void smb_krb5_socket_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
190 uint16_t flags
, void *private_data
)
192 struct smb_krb5_socket
*smb_krb5
= talloc_get_type(private_data
, struct smb_krb5_socket
);
193 switch (smb_krb5
->hi
->proto
) {
194 case KRB5_KRBHST_UDP
:
195 if (flags
& TEVENT_FD_READ
) {
196 smb_krb5_socket_recv(smb_krb5
);
199 if (flags
& TEVENT_FD_WRITE
) {
200 smb_krb5_socket_send(smb_krb5
);
205 case KRB5_KRBHST_TCP
:
206 if (flags
& TEVENT_FD_READ
) {
207 packet_recv(smb_krb5
->packet
);
210 if (flags
& TEVENT_FD_WRITE
) {
211 packet_queue_run(smb_krb5
->packet
);
216 case KRB5_KRBHST_HTTP
:
222 krb5_error_code
smb_krb5_send_and_recv_func(krb5_context context
,
224 krb5_krbhst_info
*hi
,
226 const krb5_data
*send_buf
,
232 struct addrinfo
*ai
, *a
;
233 struct smb_krb5_socket
*smb_krb5
;
237 struct tevent_context
*ev
;
238 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
244 /* If no event context was available, then create one for this loop */
245 ev
= tevent_context_init(tmp_ctx
);
247 talloc_free(tmp_ctx
);
251 ev
= talloc_get_type_abort(data
, struct tevent_context
);
254 send_blob
= data_blob_const(send_buf
->data
, send_buf
->length
);
256 ret
= krb5_krbhst_get_addrinfo(context
, hi
, &ai
);
258 talloc_free(tmp_ctx
);
262 for (a
= ai
; a
; a
= a
->ai_next
) {
263 struct socket_address
*remote_addr
;
264 smb_krb5
= talloc(tmp_ctx
, struct smb_krb5_socket
);
266 talloc_free(tmp_ctx
);
271 switch (a
->ai_family
) {
281 talloc_free(tmp_ctx
);
285 status
= NT_STATUS_INVALID_PARAMETER
;
287 case KRB5_KRBHST_UDP
:
288 status
= socket_create(name
, SOCKET_TYPE_DGRAM
, &smb_krb5
->sock
, 0);
290 case KRB5_KRBHST_TCP
:
291 status
= socket_create(name
, SOCKET_TYPE_STREAM
, &smb_krb5
->sock
, 0);
293 case KRB5_KRBHST_HTTP
:
294 talloc_free(tmp_ctx
);
297 if (!NT_STATUS_IS_OK(status
)) {
298 talloc_free(smb_krb5
);
302 talloc_steal(smb_krb5
, smb_krb5
->sock
);
304 remote_addr
= socket_address_from_sockaddr(smb_krb5
, a
->ai_addr
, a
->ai_addrlen
);
306 talloc_free(smb_krb5
);
310 status
= socket_connect_ev(smb_krb5
->sock
, NULL
, remote_addr
, 0, ev
);
311 if (!NT_STATUS_IS_OK(status
)) {
312 talloc_free(smb_krb5
);
316 /* Setup the FDE, start listening for read events
317 * from the start (otherwise we may miss a socket
318 * drop) and mark as AUTOCLOSE along with the fde */
320 /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
321 smb_krb5
->fde
= tevent_add_fd(ev
, smb_krb5
->sock
,
322 socket_get_fd(smb_krb5
->sock
),
324 smb_krb5_socket_handler
, smb_krb5
);
325 /* its now the job of the event layer to close the socket */
326 tevent_fd_set_close_fn(smb_krb5
->fde
, socket_tevent_fd_close_fn
);
327 socket_set_flags(smb_krb5
->sock
, SOCKET_FLAG_NOCLOSE
);
329 tevent_add_timer(ev
, smb_krb5
,
330 timeval_current_ofs(timeout
, 0),
331 smb_krb5_request_timeout
, smb_krb5
);
333 smb_krb5
->status
= NT_STATUS_OK
;
334 smb_krb5
->reply
= data_blob(NULL
, 0);
337 case KRB5_KRBHST_UDP
:
338 TEVENT_FD_WRITEABLE(smb_krb5
->fde
);
339 smb_krb5
->request
= send_blob
;
341 case KRB5_KRBHST_TCP
:
343 smb_krb5
->packet
= packet_init(smb_krb5
);
344 if (smb_krb5
->packet
== NULL
) {
345 talloc_free(smb_krb5
);
348 packet_set_private(smb_krb5
->packet
, smb_krb5
);
349 packet_set_socket(smb_krb5
->packet
, smb_krb5
->sock
);
350 packet_set_callback(smb_krb5
->packet
, smb_krb5_full_packet
);
351 packet_set_full_request(smb_krb5
->packet
, packet_full_request_u32
);
352 packet_set_error_handler(smb_krb5
->packet
, smb_krb5_error_handler
);
353 packet_set_event_context(smb_krb5
->packet
, ev
);
354 packet_set_fde(smb_krb5
->packet
, smb_krb5
->fde
);
356 smb_krb5
->request
= data_blob_talloc(smb_krb5
, NULL
, send_blob
.length
+ 4);
357 RSIVAL(smb_krb5
->request
.data
, 0, send_blob
.length
);
358 memcpy(smb_krb5
->request
.data
+4, send_blob
.data
, send_blob
.length
);
359 packet_send(smb_krb5
->packet
, smb_krb5
->request
);
361 case KRB5_KRBHST_HTTP
:
362 talloc_free(tmp_ctx
);
365 while ((NT_STATUS_IS_OK(smb_krb5
->status
)) && !smb_krb5
->reply
.length
) {
366 if (tevent_loop_once(ev
) != 0) {
367 talloc_free(tmp_ctx
);
371 /* After each and every event loop, reset the
372 * send_to_kdc pointers to what they were when
373 * we entered this loop. That way, if a
374 * nested event has invalidated them, we put
375 * it back before we return to the heimdal
377 ret
= krb5_set_send_to_kdc_func(context
,
378 smb_krb5_send_and_recv_func
,
381 talloc_free(tmp_ctx
);
385 if (NT_STATUS_EQUAL(smb_krb5
->status
, NT_STATUS_IO_TIMEOUT
)) {
386 talloc_free(smb_krb5
);
390 if (!NT_STATUS_IS_OK(smb_krb5
->status
)) {
391 struct tsocket_address
*addr
= socket_address_to_tsocket_address(smb_krb5
, remote_addr
);
392 const char *addr_string
= NULL
;
394 addr_string
= tsocket_address_inet_addr_string(addr
, smb_krb5
);
398 DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5
->status
),
400 talloc_free(smb_krb5
);
404 ret
= krb5_data_copy(recv_buf
, smb_krb5
->reply
.data
, smb_krb5
->reply
.length
);
406 talloc_free(tmp_ctx
);
409 talloc_free(smb_krb5
);
413 talloc_free(tmp_ctx
);
417 return KRB5_KDC_UNREACH
;
422 smb_krb5_init_context_basic(TALLOC_CTX
*tmp_ctx
,
423 struct loadparm_context
*lp_ctx
,
424 krb5_context
*_krb5_context
)
427 #ifdef SAMBA4_USES_HEIMDAL
429 const char *config_file
, *realm
;
431 krb5_context krb5_ctx
;
433 initialize_krb5_error_table();
435 ret
= krb5_init_context(&krb5_ctx
);
437 DEBUG(1,("krb5_init_context failed (%s)\n",
438 error_message(ret
)));
442 /* The MIT Kerberos build relies on using the system krb5.conf file.
443 * If you really want to use another file please set KRB5_CONFIG
445 #ifdef SAMBA4_USES_HEIMDAL
446 config_file
= lpcfg_config_path(tmp_ctx
, lp_ctx
, "krb5.conf");
448 krb5_free_context(krb5_ctx
);
452 /* Use our local krb5.conf file by default */
453 ret
= krb5_prepend_config_files_default(config_file
, &config_files
);
455 DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
456 smb_get_krb5_error_message(krb5_ctx
, ret
, tmp_ctx
)));
457 krb5_free_context(krb5_ctx
);
461 ret
= krb5_set_config_files(krb5_ctx
, config_files
);
462 krb5_free_config_files(config_files
);
464 DEBUG(1,("krb5_set_config_files failed (%s)\n",
465 smb_get_krb5_error_message(krb5_ctx
, ret
, tmp_ctx
)));
466 krb5_free_context(krb5_ctx
);
470 realm
= lpcfg_realm(lp_ctx
);
472 ret
= krb5_set_default_realm(krb5_ctx
, realm
);
474 DEBUG(1,("krb5_set_default_realm failed (%s)\n",
475 smb_get_krb5_error_message(krb5_ctx
, ret
, tmp_ctx
)));
476 krb5_free_context(krb5_ctx
);
481 *_krb5_context
= krb5_ctx
;
485 krb5_error_code
smb_krb5_init_context(void *parent_ctx
,
486 struct tevent_context
*ev
,
487 struct loadparm_context
*lp_ctx
,
488 struct smb_krb5_context
**smb_krb5_context
)
493 #ifdef SAMBA4_USES_HEIMDAL
494 krb5_log_facility
*logf
;
497 initialize_krb5_error_table();
499 tmp_ctx
= talloc_new(parent_ctx
);
500 *smb_krb5_context
= talloc_zero(tmp_ctx
, struct smb_krb5_context
);
502 if (!*smb_krb5_context
|| !tmp_ctx
) {
503 talloc_free(tmp_ctx
);
507 ret
= smb_krb5_init_context_basic(tmp_ctx
, lp_ctx
, &kctx
);
509 DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
510 error_message(ret
)));
511 talloc_free(tmp_ctx
);
514 (*smb_krb5_context
)->krb5_context
= kctx
;
516 talloc_set_destructor(*smb_krb5_context
, smb_krb5_context_destroy
);
518 #ifdef SAMBA4_USES_HEIMDAL
519 /* TODO: Should we have a different name here? */
520 ret
= krb5_initlog(kctx
, "Samba", &logf
);
523 DEBUG(1,("krb5_initlog failed (%s)\n",
524 smb_get_krb5_error_message(kctx
, ret
, tmp_ctx
)));
525 talloc_free(tmp_ctx
);
528 (*smb_krb5_context
)->pvt_log_data
= logf
;
530 ret
= krb5_addlog_func(kctx
, logf
, 0 /* min */, -1 /* max */,
531 smb_krb5_debug_wrapper
,
532 smb_krb5_debug_close
, NULL
);
534 DEBUG(1,("krb5_addlog_func failed (%s)\n",
535 smb_get_krb5_error_message(kctx
, ret
, tmp_ctx
)));
536 talloc_free(tmp_ctx
);
539 krb5_set_warn_dest(kctx
, logf
);
541 /* Set use of our socket lib */
543 struct tevent_context
*previous_ev
;
544 ret
= smb_krb5_context_set_event_ctx(*smb_krb5_context
,
547 talloc_free(tmp_ctx
);
552 /* Set options in kerberos */
554 krb5_set_dns_canonicalize_hostname(kctx
,
555 lpcfg_parm_bool(lp_ctx
, NULL
, "krb5",
556 "set_dns_canonicalize", false));
558 ret
= krb5_set_trace_callback(kctx
, smb_krb5_debug_wrapper
, NULL
);
559 if (ret
&& ret
!= KRB5_TRACE_NOSUPP
) {
560 DEBUG(1, ("krb5_set_trace_callback failed (%s)\n",
561 smb_get_krb5_error_message(kctx
, ret
, tmp_ctx
)));
562 talloc_free(tmp_ctx
);
566 talloc_steal(parent_ctx
, *smb_krb5_context
);
567 talloc_free(tmp_ctx
);
572 #ifdef SAMBA4_USES_HEIMDAL
573 krb5_error_code
smb_krb5_context_set_event_ctx(struct smb_krb5_context
*smb_krb5_context
,
574 struct tevent_context
*ev
,
575 struct tevent_context
**previous_ev
)
582 *previous_ev
= smb_krb5_context
->current_ev
;
584 smb_krb5_context
->current_ev
= talloc_reference(smb_krb5_context
, ev
);
585 if (!smb_krb5_context
->current_ev
) {
589 /* Set use of our socket lib */
590 ret
= krb5_set_send_to_kdc_func(smb_krb5_context
->krb5_context
,
591 smb_krb5_send_and_recv_func
,
594 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
595 DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
596 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
, ret
, tmp_ctx
)));
597 talloc_free(tmp_ctx
);
598 talloc_unlink(smb_krb5_context
, smb_krb5_context
->current_ev
);
599 smb_krb5_context
->current_ev
= NULL
;
605 krb5_error_code
smb_krb5_context_remove_event_ctx(struct smb_krb5_context
*smb_krb5_context
,
606 struct tevent_context
*previous_ev
,
607 struct tevent_context
*ev
)
610 talloc_unlink(smb_krb5_context
, ev
);
611 /* If there was a mismatch with things happening on a stack, then don't wipe things */
612 smb_krb5_context
->current_ev
= previous_ev
;
613 /* Set use of our socket lib */
614 ret
= krb5_set_send_to_kdc_func(smb_krb5_context
->krb5_context
,
615 smb_krb5_send_and_recv_func
,
618 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
619 DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
620 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
, ret
, tmp_ctx
)));
621 talloc_free(tmp_ctx
);