gensec: Fix CID 1458419 Control flow issues (NO_EFFECT)
[samba.git] / source4 / auth / gensec / gensec_krb5.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Kerberos backend for GENSEC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
7    Copyright (C) Andrew Tridgell 2001
8    Copyright (C) Luke Howard 2002-2003
9    Copyright (C) Stefan Metzmacher 2004-2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include <tevent.h>
28 #include "lib/util/tevent_ntstatus.h"
29 #include "system/kerberos.h"
30 #include "auth/kerberos/kerberos.h"
31 #include "auth/auth.h"
32 #include "lib/tsocket/tsocket.h"
33 #include "librpc/gen_ndr/dcerpc.h"
34 #include "auth/credentials/credentials.h"
35 #include "auth/credentials/credentials_krb5.h"
36 #include "auth/kerberos/kerberos_credentials.h"
37 #include "auth/gensec/gensec.h"
38 #include "auth/gensec/gensec_internal.h"
39 #include "auth/gensec/gensec_proto.h"
40 #include "auth/gensec/gensec_toplevel_proto.h"
41 #include "param/param.h"
42 #include "auth/auth_sam_reply.h"
43 #include "lib/util/util_net.h"
44 #include "../lib/util/asn1.h"
45 #include "auth/kerberos/pac_utils.h"
46 #include "gensec_krb5.h"
47
48 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *);
49
50 enum GENSEC_KRB5_STATE {
51         GENSEC_KRB5_SERVER_START,
52         GENSEC_KRB5_CLIENT_START,
53         GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
54         GENSEC_KRB5_DONE
55 };
56
57 struct gensec_krb5_state {
58         enum GENSEC_KRB5_STATE state_position;
59         struct smb_krb5_context *smb_krb5_context;
60         krb5_auth_context auth_context;
61         krb5_data enc_ticket;
62         krb5_keyblock *keyblock;
63         krb5_ticket *ticket;
64         bool gssapi;
65         krb5_flags ap_req_options;
66 };
67
68 static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
69 {
70         if (!gensec_krb5_state->smb_krb5_context) {
71                 /* We can't clean anything else up unless we started up this far */
72                 return 0;
73         }
74         if (gensec_krb5_state->enc_ticket.length) { 
75                 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
76                                             &gensec_krb5_state->enc_ticket); 
77         }
78
79         if (gensec_krb5_state->ticket) {
80                 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context, 
81                                  gensec_krb5_state->ticket);
82         }
83
84         /* ccache freed in a child destructor */
85
86         krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context, 
87                            gensec_krb5_state->keyblock);
88                 
89         if (gensec_krb5_state->auth_context) {
90                 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context, 
91                                    gensec_krb5_state->auth_context);
92         }
93
94         return 0;
95 }
96
97 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
98 {
99         krb5_error_code ret;
100         struct gensec_krb5_state *gensec_krb5_state;
101         struct cli_credentials *creds;
102         const struct tsocket_address *tlocal_addr, *tremote_addr;
103         krb5_address my_krb5_addr, peer_krb5_addr;
104         
105         creds = gensec_get_credentials(gensec_security);
106         if (!creds) {
107                 return NT_STATUS_INVALID_PARAMETER;
108         }
109
110         gensec_krb5_state = talloc_zero(gensec_security, struct gensec_krb5_state);
111         if (!gensec_krb5_state) {
112                 return NT_STATUS_NO_MEMORY;
113         }
114
115         gensec_security->private_data = gensec_krb5_state;
116         gensec_krb5_state->gssapi = gssapi;
117
118         talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); 
119
120         if (cli_credentials_get_krb5_context(creds, 
121                                              gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
122                 talloc_free(gensec_krb5_state);
123                 return NT_STATUS_INTERNAL_ERROR;
124         }
125
126         ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
127         if (ret) {
128                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 
129                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
130                                                     ret, gensec_krb5_state)));
131                 talloc_free(gensec_krb5_state);
132                 return NT_STATUS_INTERNAL_ERROR;
133         }
134
135         ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, 
136                                      gensec_krb5_state->auth_context,
137                                      KRB5_AUTH_CONTEXT_DO_SEQUENCE);
138         if (ret) {
139                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", 
140                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
141                                                     ret, gensec_krb5_state)));
142                 talloc_free(gensec_krb5_state);
143                 return NT_STATUS_INTERNAL_ERROR;
144         }
145
146         tlocal_addr = gensec_get_local_address(gensec_security);
147         if (tlocal_addr) {
148                 ssize_t sockaddr_ret;
149                 struct samba_sockaddr addr;
150                 bool ok;
151
152                 sockaddr_ret = tsocket_address_bsd_sockaddr(
153                         tlocal_addr, &addr.u.sa, sizeof(addr.u.sa));
154                 if (sockaddr_ret < 0) {
155                         talloc_free(gensec_krb5_state);
156                         return NT_STATUS_INTERNAL_ERROR;
157                 }
158                 addr.sa_socklen = sockaddr_ret;
159                 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &my_krb5_addr);
160                 if (!ok) {
161                         DBG_WARNING("smb_krb5_sockaddr_to_kaddr (local) failed\n");
162                         talloc_free(gensec_krb5_state);
163                         return NT_STATUS_INTERNAL_ERROR;
164                 }
165         }
166
167         tremote_addr = gensec_get_remote_address(gensec_security);
168         if (tremote_addr) {
169                 ssize_t sockaddr_ret;
170                 struct samba_sockaddr addr;
171                 bool ok;
172
173                 sockaddr_ret = tsocket_address_bsd_sockaddr(
174                         tremote_addr, &addr.u.sa, sizeof(addr.u.sa));
175                 if (sockaddr_ret < 0) {
176                         talloc_free(gensec_krb5_state);
177                         return NT_STATUS_INTERNAL_ERROR;
178                 }
179                 addr.sa_socklen = sockaddr_ret;
180                 ok = smb_krb5_sockaddr_to_kaddr(&addr.u.ss, &peer_krb5_addr);
181                 if (!ok) {
182                         DBG_WARNING("smb_krb5_sockaddr_to_kaddr (remote) failed\n");
183                         talloc_free(gensec_krb5_state);
184                         return NT_STATUS_INTERNAL_ERROR;
185                 }
186         }
187
188         ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, 
189                                      gensec_krb5_state->auth_context,
190                                      tlocal_addr ? &my_krb5_addr : NULL,
191                                      tremote_addr ? &peer_krb5_addr : NULL);
192         if (ret) {
193                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", 
194                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
195                                                     ret, gensec_krb5_state)));
196                 talloc_free(gensec_krb5_state);
197                 return NT_STATUS_INTERNAL_ERROR;
198         }
199
200         return NT_STATUS_OK;
201 }
202
203 static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi)
204 {
205         NTSTATUS nt_status;
206         struct gensec_krb5_state *gensec_krb5_state;
207
208         nt_status = gensec_krb5_start(gensec_security, gssapi);
209         if (!NT_STATUS_IS_OK(nt_status)) {
210                 return nt_status;
211         }
212         
213         gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
214         gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
215
216         return NT_STATUS_OK;
217 }
218
219 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
220 {
221         return gensec_krb5_common_server_start(gensec_security, false);
222 }
223
224 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
225 {
226         return gensec_krb5_common_server_start(gensec_security, true);
227 }
228
229 static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
230 {
231         const char *hostname;
232         struct gensec_krb5_state *gensec_krb5_state;
233         NTSTATUS nt_status;
234         hostname = gensec_get_target_hostname(gensec_security);
235         if (!hostname) {
236                 DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n"));
237                 return NT_STATUS_INVALID_PARAMETER;
238         }
239         if (is_ipaddress(hostname)) {
240                 DEBUG(2, ("Cannot do krb5 to an IP address"));
241                 return NT_STATUS_INVALID_PARAMETER;
242         }
243         if (strcmp(hostname, "localhost") == 0) {
244                 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
245                 return NT_STATUS_INVALID_PARAMETER;
246         }
247                         
248         nt_status = gensec_krb5_start(gensec_security, gssapi);
249         if (!NT_STATUS_IS_OK(nt_status)) {
250                 return nt_status;
251         }
252
253         gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
254         gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
255         gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;
256
257         if (gensec_krb5_state->gssapi) {
258                 /* The Fake GSSAPI model emulates Samba3, which does not do mutual authentication */
259                 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
260                         gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
261                 }
262         } else {
263                 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
264                 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
265                         gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
266                 }
267         }
268         return NT_STATUS_OK;
269 }
270
271 static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security,
272                                                 struct tevent_context *ev)
273 {
274         struct gensec_krb5_state *gensec_krb5_state;
275         krb5_error_code ret;
276         struct ccache_container *ccache_container;
277         const char *error_string;
278         const char *principal;
279         const char *hostname;
280         krb5_data in_data = { .length = 0 };
281         krb5_data *in_data_p = NULL;
282 #ifdef SAMBA4_USES_HEIMDAL
283         struct tevent_context *previous_ev;
284 #endif
285
286         if (lpcfg_parm_bool(gensec_security->settings->lp_ctx,
287                             NULL, "gensec_krb5", "send_authenticator_checksum", true)) {
288                 in_data_p = &in_data;
289         }
290         
291         gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
292
293         principal = gensec_get_target_principal(gensec_security);
294         hostname = gensec_get_target_hostname(gensec_security);
295
296         ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), 
297                                          ev,
298                                          gensec_security->settings->lp_ctx, &ccache_container, &error_string);
299         switch (ret) {
300         case 0:
301                 break;
302         case KRB5KDC_ERR_PREAUTH_FAILED:
303         case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
304                 return NT_STATUS_LOGON_FAILURE;
305         case KRB5_KDC_UNREACH:
306                 DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
307                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
308         case KRB5_CC_NOTFOUND:
309         case KRB5_CC_END:
310                 DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
311                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
312         default:
313                 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
314                 return NT_STATUS_UNSUCCESSFUL;
315         }
316         
317 #ifdef SAMBA4_USES_HEIMDAL
318         /* Do this every time, in case we have weird recursive issues here */
319         ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, ev, &previous_ev);
320         if (ret != 0) {
321                 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
322                 return NT_STATUS_NO_MEMORY;
323         }
324 #endif
325         if (principal) {
326                 krb5_principal target_principal;
327                 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
328                                       &target_principal);
329                 if (ret == 0) {
330                         krb5_creds this_cred;
331                         krb5_creds *cred;
332
333                         ZERO_STRUCT(this_cred);
334                         ret = krb5_cc_get_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
335                                                     ccache_container->ccache,
336                                                     &this_cred.client);
337                         if (ret != 0) {
338                                 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
339                                                     target_principal);
340                                 return NT_STATUS_UNSUCCESSFUL;
341                         }
342
343                         ret = krb5_copy_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
344                                                   target_principal,
345                                                   &this_cred.server);
346                         krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
347                                             target_principal);
348                         if (ret != 0) {
349                                 krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
350                                                         &this_cred);
351                                 return NT_STATUS_UNSUCCESSFUL;
352                         }
353                         this_cred.times.endtime = 0;
354
355                         ret = krb5_get_credentials(gensec_krb5_state->smb_krb5_context->krb5_context,
356                                                    0,
357                                                    ccache_container->ccache,
358                                                    &this_cred,
359                                                    &cred);
360                         krb5_free_cred_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
361                                                 &this_cred);
362                         if (ret != 0) {
363                                 return NT_STATUS_UNSUCCESSFUL;
364                         }
365
366                         ret = krb5_mk_req_extended(gensec_krb5_state->smb_krb5_context->krb5_context,
367                                                    &gensec_krb5_state->auth_context,
368                                                    gensec_krb5_state->ap_req_options,
369                                                    in_data_p,
370                                                    cred,
371                                                    &gensec_krb5_state->enc_ticket);
372                 }
373         } else {
374                 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 
375                                   &gensec_krb5_state->auth_context,
376                                   gensec_krb5_state->ap_req_options,
377                                   discard_const_p(char, gensec_get_target_service(gensec_security)),
378                                   discard_const_p(char, hostname),
379                                   in_data_p, ccache_container->ccache, 
380                                   &gensec_krb5_state->enc_ticket);
381         }
382
383 #ifdef SAMBA4_USES_HEIMDAL
384         smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, ev);
385 #endif
386
387         switch (ret) {
388         case 0:
389                 return NT_STATUS_OK;
390         case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
391                 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
392                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
393                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
394         case KRB5_KDC_UNREACH:
395                 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
396                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
397                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
398         case KRB5KDC_ERR_PREAUTH_FAILED:
399         case KRB5KRB_AP_ERR_TKT_EXPIRED:
400         case KRB5_CC_END:
401                 /* Too much clock skew - we will need to kinit to re-skew the clock */
402         case KRB5KRB_AP_ERR_SKEW:
403         case KRB5_KDCREP_SKEW:
404                 DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
405                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
406                 FALL_THROUGH;
407         /* just don't print a message for these really ordinary messages */
408         case KRB5_FCC_NOFILE:
409         case KRB5_CC_NOTFOUND:
410         case ENOENT:
411                 
412                 return NT_STATUS_UNSUCCESSFUL;
413                 break;
414                 
415         default:
416                 DEBUG(0, ("kerberos: %s\n", 
417                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
418                 return NT_STATUS_UNSUCCESSFUL;
419         }
420 }
421
422 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
423 {
424         return gensec_krb5_common_client_start(gensec_security, false);
425 }
426
427 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
428 {
429         return gensec_krb5_common_client_start(gensec_security, true);
430 }
431
432
433 /*
434   generate a krb5 GSS-API wrapper packet given a ticket
435 */
436 static DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
437 {
438         struct asn1_data *data;
439         DATA_BLOB ret = data_blob_null;
440
441         data = asn1_init(mem_ctx);
442         if (!data || !ticket->data) {
443                 return ret;
444         }
445
446         if (!asn1_push_tag(data, ASN1_APPLICATION(0))) goto err;
447         if (!asn1_write_OID(data, GENSEC_OID_KERBEROS5)) goto err;
448
449         if (!asn1_write(data, tok_id, 2)) goto err;
450         if (!asn1_write(data, ticket->data, ticket->length)) goto err;
451         if (!asn1_pop_tag(data)) goto err;
452
453
454         if (!asn1_extract_blob(data, mem_ctx, &ret)) {
455                 goto err;
456         }
457         asn1_free(data);
458
459         return ret;
460
461   err:
462
463         DEBUG(1, ("Failed to build krb5 wrapper at offset %d\n",
464                   (int)asn1_current_ofs(data)));
465         asn1_free(data);
466         return ret;
467 }
468
469 /*
470   parse a krb5 GSS-API wrapper packet giving a ticket
471 */
472 static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
473 {
474         bool ret = false;
475         struct asn1_data *data = asn1_init(mem_ctx);
476         int data_remaining;
477
478         if (!data) {
479                 return false;
480         }
481
482         if (!asn1_load(data, *blob)) goto err;
483         if (!asn1_start_tag(data, ASN1_APPLICATION(0))) goto err;
484         if (!asn1_check_OID(data, GENSEC_OID_KERBEROS5)) goto err;
485
486         data_remaining = asn1_tag_remaining(data);
487
488         if (data_remaining < 3) {
489                 asn1_set_error(data);
490         } else {
491                 if (!asn1_read(data, tok_id, 2)) goto err;
492                 data_remaining -= 2;
493                 *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
494                 if (!asn1_read(data, ticket->data, ticket->length)) goto err;
495         }
496
497         if (!asn1_end_tag(data)) goto err;
498
499         ret = !asn1_has_error(data);
500
501   err:
502
503         asn1_free(data);
504
505         return ret;
506 }
507
508 static NTSTATUS gensec_krb5_update_internal(struct gensec_security *gensec_security,
509                                             TALLOC_CTX *out_mem_ctx,
510                                             struct tevent_context *ev,
511                                             const DATA_BLOB in, DATA_BLOB *out)
512 {
513         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
514         krb5_error_code ret = 0;
515         NTSTATUS nt_status;
516
517         switch (gensec_krb5_state->state_position) {
518         case GENSEC_KRB5_CLIENT_START:
519         {
520                 DATA_BLOB unwrapped_out;
521                 
522                 nt_status = gensec_krb5_common_client_creds(gensec_security, ev);
523                 if (!NT_STATUS_IS_OK(nt_status)) {
524                         return nt_status;
525                 }
526
527                 if (gensec_krb5_state->gssapi) {
528                         unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
529                         
530                         /* wrap that up in a nice GSS-API wrapping */
531                         *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
532                 } else {
533                         *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
534                 }
535                 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
536                         gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
537                         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
538                 } else {
539                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
540                         nt_status = NT_STATUS_OK;
541                 }
542                 return nt_status;
543         }
544                 
545         case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
546         {
547                 DATA_BLOB unwrapped_in;
548                 krb5_data inbuf;
549                 krb5_ap_rep_enc_part *repl = NULL;
550                 uint8_t tok_id[2];
551
552                 if (gensec_krb5_state->gssapi) {
553                         if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
554                                 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
555                                 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
556                                 return NT_STATUS_INVALID_PARAMETER;
557                         }
558                 } else {
559                         unwrapped_in = in;
560                 }
561                 /* TODO: check the tok_id */
562
563                 inbuf.data = (char *)unwrapped_in.data;
564                 inbuf.length = unwrapped_in.length;
565                 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, 
566                                   gensec_krb5_state->auth_context,
567                                   &inbuf, &repl);
568                 if (ret) {
569                         DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
570                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
571                         dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
572                         nt_status = NT_STATUS_ACCESS_DENIED;
573                 } else {
574                         *out = data_blob(NULL, 0);
575                         nt_status = NT_STATUS_OK;
576                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
577                 }
578                 if (repl) {
579                         krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
580                 }
581                 return nt_status;
582         }
583
584         case GENSEC_KRB5_SERVER_START:
585         {
586                 DATA_BLOB unwrapped_in;
587                 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
588                 krb5_data inbuf, outbuf;
589                 uint8_t tok_id[2];
590                 struct keytab_container *keytab;
591                 krb5_principal server_in_keytab;
592                 const char *error_string;
593                 enum credentials_obtained obtained;
594
595                 if (!in.data) {
596                         return NT_STATUS_INVALID_PARAMETER;
597                 }       
598
599                 /* Grab the keytab, however generated */
600                 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), 
601                                                  gensec_security->settings->lp_ctx, &keytab);
602                 if (ret) {
603                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
604                 }
605                 
606                 /* This ensures we lookup the correct entry in that
607                  * keytab.  A NULL principal is acceptable, and means
608                  * that the krb5 libs should search the keytab at
609                  * accept time for any matching key */
610                 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), 
611                                                  gensec_krb5_state->smb_krb5_context, 
612                                                  &server_in_keytab, &obtained, &error_string);
613
614                 if (ret) {
615                         DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
616                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
617                 }
618
619                 if (keytab->password_based || obtained < CRED_SPECIFIED) {
620                         /* 
621                          * Use match-by-key in this case (matches
622                          * cli_credentials_get_server_gss_creds()
623                          * behaviour).  No need to free the memory,
624                          * this is handled with a talloc destructor.
625                          */
626                         server_in_keytab = NULL;
627                 }
628
629                 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
630                 if (gensec_krb5_state->gssapi
631                     && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
632                         inbuf.data = (char *)unwrapped_in.data;
633                         inbuf.length = unwrapped_in.length;
634                 } else {
635                         inbuf.data = (char *)in.data;
636                         inbuf.length = in.length;
637                 }
638
639                 ret = smb_krb5_rd_req_decoded(gensec_krb5_state->smb_krb5_context->krb5_context,
640                                               &gensec_krb5_state->auth_context,
641                                               &inbuf,
642                                               keytab->keytab,
643                                               server_in_keytab,
644                                               &outbuf,
645                                               &gensec_krb5_state->ticket,
646                                               &gensec_krb5_state->keyblock);
647
648                 if (ret) {
649                         DBG_WARNING("smb_krb5_rd_req_decoded failed\n");
650                         return NT_STATUS_LOGON_FAILURE;
651                 }
652                 unwrapped_out.data = (uint8_t *)outbuf.data;
653                 unwrapped_out.length = outbuf.length;
654                 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
655                 /* wrap that up in a nice GSS-API wrapping */
656                 if (gensec_krb5_state->gssapi) {
657                         *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
658                 } else {
659                         *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
660                 }
661                 smb_krb5_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
662                                             &outbuf);
663                 return NT_STATUS_OK;
664         }
665
666         case GENSEC_KRB5_DONE:
667         default:
668                 /* Asking too many times... */
669                 return NT_STATUS_INVALID_PARAMETER;
670         }
671 }
672
673 struct gensec_krb5_update_state {
674         NTSTATUS status;
675         DATA_BLOB out;
676 };
677
678 static struct tevent_req *gensec_krb5_update_send(TALLOC_CTX *mem_ctx,
679                                                   struct tevent_context *ev,
680                                                   struct gensec_security *gensec_security,
681                                                   const DATA_BLOB in)
682 {
683         struct tevent_req *req = NULL;
684         struct gensec_krb5_update_state *state = NULL;
685         NTSTATUS status;
686
687         req = tevent_req_create(mem_ctx, &state,
688                                 struct gensec_krb5_update_state);
689         if (req == NULL) {
690                 return NULL;
691         }
692
693         status = gensec_krb5_update_internal(gensec_security,
694                                              state, ev, in,
695                                              &state->out);
696         state->status = status;
697         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
698                 tevent_req_done(req);
699                 return tevent_req_post(req, ev);
700         }
701         if (tevent_req_nterror(req, status)) {
702                 return tevent_req_post(req, ev);
703         }
704
705         tevent_req_done(req);
706         return tevent_req_post(req, ev);
707 }
708
709 static NTSTATUS gensec_krb5_update_recv(struct tevent_req *req,
710                                         TALLOC_CTX *out_mem_ctx,
711                                         DATA_BLOB *out)
712 {
713         struct gensec_krb5_update_state *state =
714                 tevent_req_data(req,
715                 struct gensec_krb5_update_state);
716         NTSTATUS status;
717
718         *out = data_blob_null;
719
720         if (tevent_req_is_nterror(req, &status)) {
721                 tevent_req_received(req);
722                 return status;
723         }
724
725         *out = state->out;
726         talloc_steal(out_mem_ctx, state->out.data);
727         status = state->status;
728         tevent_req_received(req);
729         return status;
730 }
731
732 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, 
733                                         TALLOC_CTX *mem_ctx,
734                                         DATA_BLOB *session_key) 
735 {
736         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
737         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
738         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
739         krb5_error_code err = -1;
740         bool remote = false;
741         bool ok;
742
743         if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
744                 return NT_STATUS_NO_USER_SESSION_KEY;
745         }
746
747         switch (gensec_security->gensec_role) {
748         case GENSEC_CLIENT:
749                 remote = false;
750                 break;
751         case GENSEC_SERVER:
752                 remote = true;
753                 break;
754         }
755
756         ok = smb_krb5_get_smb_session_key(mem_ctx,
757                                           context,
758                                           auth_context,
759                                           session_key,
760                                           remote);
761         if (!ok) {
762                 DEBUG(10, ("KRB5 error getting session key %d\n", err));
763                 return NT_STATUS_NO_USER_SESSION_KEY;
764         }
765
766         return NT_STATUS_OK;
767 }
768
769 #ifdef SAMBA4_USES_HEIMDAL
770 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
771                                          TALLOC_CTX *mem_ctx,
772                                          struct auth_session_info **_session_info) 
773 {
774         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
775         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
776         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
777         struct auth_session_info *session_info = NULL;
778
779         krb5_principal client_principal;
780         char *principal_string = NULL;
781         
782         DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
783         krb5_data pac_data;
784
785         krb5_error_code ret;
786
787         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
788         if (!tmp_ctx) {
789                 return NT_STATUS_NO_MEMORY;
790         }
791         
792         ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
793         if (ret) {
794                 DEBUG(5, ("krb5_ticket_get_client failed to get client principal: %s\n", 
795                           smb_get_krb5_error_message(context, 
796                                                      ret, tmp_ctx)));
797                 talloc_free(tmp_ctx);
798                 return NT_STATUS_NO_MEMORY;
799         }
800         
801         ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 
802                                 client_principal, &principal_string);
803         if (ret) {
804                 DEBUG(1, ("Unable to parse client principal: %s\n",
805                           smb_get_krb5_error_message(context, 
806                                                      ret, tmp_ctx)));
807                 krb5_free_principal(context, client_principal);
808                 talloc_free(tmp_ctx);
809                 return NT_STATUS_NO_MEMORY;
810         }
811
812         ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 
813                                                       KRB5_AUTHDATA_WIN2K_PAC, 
814                                                       &pac_data);
815         
816         if (ret) {
817                 /* NO pac */
818                 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 
819                           smb_get_krb5_error_message(context, 
820                                                      ret, tmp_ctx)));
821         } else {
822                 /* Found pac */
823                 pac_blob = data_blob_talloc(tmp_ctx, pac_data.data, pac_data.length);
824                 smb_krb5_free_data_contents(context, &pac_data);
825                 if (!pac_blob.data) {
826                         free(principal_string);
827                         krb5_free_principal(context, client_principal);
828                         talloc_free(tmp_ctx);
829                         return NT_STATUS_NO_MEMORY;
830                 }
831
832                 /* decode and verify the pac */
833                 nt_status = kerberos_decode_pac(gensec_krb5_state,
834                                                 pac_blob,
835                                                 gensec_krb5_state->smb_krb5_context->krb5_context,
836                                                 NULL, gensec_krb5_state->keyblock,
837                                                 client_principal,
838                                                 gensec_krb5_state->ticket->ticket.authtime, NULL);
839
840                 if (!NT_STATUS_IS_OK(nt_status)) {
841                         free(principal_string);
842                         krb5_free_principal(context, client_principal);
843                         talloc_free(tmp_ctx);
844                         return nt_status;
845                 }
846
847                 pac_blob_ptr = &pac_blob;
848         }
849
850         nt_status = gensec_generate_session_info_pac(tmp_ctx,
851                                                      gensec_security,
852                                                      gensec_krb5_state->smb_krb5_context,
853                                                      pac_blob_ptr, principal_string,
854                                                      gensec_get_remote_address(gensec_security),
855                                                      &session_info);
856
857         free(principal_string);
858         krb5_free_principal(context, client_principal);
859
860         if (!NT_STATUS_IS_OK(nt_status)) {
861                 talloc_free(tmp_ctx);
862                 return nt_status;
863         }
864
865         nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);
866
867         if (!NT_STATUS_IS_OK(nt_status)) {
868                 talloc_free(tmp_ctx);
869                 return nt_status;
870         }
871
872         *_session_info = talloc_steal(mem_ctx, session_info);
873
874         talloc_free(tmp_ctx);
875         return NT_STATUS_OK;
876 }
877 #else /* MIT KERBEROS */
878 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
879                                          TALLOC_CTX *mem_ctx,
880                                          struct auth_session_info **psession_info)
881 {
882         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
883         struct gensec_krb5_state *gensec_krb5_state =
884                 (struct gensec_krb5_state *)gensec_security->private_data;
885         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
886         struct auth_session_info *session_info = NULL;
887
888         krb5_principal client_principal;
889         char *principal_string = NULL;
890
891         krb5_authdata **auth_pac_data = NULL;
892         DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
893
894         krb5_error_code code;
895
896         TALLOC_CTX *tmp_ctx;
897
898         tmp_ctx = talloc_new(mem_ctx);
899         if (tmp_ctx == NULL) {
900                 return NT_STATUS_NO_MEMORY;
901         }
902
903         code = krb5_copy_principal(context,
904                                    gensec_krb5_state->ticket->enc_part2->client,
905                                    &client_principal);
906         if (code != 0) {
907                 DBG_INFO("krb5_copy_principal failed to copy client "
908                          "principal: %s\n",
909                          smb_get_krb5_error_message(context, code, tmp_ctx));
910                 talloc_free(tmp_ctx);
911                 return NT_STATUS_NO_MEMORY;
912         }
913
914         code = krb5_unparse_name(context, client_principal, &principal_string);
915         if (code != 0) {
916                 DBG_WARNING("Unable to parse client principal: %s\n",
917                             smb_get_krb5_error_message(context, code, tmp_ctx));
918                 krb5_free_principal(context, client_principal);
919                 talloc_free(tmp_ctx);
920                 return NT_STATUS_NO_MEMORY;
921         }
922
923         code = krb5_find_authdata(context,
924                                   gensec_krb5_state->ticket->enc_part2->authorization_data,
925                                   NULL,
926                                   KRB5_AUTHDATA_WIN2K_PAC,
927                                   &auth_pac_data);
928         if (code != 0) {
929                 /* NO pac */
930                 DBG_INFO("krb5_find_authdata failed to find PAC: %s\n",
931                          smb_get_krb5_error_message(context, code, tmp_ctx));
932         } else {
933                 krb5_timestamp ticket_authtime =
934                         gensec_krb5_state->ticket->enc_part2->times.authtime;
935
936                 /* Found pac */
937                 pac_blob = data_blob_talloc(tmp_ctx,
938                                             auth_pac_data[0]->contents,
939                                             auth_pac_data[0]->length);
940                 krb5_free_authdata(context, auth_pac_data);
941                 if (pac_blob.data == NULL) {
942                         free(principal_string);
943                         krb5_free_principal(context, client_principal);
944                         talloc_free(tmp_ctx);
945                         return NT_STATUS_NO_MEMORY;
946                 }
947
948                 /* decode and verify the pac */
949                 status = kerberos_decode_pac(gensec_krb5_state,
950                                              pac_blob,
951                                              context,
952                                              NULL,
953                                              gensec_krb5_state->keyblock,
954                                              client_principal,
955                                              ticket_authtime,
956                                              NULL);
957
958                 if (!NT_STATUS_IS_OK(status)) {
959                         free(principal_string);
960                         krb5_free_principal(context, client_principal);
961                         talloc_free(tmp_ctx);
962                         return status;
963                 }
964
965                 pac_blob_ptr = &pac_blob;
966         }
967         krb5_free_principal(context, client_principal);
968
969         status = gensec_generate_session_info_pac(tmp_ctx,
970                                                   gensec_security,
971                                                   gensec_krb5_state->smb_krb5_context,
972                                                   pac_blob_ptr,
973                                                   principal_string,
974                                                   gensec_get_remote_address(gensec_security),
975                                                   &session_info);
976         SAFE_FREE(principal_string);
977         if (!NT_STATUS_IS_OK(status)) {
978                 talloc_free(tmp_ctx);
979                 return status;
980         }
981
982         status = gensec_krb5_session_key(gensec_security,
983                                          session_info,
984                                          &session_info->session_key);
985         if (!NT_STATUS_IS_OK(status)) {
986                 talloc_free(tmp_ctx);
987                 return status;
988         }
989
990         *psession_info = talloc_steal(mem_ctx, session_info);
991         talloc_free(tmp_ctx);
992
993         return NT_STATUS_OK;
994 }
995 #endif /* SAMBA4_USES_HEIMDAL */
996
997 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security, 
998                                    TALLOC_CTX *mem_ctx, 
999                                    const DATA_BLOB *in, 
1000                                    DATA_BLOB *out)
1001 {
1002         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1003         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
1004         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
1005         krb5_error_code ret;
1006         krb5_data input, output;
1007         input.length = in->length;
1008         input.data = (char *)in->data;
1009         
1010         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
1011                 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
1012                 if (ret) {
1013                         DEBUG(1, ("krb5_mk_priv failed: %s\n", 
1014                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
1015                                                              ret, mem_ctx)));
1016                         return NT_STATUS_ACCESS_DENIED;
1017                 }
1018                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1019                 
1020                 smb_krb5_free_data_contents(context, &output);
1021         } else {
1022                 return NT_STATUS_ACCESS_DENIED;
1023         }
1024         return NT_STATUS_OK;
1025 }
1026
1027 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, 
1028                                      TALLOC_CTX *mem_ctx, 
1029                                      const DATA_BLOB *in, 
1030                                      DATA_BLOB *out)
1031 {
1032         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1033         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
1034         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
1035         krb5_error_code ret;
1036         krb5_data input, output;
1037         krb5_replay_data replay;
1038         input.length = in->length;
1039         input.data = (char *)in->data;
1040         
1041         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
1042                 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
1043                 if (ret) {
1044                         DEBUG(1, ("krb5_rd_priv failed: %s\n", 
1045                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
1046                                                              ret, mem_ctx)));
1047                         return NT_STATUS_ACCESS_DENIED;
1048                 }
1049                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
1050                 
1051                 smb_krb5_free_data_contents(context, &output);
1052         } else {
1053                 return NT_STATUS_ACCESS_DENIED;
1054         }
1055         return NT_STATUS_OK;
1056 }
1057
1058 static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
1059                                      uint32_t feature)
1060 {
1061         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
1062         if (feature & GENSEC_FEATURE_SESSION_KEY) {
1063                 return true;
1064         } 
1065         if (gensec_krb5_state->gssapi) {
1066                 return false;
1067         }
1068
1069         /*
1070          * krb5_mk_priv provides SIGN and SEAL
1071          */
1072         if (feature & GENSEC_FEATURE_SIGN) {
1073                 return true;
1074         }
1075         if (feature & GENSEC_FEATURE_SEAL) {
1076                 return true;
1077         }
1078
1079         return false;
1080 }
1081
1082 static const char *gensec_krb5_final_auth_type(struct gensec_security *gensec_security)
1083 {
1084         return GENSEC_FINAL_AUTH_TYPE_KRB5;
1085 }
1086
1087 static const char *gensec_krb5_oids[] = { 
1088         GENSEC_OID_KERBEROS5,
1089         GENSEC_OID_KERBEROS5_OLD,
1090         NULL 
1091 };
1092
1093 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
1094         .name           = "fake_gssapi_krb5",
1095         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
1096         .oid            = gensec_krb5_oids,
1097         .client_start   = gensec_fake_gssapi_krb5_client_start,
1098         .server_start   = gensec_fake_gssapi_krb5_server_start,
1099         .update_send    = gensec_krb5_update_send,
1100         .update_recv    = gensec_krb5_update_recv,
1101         .magic          = gensec_magic_check_krb5_oid,
1102         .session_key    = gensec_krb5_session_key,
1103         .session_info   = gensec_krb5_session_info,
1104         .have_feature   = gensec_krb5_have_feature,
1105         .final_auth_type = gensec_krb5_final_auth_type,
1106         .enabled        = false,
1107         .kerberos       = true,
1108         .priority       = GENSEC_KRB5,
1109 };
1110
1111 static const struct gensec_security_ops gensec_krb5_security_ops = {
1112         .name           = "krb5",
1113         .client_start   = gensec_krb5_client_start,
1114         .server_start   = gensec_krb5_server_start,
1115         .update_send    = gensec_krb5_update_send,
1116         .update_recv    = gensec_krb5_update_recv,
1117         .session_key    = gensec_krb5_session_key,
1118         .session_info   = gensec_krb5_session_info,
1119         .have_feature   = gensec_krb5_have_feature,
1120         .wrap           = gensec_krb5_wrap,
1121         .unwrap         = gensec_krb5_unwrap,
1122         .final_auth_type = gensec_krb5_final_auth_type,
1123         .enabled        = true,
1124         .kerberos       = true,
1125         .priority       = GENSEC_KRB5
1126 };
1127
1128 _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *ctx)
1129 {
1130         NTSTATUS ret;
1131
1132         ret = gensec_register(ctx, &gensec_krb5_security_ops);
1133         if (!NT_STATUS_IS_OK(ret)) {
1134                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1135                         gensec_krb5_security_ops.name));
1136                 return ret;
1137         }
1138
1139         ret = gensec_register(ctx, &gensec_fake_gssapi_krb5_security_ops);
1140         if (!NT_STATUS_IS_OK(ret)) {
1141                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
1142                         gensec_fake_gssapi_krb5_security_ops.name));
1143                 return ret;
1144         }
1145
1146         return ret;
1147 }