s4-auth-krb: avoid useless condition
[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 "system/gssapi.h"
26 #include <tevent.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"
35 /*
36   context structure for operations on cldap packets
37 */
38 struct smb_krb5_socket {
39         struct socket_context *sock;
40
41         /* the fd event */
42         struct tevent_fd *fde;
43
44         NTSTATUS status;
45         DATA_BLOB request, reply;
46
47         struct packet_context *packet;
48
49         size_t partial_read;
50
51         krb5_krbhst_info *hi;
52 };
53
54 static krb5_error_code smb_krb5_context_destroy(struct smb_krb5_context *ctx)
55 {
56 #ifdef SAMBA4_USES_HEIMDAL
57         if (ctx->pvt_log_data) {
58                 /* Otherwise krb5_free_context will try and close what we
59                  * have already free()ed */
60                 krb5_set_warn_dest(ctx->krb5_context, NULL);
61                 krb5_closelog(ctx->krb5_context,
62                                 (krb5_log_facility *)ctx->pvt_log_data);
63         }
64 #else
65         krb5_set_trace_callback(ctx->krb5_context, NULL, NULL);
66 #endif
67         krb5_free_context(ctx->krb5_context);
68         return 0;
69 }
70
71 /* We never close down the DEBUG system, and no need to unreference the use */
72 static void smb_krb5_debug_close(void *private_data) {
73         return;
74 }
75
76 #ifdef SAMBA4_USES_HEIMDAL
77 static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data)
78 {
79         DEBUG(3, ("Kerberos: %s\n", msg));
80 }
81 #else
82 static void smb_krb5_debug_wrapper(krb5_context context,
83                                    const struct krb5_trace_info *info,
84                                    void *cb_data)
85 {
86         DEBUG(3, ("Kerberos: %s\n", info->message));
87 }
88 #endif
89
90 #ifdef SAMBA4_USES_HEIMDAL
91 /*
92   handle recv events on a smb_krb5 socket
93 */
94 static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
95 {
96         TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
97         DATA_BLOB blob;
98         size_t nread, dsize;
99
100         smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
101         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
102                 talloc_free(tmp_ctx);
103                 return;
104         }
105
106         blob = data_blob_talloc(tmp_ctx, NULL, dsize);
107         if (blob.data == NULL && dsize != 0) {
108                 smb_krb5->status = NT_STATUS_NO_MEMORY;
109                 talloc_free(tmp_ctx);
110                 return;
111         }
112
113         smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
114         if (!NT_STATUS_IS_OK(smb_krb5->status)) {
115                 talloc_free(tmp_ctx);
116                 return;
117         }
118         blob.length = nread;
119
120         if (nread == 0) {
121                 smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
122                 talloc_free(tmp_ctx);
123                 return;
124         }
125
126         DEBUG(4,("Received smb_krb5 packet of length %d\n",
127                  (int)blob.length));
128
129         talloc_steal(smb_krb5, blob.data);
130         smb_krb5->reply = blob;
131         talloc_free(tmp_ctx);
132 }
133
134 static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
135 {
136         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
137         talloc_steal(smb_krb5, data.data);
138         smb_krb5->reply = data;
139         smb_krb5->reply.length -= 4;
140         smb_krb5->reply.data += 4;
141         return NT_STATUS_OK;
142 }
143
144 /*
145   handle request timeouts
146 */
147 static void smb_krb5_request_timeout(struct tevent_context *event_ctx,
148                                   struct tevent_timer *te, struct timeval t,
149                                   void *private_data)
150 {
151         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
152         DEBUG(5,("Timed out smb_krb5 packet\n"));
153         smb_krb5->status = NT_STATUS_IO_TIMEOUT;
154 }
155
156 static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
157 {
158         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
159         smb_krb5->status = status;
160 }
161
162 /*
163   handle send events on a smb_krb5 socket
164 */
165 static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
166 {
167         NTSTATUS status;
168
169         size_t len;
170
171         len = smb_krb5->request.length;
172         status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
173
174         if (!NT_STATUS_IS_OK(status)) return;
175
176         TEVENT_FD_READABLE(smb_krb5->fde);
177
178         TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
179         return;
180 }
181
182
183 /*
184   handle fd events on a smb_krb5_socket
185 */
186 static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
187                                  uint16_t flags, void *private_data)
188 {
189         struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
190         switch (smb_krb5->hi->proto) {
191         case KRB5_KRBHST_UDP:
192                 if (flags & TEVENT_FD_READ) {
193                         smb_krb5_socket_recv(smb_krb5);
194                         return;
195                 }
196                 if (flags & TEVENT_FD_WRITE) {
197                         smb_krb5_socket_send(smb_krb5);
198                         return;
199                 }
200                 /* not reached */
201                 return;
202         case KRB5_KRBHST_TCP:
203                 if (flags & TEVENT_FD_READ) {
204                         packet_recv(smb_krb5->packet);
205                         return;
206                 }
207                 if (flags & TEVENT_FD_WRITE) {
208                         packet_queue_run(smb_krb5->packet);
209                         return;
210                 }
211                 /* not reached */
212                 return;
213         case KRB5_KRBHST_HTTP:
214                 /* can't happen */
215                 break;
216         }
217 }
218
219 krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
220                                             void *data,
221                                             krb5_krbhst_info *hi,
222                                             time_t timeout,
223                                             const krb5_data *send_buf,
224                                             krb5_data *recv_buf)
225 {
226         krb5_error_code ret;
227         NTSTATUS status;
228         const char *name;
229         struct addrinfo *ai, *a;
230         struct smb_krb5_socket *smb_krb5;
231
232         DATA_BLOB send_blob;
233
234         struct tevent_context *ev;
235         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
236         if (!tmp_ctx) {
237                 return ENOMEM;
238         }
239
240         if (!data) {
241                 /* If no event context was available, then create one for this loop */
242                 ev = tevent_context_init(tmp_ctx);
243                 if (!ev) {
244                         talloc_free(tmp_ctx);
245                         return ENOMEM;
246                 }
247         } else {
248                 ev = talloc_get_type_abort(data, struct tevent_context);
249         }
250
251         send_blob = data_blob_const(send_buf->data, send_buf->length);
252
253         ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
254         if (ret) {
255                 talloc_free(tmp_ctx);
256                 return ret;
257         }
258
259         for (a = ai; a; a = a->ai_next) {
260                 struct socket_address *remote_addr;
261                 smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket);
262                 if (!smb_krb5) {
263                         talloc_free(tmp_ctx);
264                         return ENOMEM;
265                 }
266                 smb_krb5->hi = hi;
267
268                 switch (a->ai_family) {
269                 case PF_INET:
270                         name = "ipv4";
271                         break;
272 #ifdef HAVE_IPV6
273                 case PF_INET6:
274                         name = "ipv6";
275                         break;
276 #endif
277                 default:
278                         talloc_free(tmp_ctx);
279                         return EINVAL;
280                 }
281
282                 status = NT_STATUS_INVALID_PARAMETER;
283                 switch (hi->proto) {
284                 case KRB5_KRBHST_UDP:
285                         status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
286                         break;
287                 case KRB5_KRBHST_TCP:
288                         status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
289                         break;
290                 case KRB5_KRBHST_HTTP:
291                         talloc_free(tmp_ctx);
292                         return EINVAL;
293                 }
294                 if (!NT_STATUS_IS_OK(status)) {
295                         talloc_free(smb_krb5);
296                         continue;
297                 }
298
299                 talloc_steal(smb_krb5, smb_krb5->sock);
300
301                 remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
302                 if (!remote_addr) {
303                         talloc_free(smb_krb5);
304                         continue;
305                 }
306
307                 status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
308                 if (!NT_STATUS_IS_OK(status)) {
309                         talloc_free(smb_krb5);
310                         continue;
311                 }
312
313                 /* Setup the FDE, start listening for read events
314                  * from the start (otherwise we may miss a socket
315                  * drop) and mark as AUTOCLOSE along with the fde */
316
317                 /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
318                 smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
319                                               socket_get_fd(smb_krb5->sock),
320                                               TEVENT_FD_READ,
321                                               smb_krb5_socket_handler, smb_krb5);
322                 /* its now the job of the event layer to close the socket */
323                 tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
324                 socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
325
326                 tevent_add_timer(ev, smb_krb5,
327                                  timeval_current_ofs(timeout, 0),
328                                  smb_krb5_request_timeout, smb_krb5);
329
330                 smb_krb5->status = NT_STATUS_OK;
331                 smb_krb5->reply = data_blob(NULL, 0);
332
333                 switch (hi->proto) {
334                 case KRB5_KRBHST_UDP:
335                         TEVENT_FD_WRITEABLE(smb_krb5->fde);
336                         smb_krb5->request = send_blob;
337                         break;
338                 case KRB5_KRBHST_TCP:
339
340                         smb_krb5->packet = packet_init(smb_krb5);
341                         if (smb_krb5->packet == NULL) {
342                                 talloc_free(smb_krb5);
343                                 return ENOMEM;
344                         }
345                         packet_set_private(smb_krb5->packet, smb_krb5);
346                         packet_set_socket(smb_krb5->packet, smb_krb5->sock);
347                         packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
348                         packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
349                         packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
350                         packet_set_event_context(smb_krb5->packet, ev);
351                         packet_set_fde(smb_krb5->packet, smb_krb5->fde);
352
353                         smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
354                         RSIVAL(smb_krb5->request.data, 0, send_blob.length);
355                         memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
356                         packet_send(smb_krb5->packet, smb_krb5->request);
357                         break;
358                 case KRB5_KRBHST_HTTP:
359                         talloc_free(tmp_ctx);
360                         return EINVAL;
361                 }
362                 while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
363                         if (tevent_loop_once(ev) != 0) {
364                                 talloc_free(tmp_ctx);
365                                 return EINVAL;
366                         }
367
368                         /* After each and every event loop, reset the
369                          * send_to_kdc pointers to what they were when
370                          * we entered this loop.  That way, if a
371                          * nested event has invalidated them, we put
372                          * it back before we return to the heimdal
373                          * code */
374                         ret = krb5_set_send_to_kdc_func(context,
375                                                         smb_krb5_send_and_recv_func,
376                                                         data);
377                         if (ret != 0) {
378                                 talloc_free(tmp_ctx);
379                                 return ret;
380                         }
381                 }
382                 if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
383                         talloc_free(smb_krb5);
384                         continue;
385                 }
386
387                 if (!NT_STATUS_IS_OK(smb_krb5->status)) {
388                         struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr);
389                         const char *addr_string = NULL;
390                         if (addr) {
391                                 addr_string = tsocket_address_inet_addr_string(addr, smb_krb5);
392                         } else {
393                                 addr_string = NULL;
394                         }
395                         DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status),
396                                  addr_string));
397                         talloc_free(smb_krb5);
398                         continue;
399                 }
400
401                 ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
402                 if (ret) {
403                         talloc_free(tmp_ctx);
404                         return ret;
405                 }
406                 talloc_free(smb_krb5);
407
408                 break;
409         }
410         talloc_free(tmp_ctx);
411         if (a) {
412                 return 0;
413         }
414         return KRB5_KDC_UNREACH;
415 }
416 #endif
417
418 krb5_error_code
419 smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
420                             struct loadparm_context *lp_ctx,
421                             krb5_context *_krb5_context)
422 {
423         krb5_error_code ret;
424         char **config_files;
425         const char *config_file, *realm;
426         krb5_context krb5_ctx;
427
428         initialize_krb5_error_table();
429
430         ret = krb5_init_context(&krb5_ctx);
431         if (ret) {
432                 DEBUG(1,("krb5_init_context failed (%s)\n",
433                          error_message(ret)));
434                 return ret;
435         }
436
437         config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf");
438         if (!config_file) {
439                 krb5_free_context(krb5_ctx);
440                 return ENOMEM;
441         }
442
443         /* Use our local krb5.conf file by default */
444         ret = krb5_prepend_config_files_default(config_file, &config_files);
445         if (ret) {
446                 DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
447                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
448                 krb5_free_context(krb5_ctx);
449                 return ret;
450         }
451
452         ret = krb5_set_config_files(krb5_ctx, config_files);
453         krb5_free_config_files(config_files);
454         if (ret) {
455                 DEBUG(1,("krb5_set_config_files failed (%s)\n",
456                          smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
457                 krb5_free_context(krb5_ctx);
458                 return ret;
459         }
460
461         realm = lpcfg_realm(lp_ctx);
462         if (realm != NULL) {
463                 ret = krb5_set_default_realm(krb5_ctx, realm);
464                 if (ret) {
465                         DEBUG(1,("krb5_set_default_realm failed (%s)\n",
466                                  smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
467                         krb5_free_context(krb5_ctx);
468                         return ret;
469                 }
470         }
471
472         *_krb5_context = krb5_ctx;
473         return 0;
474 }
475
476 krb5_error_code smb_krb5_init_context(void *parent_ctx,
477                                       struct tevent_context *ev,
478                                       struct loadparm_context *lp_ctx,
479                                       struct smb_krb5_context **smb_krb5_context)
480 {
481         krb5_error_code ret;
482         TALLOC_CTX *tmp_ctx;
483         krb5_context kctx;
484 #ifdef SAMBA4_USES_HEIMDAL
485         krb5_log_facility *logf;
486 #endif
487
488         initialize_krb5_error_table();
489
490         tmp_ctx = talloc_new(parent_ctx);
491         *smb_krb5_context = talloc_zero(tmp_ctx, struct smb_krb5_context);
492
493         if (!*smb_krb5_context || !tmp_ctx) {
494                 talloc_free(tmp_ctx);
495                 return ENOMEM;
496         }
497
498         ret = smb_krb5_init_context_basic(tmp_ctx, lp_ctx, &kctx);
499         if (ret) {
500                 DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
501                          error_message(ret)));
502                 talloc_free(tmp_ctx);
503                 return ret;
504         }
505         (*smb_krb5_context)->krb5_context = kctx;
506
507         talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);
508
509 #ifdef SAMBA4_USES_HEIMDAL
510         /* TODO: Should we have a different name here? */
511         ret = krb5_initlog(kctx, "Samba", &logf);
512
513         if (ret) {
514                 DEBUG(1,("krb5_initlog failed (%s)\n",
515                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
516                 talloc_free(tmp_ctx);
517                 return ret;
518         }
519         (*smb_krb5_context)->pvt_log_data = logf;
520
521         ret = krb5_addlog_func(kctx, logf, 0 /* min */, -1 /* max */,
522                                smb_krb5_debug_wrapper,
523                                 smb_krb5_debug_close, NULL);
524         if (ret) {
525                 DEBUG(1,("krb5_addlog_func failed (%s)\n",
526                          smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
527                 talloc_free(tmp_ctx);
528                 return ret;
529         }
530         krb5_set_warn_dest(kctx, logf);
531
532         /* Set use of our socket lib */
533         if (ev) {
534                 struct tevent_context *previous_ev;
535                 ret = smb_krb5_context_set_event_ctx(*smb_krb5_context,
536                                                      ev, &previous_ev);
537                 if (ret) {
538                         talloc_free(tmp_ctx);
539                         return ret;
540                 }
541         }
542
543         /* Set options in kerberos */
544
545         krb5_set_dns_canonicalize_hostname(kctx,
546                         lpcfg_parm_bool(lp_ctx, NULL, "krb5",
547                                         "set_dns_canonicalize", false));
548 #else
549         ret = krb5_set_trace_callback(kctx, smb_krb5_debug_wrapper, NULL);
550         if (ret && ret != KRB5_TRACE_NOSUPP) {
551                 DEBUG(1, ("krb5_set_trace_callback failed (%s)\n"
552                           smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
553                 talloc_free(tmp_ctx);
554                 return ret;
555         }
556 #endif
557         talloc_steal(parent_ctx, *smb_krb5_context);
558         talloc_free(tmp_ctx);
559
560         return 0;
561 }
562
563 #ifdef SAMBA4_USES_HEIMDAL
564 krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context,
565                                                struct tevent_context *ev,
566                                                struct tevent_context **previous_ev)
567 {
568         int ret;
569         if (!ev) {
570                 return EINVAL;
571         }
572
573         *previous_ev = smb_krb5_context->current_ev;
574
575         smb_krb5_context->current_ev = talloc_reference(smb_krb5_context, ev);
576         if (!smb_krb5_context->current_ev) {
577                 return ENOMEM;
578         }
579
580         /* Set use of our socket lib */
581         ret = krb5_set_send_to_kdc_func(smb_krb5_context->krb5_context,
582                                         smb_krb5_send_and_recv_func,
583                                         ev);
584         if (ret) {
585                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
586                 DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
587                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
588                 talloc_free(tmp_ctx);
589                 talloc_unlink(smb_krb5_context, smb_krb5_context->current_ev);
590                 smb_krb5_context->current_ev = NULL;
591                 return ret;
592         }
593         return 0;
594 }
595
596 krb5_error_code smb_krb5_context_remove_event_ctx(struct smb_krb5_context *smb_krb5_context,
597                                                   struct tevent_context *previous_ev,
598                                                   struct tevent_context *ev)
599 {
600         int ret;
601         talloc_unlink(smb_krb5_context, ev);
602         /* If there was a mismatch with things happening on a stack, then don't wipe things */
603         smb_krb5_context->current_ev = previous_ev;
604         /* Set use of our socket lib */
605         ret = krb5_set_send_to_kdc_func(smb_krb5_context->krb5_context,
606                                         smb_krb5_send_and_recv_func,
607                                         previous_ev);
608         if (ret) {
609                 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
610                 DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
611                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
612                 talloc_free(tmp_ctx);
613                 return ret;
614         }
615         return 0;
616 }
617 #endif