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