r7843: Use the new Heimdal gsskrb_acquire_creds API. This has the right
[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
7    Copyright (C) Stefan Metzmacher <metze@samba.org> 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 #undef DBGC_CLASS
33 #define DBGC_CLASS DBGC_AUTH
34
35 struct gensec_gssapi_state {
36         gss_ctx_id_t gssapi_context;
37         struct gss_channel_bindings_struct *input_chan_bindings;
38         gss_name_t server_name;
39         gss_name_t client_name;
40         OM_uint32 want_flags, got_flags;
41         const gss_OID_desc *gss_oid;
42
43         DATA_BLOB session_key;
44         DATA_BLOB pac;
45
46         struct smb_krb5_context *smb_krb5_context;
47         krb5_ccache ccache;
48         const char *ccache_name;
49         krb5_keytab keytab;
50
51         gss_cred_id_t cred;
52 };
53
54 static char *gssapi_error_string(TALLOC_CTX *mem_ctx, 
55                                  OM_uint32 maj_stat, OM_uint32 min_stat)
56 {
57         OM_uint32 disp_min_stat, disp_maj_stat;
58         gss_buffer_desc maj_error_message;
59         gss_buffer_desc min_error_message;
60         OM_uint32 msg_ctx = 0;
61
62         char *ret;
63
64         maj_error_message.value = NULL;
65         min_error_message.value = NULL;
66         
67         disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
68                            GSS_C_NULL_OID, &msg_ctx, &maj_error_message);
69         disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
70                            GSS_C_NULL_OID, &msg_ctx, &min_error_message);
71         ret = talloc_asprintf(mem_ctx, "%s: %s", (char *)maj_error_message.value, (char *)min_error_message.value);
72
73         gss_release_buffer(&disp_min_stat, &maj_error_message);
74         gss_release_buffer(&disp_min_stat, &min_error_message);
75
76         return ret;
77 }
78
79
80 static int gensec_gssapi_destory(void *ptr) 
81 {
82         struct gensec_gssapi_state *gensec_gssapi_state = ptr;
83         OM_uint32 maj_stat, min_stat;
84         
85         if (gensec_gssapi_state->cred != GSS_C_NO_CREDENTIAL) {
86                 maj_stat = gss_release_cred(&min_stat, 
87                                             &gensec_gssapi_state->cred);
88         }
89
90         if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) {
91                 maj_stat = gss_delete_sec_context (&min_stat,
92                                                    &gensec_gssapi_state->gssapi_context,
93                                                    GSS_C_NO_BUFFER);
94         }
95
96         if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) {
97                 maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name);
98         }
99         if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
100                 maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
101         }
102         return 0;
103 }
104
105 static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
106 {
107         struct gensec_gssapi_state *gensec_gssapi_state;
108         krb5_error_code ret;
109         
110         gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state);
111         if (!gensec_gssapi_state) {
112                 return NT_STATUS_NO_MEMORY;
113         }
114
115         gensec_security->private_data = gensec_gssapi_state;
116
117         gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT;
118         gensec_gssapi_state->server_name = GSS_C_NO_NAME;
119         gensec_gssapi_state->client_name = GSS_C_NO_NAME;
120
121         /* TODO: Fill in channel bindings */
122         gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
123         
124         gensec_gssapi_state->want_flags = 0;
125         gensec_gssapi_state->got_flags = 0;
126
127         gensec_gssapi_state->session_key = data_blob(NULL, 0);
128         gensec_gssapi_state->pac = data_blob(NULL, 0);
129
130         gensec_gssapi_state->cred = GSS_C_NO_CREDENTIAL;
131
132         talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); 
133
134         if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
135 #ifndef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
136                 /* GSSAPI won't give us the session keys, without the
137                  * right hooks.  This is critical when requested, so
138                  * fail outright. */
139                 return NT_STATUS_INVALID_PARAMETER;
140 #endif
141         }
142         if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
143                 gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
144         }
145         if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
146                 gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG;
147         }
148         if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
149 #ifndef GSS_C_DCE_STYLE
150                 /* GSSAPI DCE_STYLE is critical when requested, so
151                  * fail outright */
152                 return NT_STATUS_INVALID_PARAMETER;
153 #else
154                 gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE;
155 #endif
156         }
157
158         gensec_gssapi_state->gss_oid = gss_mech_krb5;
159         
160         ret = smb_krb5_init_context(gensec_gssapi_state, 
161                                     &gensec_gssapi_state->smb_krb5_context);
162         if (ret) {
163                 DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",                                  
164                          error_message(ret)));
165                 return NT_STATUS_INTERNAL_ERROR;
166         }
167         return NT_STATUS_OK;
168 }
169
170 static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security)
171 {
172         NTSTATUS nt_status;
173         OM_uint32 maj_stat, min_stat;
174         struct gensec_gssapi_state *gensec_gssapi_state;
175         struct cli_credentials *machine_account;
176
177         nt_status = gensec_gssapi_start(gensec_security);
178         if (!NT_STATUS_IS_OK(nt_status)) {
179                 return nt_status;
180         }
181
182         gensec_gssapi_state = gensec_security->private_data;
183
184         machine_account = cli_credentials_init(gensec_gssapi_state);
185         cli_credentials_set_conf(machine_account);
186         nt_status = cli_credentials_set_machine_account(machine_account);
187         
188         if (!NT_STATUS_IS_OK(nt_status)) {
189                 DEBUG(3, ("Could not obtain machine account credentials from the local database\n"));
190                 talloc_free(machine_account);
191                 return nt_status;
192         } else {
193                 nt_status = create_memory_keytab(gensec_gssapi_state,
194                                                  machine_account, 
195                                                  gensec_gssapi_state->smb_krb5_context,
196                                                  &gensec_gssapi_state->keytab);
197                 talloc_free(machine_account);
198                 if (!NT_STATUS_IS_OK(nt_status)) {
199                         DEBUG(3, ("Could not create memory keytab!\n"));
200                         talloc_free(machine_account);
201                         return nt_status;
202                 }
203         }
204
205         maj_stat = gsskrb5_acquire_cred(&min_stat, 
206                                         gensec_gssapi_state->keytab, NULL,
207                                         NULL,
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         NTSTATUS nt_status;
228         gss_buffer_desc name_token;
229         OM_uint32 maj_stat, min_stat;
230
231         nt_status = gensec_gssapi_start(gensec_security);
232         if (!NT_STATUS_IS_OK(nt_status)) {
233                 return nt_status;
234         }
235
236         gensec_gssapi_state = gensec_security->private_data;
237
238         name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", 
239                                            gensec_get_target_service(gensec_security), 
240                                            gensec_get_target_hostname(gensec_security));
241         name_token.length = strlen(name_token.value);
242
243         maj_stat = gss_import_name (&min_stat,
244                                     &name_token,
245                                     GSS_C_NT_HOSTBASED_SERVICE,
246                                     &gensec_gssapi_state->server_name);
247         if (maj_stat) {
248                 DEBUG(1, ("GSS Import name of %s failed: %s\n",
249                           (char *)name_token.value,
250                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
251                 return NT_STATUS_UNSUCCESSFUL;
252         }
253
254         name_token.value = cli_credentials_get_principal(gensec_get_credentials(gensec_security), 
255                                                          gensec_gssapi_state),
256         name_token.length = strlen(name_token.value);
257
258         maj_stat = gss_import_name (&min_stat,
259                                     &name_token,
260                                     GSS_C_NT_USER_NAME,
261                                     &gensec_gssapi_state->client_name);
262         if (maj_stat) {
263                 DEBUG(1, ("GSS Import name of %s failed: %s\n",
264                           (char *)name_token.value,
265                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
266                 return NT_STATUS_UNSUCCESSFUL;
267         }
268
269         nt_status = kinit_to_ccache(gensec_gssapi_state, 
270                                     gensec_get_credentials(gensec_security),
271                                     gensec_gssapi_state->smb_krb5_context, 
272                                     &gensec_gssapi_state->ccache, &gensec_gssapi_state->ccache_name);
273         if (!NT_STATUS_IS_OK(nt_status)) {
274                 return nt_status;
275         }
276
277         maj_stat = gsskrb5_acquire_cred(&min_stat, 
278                                         NULL, gensec_gssapi_state->ccache,
279                                         gensec_gssapi_state->client_name,
280                                         GSS_C_INDEFINITE,
281                                         GSS_C_NULL_OID_SET,
282                                         GSS_C_INITIATE,
283                                         &gensec_gssapi_state->cred,
284                                         NULL, 
285                                         NULL);
286         if (maj_stat) {
287                 DEBUG(1, ("Aquiring initiator credentails failed: %s\n", 
288                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
289                 return NT_STATUS_UNSUCCESSFUL;
290         }
291
292         return NT_STATUS_OK;
293 }
294
295
296 /**
297  * Check if the packet is one for this mechansim
298  * 
299  * @param gensec_security GENSEC state
300  * @param in The request, as a DATA_BLOB
301  * @return Error, INVALID_PARAMETER if it's not a packet for us
302  *                or NT_STATUS_OK if the packet is ok. 
303  */
304
305 static NTSTATUS gensec_gssapi_magic(struct gensec_security *gensec_security, 
306                                     const DATA_BLOB *in) 
307 {
308         if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
309                 return NT_STATUS_OK;
310         } else {
311                 return NT_STATUS_INVALID_PARAMETER;
312         }
313 }
314
315
316 /**
317  * Next state function for the GSSAPI GENSEC mechanism
318  * 
319  * @param gensec_gssapi_state GSSAPI State
320  * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
321  * @param in The request, as a DATA_BLOB
322  * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
323  * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
324  *                or NT_STATUS_OK if the user is authenticated. 
325  */
326
327 static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
328                                    TALLOC_CTX *out_mem_ctx, 
329                                    const DATA_BLOB in, DATA_BLOB *out) 
330 {
331         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
332         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
333         OM_uint32 maj_stat, min_stat;
334         OM_uint32 min_stat2;
335         gss_buffer_desc input_token, output_token;
336         gss_OID gss_oid_p;
337         input_token.length = in.length;
338         input_token.value = in.data;
339
340         switch (gensec_security->gensec_role) {
341         case GENSEC_CLIENT:
342         {
343                 maj_stat = gss_init_sec_context(&min_stat, 
344                                                 gensec_gssapi_state->cred,
345                                                 &gensec_gssapi_state->gssapi_context, 
346                                                 gensec_gssapi_state->server_name, 
347                                                 discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid),
348                                                 gensec_gssapi_state->want_flags, 
349                                                 0, 
350                                                 gensec_gssapi_state->input_chan_bindings,
351                                                 &input_token, 
352                                                 NULL, 
353                                                 &output_token, 
354                                                 &gensec_gssapi_state->got_flags, /* ret flags */
355                                                 NULL);
356                 break;
357         }
358         case GENSEC_SERVER:
359         {
360                 maj_stat = gss_accept_sec_context(&min_stat, 
361                                                   &gensec_gssapi_state->gssapi_context, 
362                                                   gensec_gssapi_state->cred,
363                                                   &input_token, 
364                                                   gensec_gssapi_state->input_chan_bindings,
365                                                   &gensec_gssapi_state->client_name, 
366                                                   &gss_oid_p,
367                                                   &output_token, 
368                                                   &gensec_gssapi_state->got_flags, 
369                                                   NULL, 
370                                                   NULL);
371                 gensec_gssapi_state->gss_oid = gss_oid_p;
372                 break;
373         }
374         default:
375                 return NT_STATUS_INVALID_PARAMETER;
376                 
377         }
378
379         *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
380         gss_release_buffer(&min_stat2, &output_token);
381
382         if (maj_stat == GSS_S_COMPLETE) {
383                 return NT_STATUS_OK;
384         } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
385                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
386         } else {
387                 if (maj_stat == GSS_S_FAILURE
388                     && (min_stat == KRB5KRB_AP_ERR_BADVERSION || min_stat == KRB5KRB_AP_ERR_MSG_TYPE)) {
389                         /* garbage input, possibly from the auto-mech detection */
390                         return NT_STATUS_INVALID_PARAMETER;
391                 }
392                 DEBUG(1, ("GSS Update failed: %s\n", 
393                           gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
394                 return nt_status;
395         }
396 }
397
398 static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
399                                    TALLOC_CTX *mem_ctx, 
400                                    const DATA_BLOB *in, 
401                                    DATA_BLOB *out)
402 {
403         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
404         OM_uint32 maj_stat, min_stat;
405         gss_buffer_desc input_token, output_token;
406         int conf_state;
407         input_token.length = in->length;
408         input_token.value = in->data;
409         
410         maj_stat = gss_wrap(&min_stat, 
411                             gensec_gssapi_state->gssapi_context, 
412                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
413                             GSS_C_QOP_DEFAULT,
414                             &input_token,
415                             &conf_state,
416                             &output_token);
417         if (GSS_ERROR(maj_stat)) {
418                 DEBUG(1, ("GSS Wrap failed: %s\n", 
419                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
420                 return NT_STATUS_ACCESS_DENIED;
421         }
422         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
423
424         gss_release_buffer(&min_stat, &output_token);
425
426         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
427             && !conf_state) {
428                 return NT_STATUS_ACCESS_DENIED;
429         }
430         return NT_STATUS_OK;
431 }
432
433 static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, 
434                                      TALLOC_CTX *mem_ctx, 
435                                      const DATA_BLOB *in, 
436                                      DATA_BLOB *out)
437 {
438         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
439         OM_uint32 maj_stat, min_stat;
440         gss_buffer_desc input_token, output_token;
441         int conf_state;
442         gss_qop_t qop_state;
443         input_token.length = in->length;
444         input_token.value = in->data;
445         
446         maj_stat = gss_unwrap(&min_stat, 
447                               gensec_gssapi_state->gssapi_context, 
448                               &input_token,
449                               &output_token, 
450                               &conf_state,
451                               &qop_state);
452         if (GSS_ERROR(maj_stat)) {
453                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
454                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
455                 return NT_STATUS_ACCESS_DENIED;
456         }
457         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
458
459         gss_release_buffer(&min_stat, &output_token);
460         
461         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
462             && !conf_state) {
463                 return NT_STATUS_ACCESS_DENIED;
464         }
465         return NT_STATUS_OK;
466 }
467
468 static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security) 
469 {
470         /* not const but work for DCERPC packets and arcfour */
471         return 45;
472 }
473
474 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
475                                           TALLOC_CTX *mem_ctx, 
476                                           uint8_t *data, size_t length, 
477                                           const uint8_t *whole_pdu, size_t pdu_length, 
478                                           DATA_BLOB *sig)
479 {
480         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
481         OM_uint32 maj_stat, min_stat;
482         gss_buffer_desc input_token, output_token;
483         int conf_state;
484         ssize_t sig_length = 0;
485
486         input_token.length = length;
487         input_token.value = data;
488         
489         maj_stat = gss_wrap(&min_stat, 
490                             gensec_gssapi_state->gssapi_context,
491                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
492                             GSS_C_QOP_DEFAULT,
493                             &input_token,
494                             &conf_state,
495                             &output_token);
496         if (GSS_ERROR(maj_stat)) {
497                 DEBUG(1, ("GSS Wrap failed: %s\n", 
498                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
499                 return NT_STATUS_ACCESS_DENIED;
500         }
501
502         if (output_token.length < length) {
503                 return NT_STATUS_INTERNAL_ERROR;
504         }
505
506         sig_length = 45;
507
508         memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
509         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
510
511         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
512         dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
513         dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
514
515         gss_release_buffer(&min_stat, &output_token);
516
517         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
518             && !conf_state) {
519                 return NT_STATUS_ACCESS_DENIED;
520         }
521         return NT_STATUS_OK;
522 }
523
524 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
525                                             TALLOC_CTX *mem_ctx, 
526                                             uint8_t *data, size_t length, 
527                                             const uint8_t *whole_pdu, size_t pdu_length,
528                                             const DATA_BLOB *sig)
529 {
530         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
531         OM_uint32 maj_stat, min_stat;
532         gss_buffer_desc input_token, output_token;
533         int conf_state;
534         gss_qop_t qop_state;
535         DATA_BLOB in;
536
537         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
538
539         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
540
541         memcpy(in.data, sig->data, sig->length);
542         memcpy(in.data + sig->length, data, length);
543
544         input_token.length = in.length;
545         input_token.value = in.data;
546         
547         maj_stat = gss_unwrap(&min_stat, 
548                               gensec_gssapi_state->gssapi_context, 
549                               &input_token,
550                               &output_token, 
551                               &conf_state,
552                               &qop_state);
553         if (GSS_ERROR(maj_stat)) {
554                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
555                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
556                 return NT_STATUS_ACCESS_DENIED;
557         }
558
559         if (output_token.length != length) {
560                 return NT_STATUS_INTERNAL_ERROR;
561         }
562
563         memcpy(data, output_token.value, length);
564
565         gss_release_buffer(&min_stat, &output_token);
566         
567         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
568             && !conf_state) {
569                 return NT_STATUS_ACCESS_DENIED;
570         }
571         return NT_STATUS_OK;
572 }
573
574 static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, 
575                                           TALLOC_CTX *mem_ctx, 
576                                           const uint8_t *data, size_t length, 
577                                           const uint8_t *whole_pdu, size_t pdu_length, 
578                                           DATA_BLOB *sig)
579 {
580         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
581         OM_uint32 maj_stat, min_stat;
582         gss_buffer_desc input_token, output_token;
583         int conf_state;
584         ssize_t sig_length = 0;
585
586         input_token.length = length;
587         input_token.value = discard_const_p(uint8_t *, data);
588
589         maj_stat = gss_wrap(&min_stat, 
590                             gensec_gssapi_state->gssapi_context,
591                             0,
592                             GSS_C_QOP_DEFAULT,
593                             &input_token,
594                             &conf_state,
595                             &output_token);
596         if (GSS_ERROR(maj_stat)) {
597                 DEBUG(1, ("GSS Wrap failed: %s\n", 
598                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
599                 return NT_STATUS_ACCESS_DENIED;
600         }
601
602         if (output_token.length < length) {
603                 return NT_STATUS_INTERNAL_ERROR;
604         }
605
606         sig_length = 45;
607
608         /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
609         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
610
611         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
612
613         gss_release_buffer(&min_stat, &output_token);
614
615         return NT_STATUS_OK;
616 }
617
618 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
619                                            TALLOC_CTX *mem_ctx, 
620                                            const uint8_t *data, size_t length, 
621                                            const uint8_t *whole_pdu, size_t pdu_length, 
622                                            const DATA_BLOB *sig)
623 {
624         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
625         OM_uint32 maj_stat, min_stat;
626         gss_buffer_desc input_token, output_token;
627         int conf_state;
628         gss_qop_t qop_state;
629         DATA_BLOB in;
630
631         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
632
633         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
634
635         memcpy(in.data, sig->data, sig->length);
636         memcpy(in.data + sig->length, data, length);
637
638         input_token.length = in.length;
639         input_token.value = in.data;
640         
641         maj_stat = gss_unwrap(&min_stat, 
642                               gensec_gssapi_state->gssapi_context, 
643                               &input_token,
644                               &output_token, 
645                               &conf_state,
646                               &qop_state);
647         if (GSS_ERROR(maj_stat)) {
648                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
649                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
650                 return NT_STATUS_ACCESS_DENIED;
651         }
652
653         if (output_token.length != length) {
654                 return NT_STATUS_INTERNAL_ERROR;
655         }
656
657         gss_release_buffer(&min_stat, &output_token);
658
659         return NT_STATUS_OK;
660 }
661
662 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
663                                        uint32_t feature) 
664 {
665         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
666         if (feature & GENSEC_FEATURE_SIGN) {
667                 return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
668         }
669         if (feature & GENSEC_FEATURE_SEAL) {
670                 return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
671         }
672         if (feature & GENSEC_FEATURE_SESSION_KEY) {
673 #ifdef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
674                 if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
675                     && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, gensec_gssapi_state->gss_oid->length) == 0)) {
676                         return True;
677                 }
678 #endif 
679         }
680         return False;
681 }
682
683 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
684                                           DATA_BLOB *session_key) 
685 {
686         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
687         
688         if (gensec_gssapi_state->session_key.data) {
689                 *session_key = gensec_gssapi_state->session_key;
690                 return NT_STATUS_OK;
691         }
692
693 #ifdef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
694         /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
695         if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
696             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
697                        gensec_gssapi_state->gss_oid->length) == 0)) {
698                 OM_uint32 maj_stat, min_stat;
699                 gss_buffer_desc skey;
700                 
701                 maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
702                                                         gensec_gssapi_state->gssapi_context, 
703                                                         &skey);
704                 
705                 if (maj_stat == 0) {
706                         DEBUG(10, ("Got KRB5 session key of length %d\n",  skey.length));
707                         gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
708                                                                             skey.value, skey.length);
709                         *session_key = gensec_gssapi_state->session_key;
710                         dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
711                         
712                         gss_release_buffer(&min_stat, &skey);
713                         return NT_STATUS_OK;
714                 }
715                 return NT_STATUS_NO_USER_SESSION_KEY;
716         }
717 #endif
718         
719         DEBUG(1, ("NO session key for this mech\n"));
720         return NT_STATUS_NO_USER_SESSION_KEY;
721 }
722
723 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
724                                          struct auth_session_info **_session_info) 
725 {
726         NTSTATUS nt_status;
727         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
728         struct auth_serversupplied_info *server_info = NULL;
729         struct auth_session_info *session_info = NULL;
730         char *p;
731         char *principal;
732         const char *account_name;
733         const char *realm;
734         OM_uint32 maj_stat, min_stat;
735         gss_buffer_desc name_token;
736         
737         maj_stat = gss_display_name (&min_stat,
738                                      gensec_gssapi_state->client_name,
739                                      &name_token,
740                                      NULL);
741         if (maj_stat) {
742                 return NT_STATUS_FOOBAR;
743         }
744
745         principal = talloc_strndup(gensec_gssapi_state, name_token.value, name_token.length);
746
747         gss_release_buffer(&min_stat, &name_token);
748
749         NT_STATUS_HAVE_NO_MEMORY(principal);
750
751         p = strchr(principal, '@');
752         if (p) {
753                 *p = '\0';
754                 p++;
755                 realm = p;
756         } else {
757                 realm = lp_realm();
758         }
759         account_name = principal;
760
761         /* IF we have the PAC - otherwise we need to get this
762          * data from elsewere - local ldb, or (TODO) lookup of some
763          * kind... 
764          *
765          * when heimdal can generate the PAC, we should fail if there's
766          * no PAC present
767          */
768
769         {
770                 DATA_BLOB user_sess_key = data_blob(NULL, 0);
771                 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
772                 /* TODO: should we pass the krb5 session key in here? */
773                 nt_status = sam_get_server_info(gensec_gssapi_state, account_name, realm,
774                                                 user_sess_key, lm_sess_key,
775                                                 &server_info);
776                 talloc_free(principal);
777                 NT_STATUS_NOT_OK_RETURN(nt_status);
778         }
779
780         /* references the server_info into the session_info */
781         nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
782         talloc_free(server_info);
783         NT_STATUS_NOT_OK_RETURN(nt_status);
784
785         nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
786         NT_STATUS_NOT_OK_RETURN(nt_status);
787
788         *_session_info = session_info;
789
790         return NT_STATUS_OK;
791 }
792
793 static const char *gensec_krb5_oids[] = { 
794         GENSEC_OID_KERBEROS5,
795         GENSEC_OID_KERBEROS5_OLD,
796         NULL 
797 };
798
799 /* As a server, this could in theory accept any GSSAPI mech */
800 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
801         .name           = "gssapi_krb5",
802         .oid            = gensec_krb5_oids,
803         .client_start   = gensec_gssapi_client_start,
804         .server_start   = gensec_gssapi_server_start,
805         .magic          = gensec_gssapi_magic,
806         .update         = gensec_gssapi_update,
807         .session_key    = gensec_gssapi_session_key,
808         .session_info   = gensec_gssapi_session_info,
809         .sig_size       = gensec_gssapi_sig_size,
810         .sign_packet    = gensec_gssapi_sign_packet,
811         .check_packet   = gensec_gssapi_check_packet,
812         .seal_packet    = gensec_gssapi_seal_packet,
813         .unseal_packet  = gensec_gssapi_unseal_packet,
814         .wrap           = gensec_gssapi_wrap,
815         .unwrap         = gensec_gssapi_unwrap,
816         .have_feature   = gensec_gssapi_have_feature,
817         .enabled        = False
818 };
819
820 NTSTATUS gensec_gssapi_init(void)
821 {
822         NTSTATUS ret;
823
824         ret = gensec_register(&gensec_gssapi_krb5_security_ops);
825         if (!NT_STATUS_IS_OK(ret)) {
826                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
827                         gensec_gssapi_krb5_security_ops.name));
828                 return ret;
829         }
830
831         return ret;
832 }