r10066: This is the second in my patches to work on Samba4's kerberos support,
[samba.git] / source4 / auth / gensec / gensec_gssapi.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Kerberos backend for GENSEC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7    Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-2005
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "system/kerberos.h"
27 #include "system/network.h"
28 #include "auth/kerberos/kerberos.h"
29 #include "librpc/gen_ndr/ndr_krb5pac.h"
30 #include "auth/auth.h"
31
32 struct gensec_gssapi_state {
33         gss_ctx_id_t gssapi_context;
34         struct gss_channel_bindings_struct *input_chan_bindings;
35         gss_name_t server_name;
36         gss_name_t client_name;
37         OM_uint32 want_flags, got_flags;
38         const gss_OID_desc *gss_oid;
39
40         DATA_BLOB session_key;
41         DATA_BLOB pac;
42
43         struct smb_krb5_context *smb_krb5_context;
44         krb5_ccache ccache;
45         const char *ccache_name;
46         krb5_keytab keytab;
47
48         gss_cred_id_t cred;
49 };
50
51 static char *gssapi_error_string(TALLOC_CTX *mem_ctx, 
52                                  OM_uint32 maj_stat, OM_uint32 min_stat)
53 {
54         OM_uint32 disp_min_stat, disp_maj_stat;
55         gss_buffer_desc maj_error_message;
56         gss_buffer_desc min_error_message;
57         OM_uint32 msg_ctx = 0;
58
59         char *ret;
60
61         maj_error_message.value = NULL;
62         min_error_message.value = NULL;
63         
64         disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
65                            GSS_C_NULL_OID, &msg_ctx, &maj_error_message);
66         disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
67                            GSS_C_NULL_OID, &msg_ctx, &min_error_message);
68         ret = talloc_asprintf(mem_ctx, "%s: %s", (char *)maj_error_message.value, (char *)min_error_message.value);
69
70         gss_release_buffer(&disp_min_stat, &maj_error_message);
71         gss_release_buffer(&disp_min_stat, &min_error_message);
72
73         return ret;
74 }
75
76
77 static int gensec_gssapi_destory(void *ptr) 
78 {
79         struct gensec_gssapi_state *gensec_gssapi_state = ptr;
80         OM_uint32 maj_stat, min_stat;
81         
82         if (gensec_gssapi_state->cred != GSS_C_NO_CREDENTIAL) {
83                 maj_stat = gss_release_cred(&min_stat, 
84                                             &gensec_gssapi_state->cred);
85         }
86
87         if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) {
88                 maj_stat = gss_delete_sec_context (&min_stat,
89                                                    &gensec_gssapi_state->gssapi_context,
90                                                    GSS_C_NO_BUFFER);
91         }
92
93         if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) {
94                 maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name);
95         }
96         if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
97                 maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
98         }
99         return 0;
100 }
101
102 static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
103 {
104         struct gensec_gssapi_state *gensec_gssapi_state;
105         krb5_error_code ret;
106         
107         gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state);
108         if (!gensec_gssapi_state) {
109                 return NT_STATUS_NO_MEMORY;
110         }
111
112         gensec_security->private_data = gensec_gssapi_state;
113
114         gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT;
115         gensec_gssapi_state->server_name = GSS_C_NO_NAME;
116         gensec_gssapi_state->client_name = GSS_C_NO_NAME;
117
118         /* TODO: Fill in channel bindings */
119         gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
120         
121         gensec_gssapi_state->want_flags = GSS_C_MUTUAL_FLAG;
122         gensec_gssapi_state->got_flags = 0;
123
124         gensec_gssapi_state->session_key = data_blob(NULL, 0);
125         gensec_gssapi_state->pac = data_blob(NULL, 0);
126
127         gensec_gssapi_state->cred = GSS_C_NO_CREDENTIAL;
128
129         talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); 
130
131         if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
132                 gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
133         }
134         if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
135                 gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG;
136         }
137         if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
138                 gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE;
139         }
140
141         gensec_gssapi_state->gss_oid = gss_mech_krb5;
142         
143         ret = smb_krb5_init_context(gensec_gssapi_state, 
144                                     &gensec_gssapi_state->smb_krb5_context);
145         if (ret) {
146                 DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",                                  
147                          error_message(ret)));
148                 return NT_STATUS_INTERNAL_ERROR;
149         }
150         return NT_STATUS_OK;
151 }
152
153 static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security)
154 {
155         NTSTATUS nt_status;
156         OM_uint32 maj_stat, min_stat;
157         gss_buffer_desc name_token;
158         struct gensec_gssapi_state *gensec_gssapi_state;
159         struct cli_credentials *machine_account;
160
161         nt_status = gensec_gssapi_start(gensec_security);
162         if (!NT_STATUS_IS_OK(nt_status)) {
163                 return nt_status;
164         }
165
166         gensec_gssapi_state = gensec_security->private_data;
167
168         machine_account = cli_credentials_init(gensec_gssapi_state);
169         cli_credentials_set_conf(machine_account);
170         nt_status = cli_credentials_set_machine_account(machine_account);
171         
172         if (!NT_STATUS_IS_OK(nt_status)) {
173                 DEBUG(3, ("Could not obtain machine account credentials from the local database\n"));
174                 talloc_free(machine_account);
175                 return nt_status;
176         } else {
177                 nt_status = create_memory_keytab(gensec_gssapi_state,
178                                                  machine_account, 
179                                                  gensec_gssapi_state->smb_krb5_context,
180                                                  &gensec_gssapi_state->keytab);
181                 if (!NT_STATUS_IS_OK(nt_status)) {
182                         DEBUG(3, ("Could not create memory keytab!\n"));
183                         talloc_free(machine_account);
184                         return nt_status;
185                 }
186         }
187
188         name_token.value = cli_credentials_get_principal(machine_account, 
189                                                          machine_account);
190         name_token.length = strlen(name_token.value);
191
192         maj_stat = gss_import_name (&min_stat,
193                                     &name_token,
194                                     GSS_C_NT_USER_NAME,
195                                     &gensec_gssapi_state->server_name);
196         talloc_free(machine_account);
197
198         if (maj_stat) {
199                 DEBUG(2, ("GSS Import name of %s failed: %s\n",
200                           (char *)name_token.value,
201                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
202                 return NT_STATUS_UNSUCCESSFUL;
203         }
204
205         maj_stat = gsskrb5_acquire_cred(&min_stat, 
206                                         gensec_gssapi_state->keytab, NULL,
207                                         gensec_gssapi_state->server_name,
208                                         GSS_C_INDEFINITE,
209                                         GSS_C_NULL_OID_SET,
210                                         GSS_C_ACCEPT,
211                                         &gensec_gssapi_state->cred,
212                                         NULL, 
213                                         NULL);
214         if (maj_stat) {
215                 DEBUG(1, ("Aquiring acceptor credentails failed: %s\n", 
216                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
217                 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
218         }
219
220         return NT_STATUS_OK;
221
222 }
223
224 static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security)
225 {
226         struct gensec_gssapi_state *gensec_gssapi_state;
227         struct cli_credentials *creds = gensec_get_credentials(gensec_security);
228         struct ccache_container *ccache;
229         krb5_error_code ret;
230         NTSTATUS nt_status;
231         gss_buffer_desc name_token;
232         OM_uint32 maj_stat, min_stat;
233         const char *hostname = gensec_get_target_hostname(gensec_security);
234
235         if (!hostname) {
236                 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
237                 return NT_STATUS_INVALID_PARAMETER;
238         }
239         if (is_ipaddress(hostname)) {
240                 DEBUG(2, ("Cannot do GSSAPI to a IP address"));
241                 return NT_STATUS_INVALID_PARAMETER;
242         }
243
244         nt_status = gensec_gssapi_start(gensec_security);
245         if (!NT_STATUS_IS_OK(nt_status)) {
246                 return nt_status;
247         }
248
249         gensec_gssapi_state = gensec_security->private_data;
250
251         name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", 
252                                            gensec_get_target_service(gensec_security), 
253                                            hostname);
254         name_token.length = strlen(name_token.value);
255
256         maj_stat = gss_import_name (&min_stat,
257                                     &name_token,
258                                     GSS_C_NT_HOSTBASED_SERVICE,
259                                     &gensec_gssapi_state->server_name);
260         if (maj_stat) {
261                 DEBUG(2, ("GSS Import name of %s failed: %s\n",
262                           (char *)name_token.value,
263                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
264                 return NT_STATUS_INVALID_PARAMETER;
265         }
266
267         ret = cli_credentials_get_ccache(creds, 
268                                          &ccache);
269         if (ret) {
270                 DEBUG(1, ("Failed to get CCACHE for gensec_gssapi: %s\n", error_message(ret)));
271                 return NT_STATUS_UNSUCCESSFUL;
272         }
273
274         name_token.value = cli_credentials_get_principal(creds, 
275                                                          gensec_gssapi_state);
276         name_token.length = strlen(name_token.value);
277
278         maj_stat = gss_import_name (&min_stat,
279                                     &name_token,
280                                     GSS_C_NT_USER_NAME,
281                                     &gensec_gssapi_state->client_name);
282         if (maj_stat) {
283                 DEBUG(2, ("GSS Import name of %s failed: %s\n",
284                           (char *)name_token.value,
285                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
286                 return NT_STATUS_UNSUCCESSFUL;
287         }
288
289         maj_stat = gsskrb5_acquire_cred(&min_stat, 
290                                         NULL, ccache->ccache,
291                                         gensec_gssapi_state->client_name,
292                                         GSS_C_INDEFINITE,
293                                         GSS_C_NULL_OID_SET,
294                                         GSS_C_INITIATE,
295                                         &gensec_gssapi_state->cred,
296                                         NULL, 
297                                         NULL);
298         if (maj_stat) {
299                 DEBUG(1, ("Aquiring initiator credentails failed: %s\n", 
300                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
301                 return NT_STATUS_UNSUCCESSFUL;
302         }
303
304         return NT_STATUS_OK;
305 }
306
307
308 /**
309  * Check if the packet is one for this mechansim
310  * 
311  * @param gensec_security GENSEC state
312  * @param in The request, as a DATA_BLOB
313  * @return Error, INVALID_PARAMETER if it's not a packet for us
314  *                or NT_STATUS_OK if the packet is ok. 
315  */
316
317 static NTSTATUS gensec_gssapi_magic(struct gensec_security *gensec_security, 
318                                     const DATA_BLOB *in) 
319 {
320         if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
321                 return NT_STATUS_OK;
322         } else {
323                 return NT_STATUS_INVALID_PARAMETER;
324         }
325 }
326
327
328 /**
329  * Next state function for the GSSAPI GENSEC mechanism
330  * 
331  * @param gensec_gssapi_state GSSAPI State
332  * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
333  * @param in The request, as a DATA_BLOB
334  * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
335  * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
336  *                or NT_STATUS_OK if the user is authenticated. 
337  */
338
339 static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
340                                    TALLOC_CTX *out_mem_ctx, 
341                                    const DATA_BLOB in, DATA_BLOB *out) 
342 {
343         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
344         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
345         OM_uint32 maj_stat, min_stat;
346         OM_uint32 min_stat2;
347         gss_buffer_desc input_token, output_token;
348         gss_OID gss_oid_p;
349         input_token.length = in.length;
350         input_token.value = in.data;
351
352         switch (gensec_security->gensec_role) {
353         case GENSEC_CLIENT:
354         {
355                 maj_stat = gss_init_sec_context(&min_stat, 
356                                                 gensec_gssapi_state->cred,
357                                                 &gensec_gssapi_state->gssapi_context, 
358                                                 gensec_gssapi_state->server_name, 
359                                                 discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid),
360                                                 gensec_gssapi_state->want_flags, 
361                                                 0, 
362                                                 gensec_gssapi_state->input_chan_bindings,
363                                                 &input_token, 
364                                                 NULL, 
365                                                 &output_token, 
366                                                 &gensec_gssapi_state->got_flags, /* ret flags */
367                                                 NULL);
368                 break;
369         }
370         case GENSEC_SERVER:
371         {
372                 maj_stat = gss_accept_sec_context(&min_stat, 
373                                                   &gensec_gssapi_state->gssapi_context, 
374                                                   gensec_gssapi_state->cred,
375                                                   &input_token, 
376                                                   gensec_gssapi_state->input_chan_bindings,
377                                                   &gensec_gssapi_state->client_name, 
378                                                   &gss_oid_p,
379                                                   &output_token, 
380                                                   &gensec_gssapi_state->got_flags, 
381                                                   NULL, 
382                                                   NULL);
383                 gensec_gssapi_state->gss_oid = gss_oid_p;
384                 break;
385         }
386         default:
387                 return NT_STATUS_INVALID_PARAMETER;
388                 
389         }
390
391         if (maj_stat == GSS_S_COMPLETE) {
392                 *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
393                 gss_release_buffer(&min_stat2, &output_token);
394
395                 return NT_STATUS_OK;
396         } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
397                 *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
398                 gss_release_buffer(&min_stat2, &output_token);
399
400                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
401         } else {
402                 if (maj_stat == GSS_S_FAILURE
403                     && (min_stat == KRB5KRB_AP_ERR_BADVERSION || min_stat == KRB5KRB_AP_ERR_MSG_TYPE)) {
404                         /* garbage input, possibly from the auto-mech detection */
405                         return NT_STATUS_INVALID_PARAMETER;
406                 }
407                 DEBUG(1, ("GSS Update failed: %s\n", 
408                           gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
409                 return nt_status;
410         }
411 }
412
413 static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
414                                    TALLOC_CTX *mem_ctx, 
415                                    const DATA_BLOB *in, 
416                                    DATA_BLOB *out)
417 {
418         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
419         OM_uint32 maj_stat, min_stat;
420         gss_buffer_desc input_token, output_token;
421         int conf_state;
422         input_token.length = in->length;
423         input_token.value = in->data;
424         
425         maj_stat = gss_wrap(&min_stat, 
426                             gensec_gssapi_state->gssapi_context, 
427                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
428                             GSS_C_QOP_DEFAULT,
429                             &input_token,
430                             &conf_state,
431                             &output_token);
432         if (GSS_ERROR(maj_stat)) {
433                 DEBUG(1, ("gensec_gssapi_wrap: GSS Wrap failed: %s\n", 
434                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
435                 return NT_STATUS_ACCESS_DENIED;
436         }
437
438         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
439         gss_release_buffer(&min_stat, &output_token);
440
441         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
442             && !conf_state) {
443                 return NT_STATUS_ACCESS_DENIED;
444         }
445         return NT_STATUS_OK;
446 }
447
448 static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, 
449                                      TALLOC_CTX *mem_ctx, 
450                                      const DATA_BLOB *in, 
451                                      DATA_BLOB *out)
452 {
453         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
454         OM_uint32 maj_stat, min_stat;
455         gss_buffer_desc input_token, output_token;
456         int conf_state;
457         gss_qop_t qop_state;
458         input_token.length = in->length;
459         input_token.value = in->data;
460         
461         maj_stat = gss_unwrap(&min_stat, 
462                               gensec_gssapi_state->gssapi_context, 
463                               &input_token,
464                               &output_token, 
465                               &conf_state,
466                               &qop_state);
467         if (GSS_ERROR(maj_stat)) {
468                 DEBUG(1, ("gensec_gssapi_unwrap: GSS UnWrap failed: %s\n", 
469                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
470                 return NT_STATUS_ACCESS_DENIED;
471         }
472
473         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
474         gss_release_buffer(&min_stat, &output_token);
475         
476         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
477             && !conf_state) {
478                 return NT_STATUS_ACCESS_DENIED;
479         }
480         return NT_STATUS_OK;
481 }
482
483 static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security) 
484 {
485         /* not const but work for DCERPC packets and arcfour */
486         return 45;
487 }
488
489 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
490                                           TALLOC_CTX *mem_ctx, 
491                                           uint8_t *data, size_t length, 
492                                           const uint8_t *whole_pdu, size_t pdu_length, 
493                                           DATA_BLOB *sig)
494 {
495         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
496         OM_uint32 maj_stat, min_stat;
497         gss_buffer_desc input_token, output_token;
498         int conf_state;
499         ssize_t sig_length = 0;
500
501         input_token.length = length;
502         input_token.value = data;
503         
504         maj_stat = gss_wrap(&min_stat, 
505                             gensec_gssapi_state->gssapi_context,
506                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
507                             GSS_C_QOP_DEFAULT,
508                             &input_token,
509                             &conf_state,
510                             &output_token);
511         if (GSS_ERROR(maj_stat)) {
512                 DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap failed: %s\n", 
513                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
514                 return NT_STATUS_ACCESS_DENIED;
515         }
516
517         if (output_token.length < length) {
518                 return NT_STATUS_INTERNAL_ERROR;
519         }
520
521         sig_length = 45;
522
523         memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
524         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
525
526         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
527         dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
528         dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
529
530         gss_release_buffer(&min_stat, &output_token);
531
532         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
533             && !conf_state) {
534                 return NT_STATUS_ACCESS_DENIED;
535         }
536         return NT_STATUS_OK;
537 }
538
539 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
540                                             TALLOC_CTX *mem_ctx, 
541                                             uint8_t *data, size_t length, 
542                                             const uint8_t *whole_pdu, size_t pdu_length,
543                                             const DATA_BLOB *sig)
544 {
545         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
546         OM_uint32 maj_stat, min_stat;
547         gss_buffer_desc input_token, output_token;
548         int conf_state;
549         gss_qop_t qop_state;
550         DATA_BLOB in;
551
552         dump_data_pw("gensec_gssapi_unseal_packet: sig\n", sig->data, sig->length);
553
554         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
555
556         memcpy(in.data, sig->data, sig->length);
557         memcpy(in.data + sig->length, data, length);
558
559         input_token.length = in.length;
560         input_token.value = in.data;
561         
562         maj_stat = gss_unwrap(&min_stat, 
563                               gensec_gssapi_state->gssapi_context, 
564                               &input_token,
565                               &output_token, 
566                               &conf_state,
567                               &qop_state);
568         if (GSS_ERROR(maj_stat)) {
569                 DEBUG(1, ("gensec_gssapi_unseal_packet: GSS UnWrap failed: %s\n", 
570                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
571                 return NT_STATUS_ACCESS_DENIED;
572         }
573
574         if (output_token.length != length) {
575                 return NT_STATUS_INTERNAL_ERROR;
576         }
577
578         memcpy(data, output_token.value, length);
579
580         gss_release_buffer(&min_stat, &output_token);
581         
582         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
583             && !conf_state) {
584                 return NT_STATUS_ACCESS_DENIED;
585         }
586         return NT_STATUS_OK;
587 }
588
589 static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, 
590                                           TALLOC_CTX *mem_ctx, 
591                                           const uint8_t *data, size_t length, 
592                                           const uint8_t *whole_pdu, size_t pdu_length, 
593                                           DATA_BLOB *sig)
594 {
595         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
596         OM_uint32 maj_stat, min_stat;
597         gss_buffer_desc input_token, output_token;
598         int conf_state;
599         ssize_t sig_length = 0;
600
601         input_token.length = length;
602         input_token.value = discard_const_p(uint8_t *, data);
603
604         maj_stat = gss_wrap(&min_stat, 
605                             gensec_gssapi_state->gssapi_context,
606                             0,
607                             GSS_C_QOP_DEFAULT,
608                             &input_token,
609                             &conf_state,
610                             &output_token);
611         if (GSS_ERROR(maj_stat)) {
612                 DEBUG(1, ("GSS Wrap failed: %s\n", 
613                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
614                 return NT_STATUS_ACCESS_DENIED;
615         }
616
617         if (output_token.length < length) {
618                 return NT_STATUS_INTERNAL_ERROR;
619         }
620
621         sig_length = 45;
622
623         /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
624         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
625
626         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
627
628         gss_release_buffer(&min_stat, &output_token);
629
630         return NT_STATUS_OK;
631 }
632
633 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
634                                            TALLOC_CTX *mem_ctx, 
635                                            const uint8_t *data, size_t length, 
636                                            const uint8_t *whole_pdu, size_t pdu_length, 
637                                            const DATA_BLOB *sig)
638 {
639         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
640         OM_uint32 maj_stat, min_stat;
641         gss_buffer_desc input_token, output_token;
642         int conf_state;
643         gss_qop_t qop_state;
644         DATA_BLOB in;
645
646         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
647
648         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
649
650         memcpy(in.data, sig->data, sig->length);
651         memcpy(in.data + sig->length, data, length);
652
653         input_token.length = in.length;
654         input_token.value = in.data;
655         
656         maj_stat = gss_unwrap(&min_stat, 
657                               gensec_gssapi_state->gssapi_context, 
658                               &input_token,
659                               &output_token, 
660                               &conf_state,
661                               &qop_state);
662         if (GSS_ERROR(maj_stat)) {
663                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
664                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
665                 return NT_STATUS_ACCESS_DENIED;
666         }
667
668         if (output_token.length != length) {
669                 return NT_STATUS_INTERNAL_ERROR;
670         }
671
672         gss_release_buffer(&min_stat, &output_token);
673
674         return NT_STATUS_OK;
675 }
676
677 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
678                                        uint32_t feature) 
679 {
680         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
681         if (feature & GENSEC_FEATURE_SIGN) {
682                 return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
683         }
684         if (feature & GENSEC_FEATURE_SEAL) {
685                 return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
686         }
687         if (feature & GENSEC_FEATURE_SESSION_KEY) {
688                 if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
689                     && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, gensec_gssapi_state->gss_oid->length) == 0)) {
690                         return True;
691                 }
692         }
693         if (feature & GENSEC_FEATURE_DCE_STYLE) {
694                 return gensec_gssapi_state->got_flags & GSS_C_DCE_STYLE;
695         }
696         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
697                 return True;
698         }
699         return False;
700 }
701
702 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
703                                           DATA_BLOB *session_key) 
704 {
705         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
706         
707         if (gensec_gssapi_state->session_key.data) {
708                 *session_key = gensec_gssapi_state->session_key;
709                 return NT_STATUS_OK;
710         }
711
712         /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
713         if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
714             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
715                        gensec_gssapi_state->gss_oid->length) == 0)) {
716                 OM_uint32 maj_stat, min_stat;
717                 gss_buffer_desc skey;
718                 
719                 maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
720                                                         gensec_gssapi_state->gssapi_context, 
721                                                         &skey);
722                 
723                 if (maj_stat == 0) {
724                         DEBUG(10, ("Got KRB5 session key of length %d\n",  
725                                    (int)skey.length));
726                         gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
727                                                                             skey.value, skey.length);
728                         *session_key = gensec_gssapi_state->session_key;
729                         dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
730                         
731                         gss_release_buffer(&min_stat, &skey);
732                         return NT_STATUS_OK;
733                 }
734                 return NT_STATUS_NO_USER_SESSION_KEY;
735         }
736         
737         DEBUG(1, ("NO session key for this mech\n"));
738         return NT_STATUS_NO_USER_SESSION_KEY;
739 }
740
741 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
742                                          struct auth_session_info **_session_info) 
743 {
744         NTSTATUS nt_status;
745         TALLOC_CTX *mem_ctx;
746         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
747         struct auth_serversupplied_info *server_info = NULL;
748         struct auth_session_info *session_info = NULL;
749         struct PAC_LOGON_INFO *logon_info;
750         OM_uint32 maj_stat, min_stat;
751         gss_buffer_desc name_token;
752         gss_buffer_desc pac;
753         krb5_keyblock *keyblock;
754         time_t authtime;
755         krb5_principal principal;
756         char *principal_string;
757         
758         if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
759             || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
760                        gensec_gssapi_state->gss_oid->length) != 0)) {
761                 DEBUG(1, ("NO session info available for this mech\n"));
762                 return NT_STATUS_INVALID_PARAMETER;
763         }
764                 
765         mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); 
766         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
767
768         maj_stat = gss_display_name (&min_stat,
769                                      gensec_gssapi_state->client_name,
770                                      &name_token,
771                                      NULL);
772         if (maj_stat) {
773                 return NT_STATUS_FOOBAR;
774         }
775
776         principal_string = talloc_strndup(mem_ctx, name_token.value, name_token.length);
777
778         gss_release_buffer(&min_stat, &name_token);
779
780         if (!principal_string) {
781                 talloc_free(mem_ctx);
782                 return NT_STATUS_NO_MEMORY;
783         }
784
785         maj_stat = gss_krb5_copy_service_keyblock(&min_stat, 
786                                                   gensec_gssapi_state->gssapi_context, 
787                                                   &keyblock);
788
789         if (maj_stat == 0) {
790                 maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
791                                                                      gensec_gssapi_state->gssapi_context, 
792                                                                      &authtime);
793         }
794
795         if (maj_stat == 0) {
796                 maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, 
797                                                                        gensec_gssapi_state->gssapi_context, 
798                                                                        KRB5_AUTHDATA_IF_RELEVANT,
799                                                                        &pac);
800         }
801         
802         if (maj_stat == 0) {
803                 krb5_error_code ret;
804                 DATA_BLOB pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
805                 pac_blob = unwrap_pac(mem_ctx, &pac_blob);
806                 gss_release_buffer(&min_stat, &pac);
807
808                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
809                                       principal_string, &principal);
810                 if (ret) {
811                         talloc_free(mem_ctx);
812                         return NT_STATUS_INVALID_PARAMETER;
813                 }
814                 
815                 /* decode and verify the pac */
816                 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob,
817                                                     gensec_gssapi_state->smb_krb5_context->krb5_context,
818                                                     NULL, keyblock, principal, authtime);
819                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
820
821                 if (NT_STATUS_IS_OK(nt_status)) {
822                         union netr_Validation validation;
823                         validation.sam3 = &logon_info->info3;
824                         nt_status = make_server_info_netlogon_validation(gensec_gssapi_state, 
825                                                                          NULL,
826                                                                          3, &validation,
827                                                                          &server_info); 
828                         if (!NT_STATUS_IS_OK(nt_status)) {
829                                 talloc_free(mem_ctx);
830                                 return nt_status;
831                         }
832                 } else {
833                         maj_stat = 1;
834                 }
835         }
836         
837         if (maj_stat) {
838                 krb5_error_code ret;
839                 DATA_BLOB user_sess_key = data_blob(NULL, 0);
840                 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
841                 /* IF we have the PAC - otherwise we need to get this
842                  * data from elsewere - local ldb, or (TODO) lookup of some
843                  * kind... 
844                  *
845                  * when heimdal can generate the PAC, we should fail if there's
846                  * no PAC present
847                  */
848
849                 char *account_name;
850                 const char *realm;
851                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
852                                       principal_string, &principal);
853                 if (ret) {
854                         talloc_free(mem_ctx);
855                         return NT_STATUS_INVALID_PARAMETER;
856                 }
857                 
858                 realm = krb5_principal_get_realm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
859                                                  principal);
860                 ret = krb5_unparse_name_norealm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
861                                                 principal, &account_name);
862                 if (ret) {
863                         krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
864                         talloc_free(mem_ctx);
865                         return NT_STATUS_NO_MEMORY;
866                 }
867
868                 DEBUG(1, ("Unable to use PAC, resorting to local user lookup!\n"));
869                 nt_status = sam_get_server_info(mem_ctx, account_name, realm,
870                                                 user_sess_key, lm_sess_key,
871                                                 &server_info);
872                 free(account_name);
873                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
874
875                 if (!NT_STATUS_IS_OK(nt_status)) {
876                         talloc_free(mem_ctx);
877                         return nt_status;
878                 }
879         }
880
881         /* references the server_info into the session_info */
882         nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
883         talloc_free(mem_ctx);
884         talloc_free(server_info);
885         NT_STATUS_NOT_OK_RETURN(nt_status);
886
887         nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
888         NT_STATUS_NOT_OK_RETURN(nt_status);
889
890         *_session_info = session_info;
891
892         return NT_STATUS_OK;
893 }
894
895 static const char *gensec_krb5_oids[] = { 
896         GENSEC_OID_KERBEROS5,
897         GENSEC_OID_KERBEROS5_OLD,
898         NULL 
899 };
900
901 /* As a server, this could in theory accept any GSSAPI mech */
902 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
903         .name           = "gssapi_krb5",
904         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
905         .oid            = gensec_krb5_oids,
906         .client_start   = gensec_gssapi_client_start,
907         .server_start   = gensec_gssapi_server_start,
908         .magic          = gensec_gssapi_magic,
909         .update         = gensec_gssapi_update,
910         .session_key    = gensec_gssapi_session_key,
911         .session_info   = gensec_gssapi_session_info,
912         .sig_size       = gensec_gssapi_sig_size,
913         .sign_packet    = gensec_gssapi_sign_packet,
914         .check_packet   = gensec_gssapi_check_packet,
915         .seal_packet    = gensec_gssapi_seal_packet,
916         .unseal_packet  = gensec_gssapi_unseal_packet,
917         .wrap           = gensec_gssapi_wrap,
918         .unwrap         = gensec_gssapi_unwrap,
919         .have_feature   = gensec_gssapi_have_feature,
920         .enabled        = False
921 };
922
923 NTSTATUS gensec_gssapi_init(void)
924 {
925         NTSTATUS ret;
926
927         ret = gensec_register(&gensec_gssapi_krb5_security_ops);
928         if (!NT_STATUS_IS_OK(ret)) {
929                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
930                         gensec_gssapi_krb5_security_ops.name));
931                 return ret;
932         }
933
934         return ret;
935 }