6316b52bad764e56a74c961b7990f17684e23514
[tprouty/samba.git] / source / 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 = 0;
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         *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
392         gss_release_buffer(&min_stat2, &output_token);
393
394         if (maj_stat == GSS_S_COMPLETE) {
395                 return NT_STATUS_OK;
396         } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
397                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
398         } else {
399                 if (maj_stat == GSS_S_FAILURE
400                     && (min_stat == KRB5KRB_AP_ERR_BADVERSION || min_stat == KRB5KRB_AP_ERR_MSG_TYPE)) {
401                         /* garbage input, possibly from the auto-mech detection */
402                         return NT_STATUS_INVALID_PARAMETER;
403                 }
404                 DEBUG(1, ("GSS Update failed: %s\n", 
405                           gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
406                 return nt_status;
407         }
408 }
409
410 static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
411                                    TALLOC_CTX *mem_ctx, 
412                                    const DATA_BLOB *in, 
413                                    DATA_BLOB *out)
414 {
415         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
416         OM_uint32 maj_stat, min_stat;
417         gss_buffer_desc input_token, output_token;
418         int conf_state;
419         input_token.length = in->length;
420         input_token.value = in->data;
421         
422         maj_stat = gss_wrap(&min_stat, 
423                             gensec_gssapi_state->gssapi_context, 
424                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
425                             GSS_C_QOP_DEFAULT,
426                             &input_token,
427                             &conf_state,
428                             &output_token);
429         if (GSS_ERROR(maj_stat)) {
430                 DEBUG(1, ("GSS Wrap failed: %s\n", 
431                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
432                 return NT_STATUS_ACCESS_DENIED;
433         }
434         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
435
436         gss_release_buffer(&min_stat, &output_token);
437
438         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
439             && !conf_state) {
440                 return NT_STATUS_ACCESS_DENIED;
441         }
442         return NT_STATUS_OK;
443 }
444
445 static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, 
446                                      TALLOC_CTX *mem_ctx, 
447                                      const DATA_BLOB *in, 
448                                      DATA_BLOB *out)
449 {
450         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
451         OM_uint32 maj_stat, min_stat;
452         gss_buffer_desc input_token, output_token;
453         int conf_state;
454         gss_qop_t qop_state;
455         input_token.length = in->length;
456         input_token.value = in->data;
457         
458         maj_stat = gss_unwrap(&min_stat, 
459                               gensec_gssapi_state->gssapi_context, 
460                               &input_token,
461                               &output_token, 
462                               &conf_state,
463                               &qop_state);
464         if (GSS_ERROR(maj_stat)) {
465                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
466                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
467                 return NT_STATUS_ACCESS_DENIED;
468         }
469         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
470
471         gss_release_buffer(&min_stat, &output_token);
472         
473         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
474             && !conf_state) {
475                 return NT_STATUS_ACCESS_DENIED;
476         }
477         return NT_STATUS_OK;
478 }
479
480 static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security) 
481 {
482         /* not const but work for DCERPC packets and arcfour */
483         return 45;
484 }
485
486 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
487                                           TALLOC_CTX *mem_ctx, 
488                                           uint8_t *data, size_t length, 
489                                           const uint8_t *whole_pdu, size_t pdu_length, 
490                                           DATA_BLOB *sig)
491 {
492         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
493         OM_uint32 maj_stat, min_stat;
494         gss_buffer_desc input_token, output_token;
495         int conf_state;
496         ssize_t sig_length = 0;
497
498         input_token.length = length;
499         input_token.value = data;
500         
501         maj_stat = gss_wrap(&min_stat, 
502                             gensec_gssapi_state->gssapi_context,
503                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
504                             GSS_C_QOP_DEFAULT,
505                             &input_token,
506                             &conf_state,
507                             &output_token);
508         if (GSS_ERROR(maj_stat)) {
509                 DEBUG(1, ("GSS Wrap failed: %s\n", 
510                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
511                 return NT_STATUS_ACCESS_DENIED;
512         }
513
514         if (output_token.length < length) {
515                 return NT_STATUS_INTERNAL_ERROR;
516         }
517
518         sig_length = 45;
519
520         memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
521         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
522
523         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
524         dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
525         dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
526
527         gss_release_buffer(&min_stat, &output_token);
528
529         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
530             && !conf_state) {
531                 return NT_STATUS_ACCESS_DENIED;
532         }
533         return NT_STATUS_OK;
534 }
535
536 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
537                                             TALLOC_CTX *mem_ctx, 
538                                             uint8_t *data, size_t length, 
539                                             const uint8_t *whole_pdu, size_t pdu_length,
540                                             const DATA_BLOB *sig)
541 {
542         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
543         OM_uint32 maj_stat, min_stat;
544         gss_buffer_desc input_token, output_token;
545         int conf_state;
546         gss_qop_t qop_state;
547         DATA_BLOB in;
548
549         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
550
551         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
552
553         memcpy(in.data, sig->data, sig->length);
554         memcpy(in.data + sig->length, data, length);
555
556         input_token.length = in.length;
557         input_token.value = in.data;
558         
559         maj_stat = gss_unwrap(&min_stat, 
560                               gensec_gssapi_state->gssapi_context, 
561                               &input_token,
562                               &output_token, 
563                               &conf_state,
564                               &qop_state);
565         if (GSS_ERROR(maj_stat)) {
566                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
567                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
568                 return NT_STATUS_ACCESS_DENIED;
569         }
570
571         if (output_token.length != length) {
572                 return NT_STATUS_INTERNAL_ERROR;
573         }
574
575         memcpy(data, output_token.value, length);
576
577         gss_release_buffer(&min_stat, &output_token);
578         
579         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
580             && !conf_state) {
581                 return NT_STATUS_ACCESS_DENIED;
582         }
583         return NT_STATUS_OK;
584 }
585
586 static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, 
587                                           TALLOC_CTX *mem_ctx, 
588                                           const uint8_t *data, size_t length, 
589                                           const uint8_t *whole_pdu, size_t pdu_length, 
590                                           DATA_BLOB *sig)
591 {
592         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
593         OM_uint32 maj_stat, min_stat;
594         gss_buffer_desc input_token, output_token;
595         int conf_state;
596         ssize_t sig_length = 0;
597
598         input_token.length = length;
599         input_token.value = discard_const_p(uint8_t *, data);
600
601         maj_stat = gss_wrap(&min_stat, 
602                             gensec_gssapi_state->gssapi_context,
603                             0,
604                             GSS_C_QOP_DEFAULT,
605                             &input_token,
606                             &conf_state,
607                             &output_token);
608         if (GSS_ERROR(maj_stat)) {
609                 DEBUG(1, ("GSS Wrap failed: %s\n", 
610                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
611                 return NT_STATUS_ACCESS_DENIED;
612         }
613
614         if (output_token.length < length) {
615                 return NT_STATUS_INTERNAL_ERROR;
616         }
617
618         sig_length = 45;
619
620         /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
621         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
622
623         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
624
625         gss_release_buffer(&min_stat, &output_token);
626
627         return NT_STATUS_OK;
628 }
629
630 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
631                                            TALLOC_CTX *mem_ctx, 
632                                            const uint8_t *data, size_t length, 
633                                            const uint8_t *whole_pdu, size_t pdu_length, 
634                                            const DATA_BLOB *sig)
635 {
636         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
637         OM_uint32 maj_stat, min_stat;
638         gss_buffer_desc input_token, output_token;
639         int conf_state;
640         gss_qop_t qop_state;
641         DATA_BLOB in;
642
643         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
644
645         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
646
647         memcpy(in.data, sig->data, sig->length);
648         memcpy(in.data + sig->length, data, length);
649
650         input_token.length = in.length;
651         input_token.value = in.data;
652         
653         maj_stat = gss_unwrap(&min_stat, 
654                               gensec_gssapi_state->gssapi_context, 
655                               &input_token,
656                               &output_token, 
657                               &conf_state,
658                               &qop_state);
659         if (GSS_ERROR(maj_stat)) {
660                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
661                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
662                 return NT_STATUS_ACCESS_DENIED;
663         }
664
665         if (output_token.length != length) {
666                 return NT_STATUS_INTERNAL_ERROR;
667         }
668
669         gss_release_buffer(&min_stat, &output_token);
670
671         return NT_STATUS_OK;
672 }
673
674 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
675                                        uint32_t feature) 
676 {
677         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
678         if (feature & GENSEC_FEATURE_SIGN) {
679                 return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
680         }
681         if (feature & GENSEC_FEATURE_SEAL) {
682                 return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
683         }
684         if (feature & GENSEC_FEATURE_SESSION_KEY) {
685                 if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
686                     && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, gensec_gssapi_state->gss_oid->length) == 0)) {
687                         return True;
688                 }
689         }
690         if (feature & GENSEC_FEATURE_DCE_STYLE) {
691                 return True;
692         }
693         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
694                 return True;
695         }
696         return False;
697 }
698
699 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
700                                           DATA_BLOB *session_key) 
701 {
702         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
703         
704         if (gensec_gssapi_state->session_key.data) {
705                 *session_key = gensec_gssapi_state->session_key;
706                 return NT_STATUS_OK;
707         }
708
709         /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
710         if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
711             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
712                        gensec_gssapi_state->gss_oid->length) == 0)) {
713                 OM_uint32 maj_stat, min_stat;
714                 gss_buffer_desc skey;
715                 
716                 maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
717                                                         gensec_gssapi_state->gssapi_context, 
718                                                         &skey);
719                 
720                 if (maj_stat == 0) {
721                         DEBUG(10, ("Got KRB5 session key of length %d\n",  
722                                    (int)skey.length));
723                         gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
724                                                                             skey.value, skey.length);
725                         *session_key = gensec_gssapi_state->session_key;
726                         dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
727                         
728                         gss_release_buffer(&min_stat, &skey);
729                         return NT_STATUS_OK;
730                 }
731                 return NT_STATUS_NO_USER_SESSION_KEY;
732         }
733         
734         DEBUG(1, ("NO session key for this mech\n"));
735         return NT_STATUS_NO_USER_SESSION_KEY;
736 }
737
738 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
739                                          struct auth_session_info **_session_info) 
740 {
741         NTSTATUS nt_status;
742         TALLOC_CTX *mem_ctx;
743         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
744         struct auth_serversupplied_info *server_info = NULL;
745         struct auth_session_info *session_info = NULL;
746         struct PAC_LOGON_INFO *logon_info;
747         char *p;
748         char *principal;
749         const char *account_name;
750         const char *realm;
751         OM_uint32 maj_stat, min_stat;
752         gss_buffer_desc name_token;
753         gss_buffer_desc pac;
754         krb5_keyblock *keyblock;
755         
756         mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); 
757         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
758
759         maj_stat = gss_display_name (&min_stat,
760                                      gensec_gssapi_state->client_name,
761                                      &name_token,
762                                      NULL);
763         if (maj_stat) {
764                 return NT_STATUS_FOOBAR;
765         }
766
767         principal = talloc_strndup(mem_ctx, name_token.value, name_token.length);
768
769         gss_release_buffer(&min_stat, &name_token);
770
771         if (!principal) {
772                 talloc_free(mem_ctx);
773                 return NT_STATUS_NO_MEMORY;
774         }
775
776         p = strchr(principal, '@');
777         if (p) {
778                 *p = '\0';
779                 p++;
780                 realm = p;
781         } else {
782                 realm = lp_realm();
783         }
784         account_name = principal;
785         
786         maj_stat = gss_krb5_copy_service_keyblock(&min_stat, 
787                                                   gensec_gssapi_state->gssapi_context, 
788                                                   &keyblock);
789
790         maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, 
791                                                                gensec_gssapi_state->gssapi_context, 
792                                                                KRB5_AUTHDATA_IF_RELEVANT,
793                                                                &pac);
794         
795         if (maj_stat == 0) {
796                 DATA_BLOB pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
797                 pac_blob = unwrap_pac(mem_ctx, &pac_blob);
798                 gss_release_buffer(&min_stat, &pac);
799                 
800                 /* decode and verify the pac */
801                 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob,
802                                                     gensec_gssapi_state->smb_krb5_context->krb5_context,
803                                                     NULL, keyblock);
804
805                 if (NT_STATUS_IS_OK(nt_status)) {
806                         union netr_Validation validation;
807                         validation.sam3 = &logon_info->info3;
808                         nt_status = make_server_info_netlogon_validation(gensec_gssapi_state, 
809                                                                          account_name,
810                                                                          3, &validation,
811                                                                          &server_info); 
812                         if (!NT_STATUS_IS_OK(nt_status)) {
813                                 talloc_free(mem_ctx);
814                                 return nt_status;
815                         }
816                 } else {
817                         maj_stat = 1;
818                 }
819         }
820         
821         if (maj_stat) {
822                 /* IF we have the PAC - otherwise we need to get this
823                  * data from elsewere - local ldb, or (TODO) lookup of some
824                  * kind... 
825                  *
826                  * when heimdal can generate the PAC, we should fail if there's
827                  * no PAC present
828                  */
829
830                 DATA_BLOB user_sess_key = data_blob(NULL, 0);
831                 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
832                 /* TODO: should we pass the krb5 session key in here? */
833                 nt_status = sam_get_server_info(mem_ctx, account_name, realm,
834                                                 user_sess_key, lm_sess_key,
835                                                 &server_info);
836                 if (!NT_STATUS_IS_OK(nt_status)) {
837                         talloc_free(mem_ctx);
838                         return nt_status;
839                 }
840         }
841
842         /* references the server_info into the session_info */
843         nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
844         talloc_free(server_info);
845         NT_STATUS_NOT_OK_RETURN(nt_status);
846
847         nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
848         NT_STATUS_NOT_OK_RETURN(nt_status);
849
850         *_session_info = session_info;
851
852         return NT_STATUS_OK;
853 }
854
855 static const char *gensec_krb5_oids[] = { 
856         GENSEC_OID_KERBEROS5,
857         GENSEC_OID_KERBEROS5_OLD,
858         NULL 
859 };
860
861 /* As a server, this could in theory accept any GSSAPI mech */
862 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
863         .name           = "gssapi_krb5",
864         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
865         .oid            = gensec_krb5_oids,
866         .client_start   = gensec_gssapi_client_start,
867         .server_start   = gensec_gssapi_server_start,
868         .magic          = gensec_gssapi_magic,
869         .update         = gensec_gssapi_update,
870         .session_key    = gensec_gssapi_session_key,
871         .session_info   = gensec_gssapi_session_info,
872         .sig_size       = gensec_gssapi_sig_size,
873         .sign_packet    = gensec_gssapi_sign_packet,
874         .check_packet   = gensec_gssapi_check_packet,
875         .seal_packet    = gensec_gssapi_seal_packet,
876         .unseal_packet  = gensec_gssapi_unseal_packet,
877         .wrap           = gensec_gssapi_wrap,
878         .unwrap         = gensec_gssapi_unwrap,
879         .have_feature   = gensec_gssapi_have_feature,
880         .enabled        = False
881 };
882
883 NTSTATUS gensec_gssapi_init(void)
884 {
885         NTSTATUS ret;
886
887         ret = gensec_register(&gensec_gssapi_krb5_security_ops);
888         if (!NT_STATUS_IS_OK(ret)) {
889                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
890                         gensec_gssapi_krb5_security_ops.name));
891                 return ret;
892         }
893
894         return ret;
895 }