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