r10153: This patch adds a new parameter to gensec_sig_size(), the size of the
[gd/samba-autobuild/.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, size_t data_size) 
484 {
485         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
486         OM_uint32 maj_stat, min_stat;
487         OM_uint32 output_size;
488         if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
489             || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
490                        gensec_gssapi_state->gss_oid->length) != 0)) {
491                 DEBUG(1, ("NO sig size available for this mech\n"));
492                 return 0;
493         }
494                 
495         maj_stat = gsskrb5_wrap_size(&min_stat, 
496                                      gensec_gssapi_state->gssapi_context,
497                                      gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
498                                      GSS_C_QOP_DEFAULT,
499                                      data_size, 
500                                      &output_size);
501         if (GSS_ERROR(maj_stat)) {
502                 TALLOC_CTX *mem_ctx = talloc_new(NULL); 
503                 DEBUG(1, ("gensec_gssapi_seal_packet: determinaing signature size with gss_wrap_size_limit failed: %s\n", 
504                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
505                 talloc_free(mem_ctx);
506                 return 0;
507         }
508
509         if (output_size < data_size) {
510                 return 0;
511         }
512
513         /* The difference between the max output and the max input must be the signature */
514         return output_size - data_size;
515 }
516
517 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
518                                           TALLOC_CTX *mem_ctx, 
519                                           uint8_t *data, size_t length, 
520                                           const uint8_t *whole_pdu, size_t pdu_length, 
521                                           DATA_BLOB *sig)
522 {
523         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
524         OM_uint32 maj_stat, min_stat;
525         gss_buffer_desc input_token, output_token;
526         int conf_state;
527         ssize_t sig_length;
528
529         input_token.length = length;
530         input_token.value = data;
531         
532         maj_stat = gss_wrap(&min_stat, 
533                             gensec_gssapi_state->gssapi_context,
534                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
535                             GSS_C_QOP_DEFAULT,
536                             &input_token,
537                             &conf_state,
538                             &output_token);
539         if (GSS_ERROR(maj_stat)) {
540                 DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap failed: %s\n", 
541                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
542                 return NT_STATUS_ACCESS_DENIED;
543         }
544
545         sig_length = gensec_gssapi_sig_size(gensec_security, length);
546
547         /* Caller must pad to right boundary */
548         if (output_token.length != (length + sig_length)) {
549                 DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap length [%d] does not match caller length [%d] plus sig size [%d] = [%d]\n", 
550                           output_token.length, length, sig_length, length + sig_length));
551                 return NT_STATUS_INTERNAL_ERROR;
552         }
553
554         memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
555         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
556
557         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
558         dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
559         dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
560
561         gss_release_buffer(&min_stat, &output_token);
562
563         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
564             && !conf_state) {
565                 return NT_STATUS_ACCESS_DENIED;
566         }
567         return NT_STATUS_OK;
568 }
569
570 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
571                                             TALLOC_CTX *mem_ctx, 
572                                             uint8_t *data, size_t length, 
573                                             const uint8_t *whole_pdu, size_t pdu_length,
574                                             const DATA_BLOB *sig)
575 {
576         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
577         OM_uint32 maj_stat, min_stat;
578         gss_buffer_desc input_token, output_token;
579         int conf_state;
580         gss_qop_t qop_state;
581         DATA_BLOB in;
582
583         dump_data_pw("gensec_gssapi_unseal_packet: sig\n", sig->data, sig->length);
584
585         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
586
587         memcpy(in.data, sig->data, sig->length);
588         memcpy(in.data + sig->length, data, length);
589
590         input_token.length = in.length;
591         input_token.value = in.data;
592         
593         maj_stat = gss_unwrap(&min_stat, 
594                               gensec_gssapi_state->gssapi_context, 
595                               &input_token,
596                               &output_token, 
597                               &conf_state,
598                               &qop_state);
599         if (GSS_ERROR(maj_stat)) {
600                 DEBUG(1, ("gensec_gssapi_unseal_packet: GSS UnWrap failed: %s\n", 
601                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
602                 return NT_STATUS_ACCESS_DENIED;
603         }
604
605         if (output_token.length != length) {
606                 return NT_STATUS_INTERNAL_ERROR;
607         }
608
609         memcpy(data, output_token.value, length);
610
611         gss_release_buffer(&min_stat, &output_token);
612         
613         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
614             && !conf_state) {
615                 return NT_STATUS_ACCESS_DENIED;
616         }
617         return NT_STATUS_OK;
618 }
619
620 static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, 
621                                           TALLOC_CTX *mem_ctx, 
622                                           const uint8_t *data, size_t length, 
623                                           const uint8_t *whole_pdu, size_t pdu_length, 
624                                           DATA_BLOB *sig)
625 {
626         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
627         OM_uint32 maj_stat, min_stat;
628         gss_buffer_desc input_token, output_token;
629         int conf_state;
630         ssize_t sig_length = 0;
631
632         input_token.length = length;
633         input_token.value = discard_const_p(uint8_t *, data);
634
635         maj_stat = gss_wrap(&min_stat, 
636                             gensec_gssapi_state->gssapi_context,
637                             0,
638                             GSS_C_QOP_DEFAULT,
639                             &input_token,
640                             &conf_state,
641                             &output_token);
642         if (GSS_ERROR(maj_stat)) {
643                 DEBUG(1, ("GSS Wrap failed: %s\n", 
644                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
645                 return NT_STATUS_ACCESS_DENIED;
646         }
647
648         if (output_token.length < length) {
649                 return NT_STATUS_INTERNAL_ERROR;
650         }
651
652         sig_length = gensec_gssapi_sig_size(gensec_security, length);
653
654         /* Caller must pad to right boundary */
655         if (output_token.length != (length + sig_length)) {
656                 DEBUG(1, ("gensec_gssapi_sign_packet: GSS Wrap length [%d] does not match caller length [%d] plus sig size [%d] = [%d]\n", 
657                           output_token.length, length, sig_length, length + sig_length));
658                 return NT_STATUS_INTERNAL_ERROR;
659         }
660
661         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
662
663         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
664
665         gss_release_buffer(&min_stat, &output_token);
666
667         return NT_STATUS_OK;
668 }
669
670 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
671                                            TALLOC_CTX *mem_ctx, 
672                                            const uint8_t *data, size_t length, 
673                                            const uint8_t *whole_pdu, size_t pdu_length, 
674                                            const DATA_BLOB *sig)
675 {
676         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
677         OM_uint32 maj_stat, min_stat;
678         gss_buffer_desc input_token, output_token;
679         int conf_state;
680         gss_qop_t qop_state;
681         DATA_BLOB in;
682
683         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
684
685         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
686
687         memcpy(in.data, sig->data, sig->length);
688         memcpy(in.data + sig->length, data, length);
689
690         input_token.length = in.length;
691         input_token.value = in.data;
692         
693         maj_stat = gss_unwrap(&min_stat, 
694                               gensec_gssapi_state->gssapi_context, 
695                               &input_token,
696                               &output_token, 
697                               &conf_state,
698                               &qop_state);
699         if (GSS_ERROR(maj_stat)) {
700                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
701                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
702                 return NT_STATUS_ACCESS_DENIED;
703         }
704
705         if (output_token.length != length) {
706                 return NT_STATUS_INTERNAL_ERROR;
707         }
708
709         gss_release_buffer(&min_stat, &output_token);
710
711         return NT_STATUS_OK;
712 }
713
714 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
715                                        uint32_t feature) 
716 {
717         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
718         if (feature & GENSEC_FEATURE_SIGN) {
719                 return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
720         }
721         if (feature & GENSEC_FEATURE_SEAL) {
722                 return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
723         }
724         if (feature & GENSEC_FEATURE_SESSION_KEY) {
725                 if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
726                     && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, gensec_gssapi_state->gss_oid->length) == 0)) {
727                         return True;
728                 }
729         }
730         if (feature & GENSEC_FEATURE_DCE_STYLE) {
731                 return gensec_gssapi_state->got_flags & GSS_C_DCE_STYLE;
732         }
733         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
734                 return True;
735         }
736         return False;
737 }
738
739 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
740                                           DATA_BLOB *session_key) 
741 {
742         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
743         
744         if (gensec_gssapi_state->session_key.data) {
745                 *session_key = gensec_gssapi_state->session_key;
746                 return NT_STATUS_OK;
747         }
748
749         /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
750         if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
751             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
752                        gensec_gssapi_state->gss_oid->length) == 0)) {
753                 OM_uint32 maj_stat, min_stat;
754                 gss_buffer_desc skey;
755                 
756                 maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
757                                                         gensec_gssapi_state->gssapi_context, 
758                                                         &skey);
759                 
760                 if (maj_stat == 0) {
761                         DEBUG(10, ("Got KRB5 session key of length %d\n",  
762                                    (int)skey.length));
763                         gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
764                                                                             skey.value, skey.length);
765                         *session_key = gensec_gssapi_state->session_key;
766                         dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
767                         
768                         gss_release_buffer(&min_stat, &skey);
769                         return NT_STATUS_OK;
770                 }
771                 return NT_STATUS_NO_USER_SESSION_KEY;
772         }
773         
774         DEBUG(1, ("NO session key for this mech\n"));
775         return NT_STATUS_NO_USER_SESSION_KEY;
776 }
777
778 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
779                                          struct auth_session_info **_session_info) 
780 {
781         NTSTATUS nt_status;
782         TALLOC_CTX *mem_ctx;
783         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
784         struct auth_serversupplied_info *server_info = NULL;
785         struct auth_session_info *session_info = NULL;
786         struct PAC_LOGON_INFO *logon_info;
787         OM_uint32 maj_stat, min_stat;
788         gss_buffer_desc name_token;
789         gss_buffer_desc pac;
790         krb5_keyblock *keyblock;
791         time_t authtime;
792         krb5_principal principal;
793         char *principal_string;
794         
795         if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
796             || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
797                        gensec_gssapi_state->gss_oid->length) != 0)) {
798                 DEBUG(1, ("NO session info available for this mech\n"));
799                 return NT_STATUS_INVALID_PARAMETER;
800         }
801                 
802         mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); 
803         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
804
805         maj_stat = gss_display_name (&min_stat,
806                                      gensec_gssapi_state->client_name,
807                                      &name_token,
808                                      NULL);
809         if (maj_stat) {
810                 return NT_STATUS_FOOBAR;
811         }
812
813         principal_string = talloc_strndup(mem_ctx, name_token.value, name_token.length);
814
815         gss_release_buffer(&min_stat, &name_token);
816
817         if (!principal_string) {
818                 talloc_free(mem_ctx);
819                 return NT_STATUS_NO_MEMORY;
820         }
821
822         maj_stat = gss_krb5_copy_service_keyblock(&min_stat, 
823                                                   gensec_gssapi_state->gssapi_context, 
824                                                   &keyblock);
825
826         if (maj_stat == 0) {
827                 maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
828                                                                      gensec_gssapi_state->gssapi_context, 
829                                                                      &authtime);
830         }
831
832         if (maj_stat == 0) {
833                 maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, 
834                                                                        gensec_gssapi_state->gssapi_context, 
835                                                                        KRB5_AUTHDATA_IF_RELEVANT,
836                                                                        &pac);
837         }
838         
839         if (maj_stat == 0) {
840                 krb5_error_code ret;
841                 DATA_BLOB pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
842                 pac_blob = unwrap_pac(mem_ctx, &pac_blob);
843                 gss_release_buffer(&min_stat, &pac);
844
845                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
846                                       principal_string, &principal);
847                 if (ret) {
848                         talloc_free(mem_ctx);
849                         return NT_STATUS_INVALID_PARAMETER;
850                 }
851                 
852                 /* decode and verify the pac */
853                 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob,
854                                                     gensec_gssapi_state->smb_krb5_context->krb5_context,
855                                                     NULL, keyblock, principal, authtime);
856                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
857
858                 if (NT_STATUS_IS_OK(nt_status)) {
859                         union netr_Validation validation;
860                         validation.sam3 = &logon_info->info3;
861                         nt_status = make_server_info_netlogon_validation(gensec_gssapi_state, 
862                                                                          NULL,
863                                                                          3, &validation,
864                                                                          &server_info); 
865                         if (!NT_STATUS_IS_OK(nt_status)) {
866                                 talloc_free(mem_ctx);
867                                 return nt_status;
868                         }
869                 } else {
870                         maj_stat = 1;
871                 }
872         }
873         
874         if (maj_stat) {
875                 krb5_error_code ret;
876                 DATA_BLOB user_sess_key = data_blob(NULL, 0);
877                 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
878                 /* IF we have the PAC - otherwise we need to get this
879                  * data from elsewere - local ldb, or (TODO) lookup of some
880                  * kind... 
881                  *
882                  * when heimdal can generate the PAC, we should fail if there's
883                  * no PAC present
884                  */
885
886                 char *account_name;
887                 const char *realm;
888                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
889                                       principal_string, &principal);
890                 if (ret) {
891                         talloc_free(mem_ctx);
892                         return NT_STATUS_INVALID_PARAMETER;
893                 }
894                 
895                 realm = krb5_principal_get_realm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
896                                                  principal);
897                 ret = krb5_unparse_name_norealm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
898                                                 principal, &account_name);
899                 if (ret) {
900                         krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
901                         talloc_free(mem_ctx);
902                         return NT_STATUS_NO_MEMORY;
903                 }
904
905                 DEBUG(1, ("Unable to use PAC, resorting to local user lookup!\n"));
906                 nt_status = sam_get_server_info(mem_ctx, account_name, realm,
907                                                 user_sess_key, lm_sess_key,
908                                                 &server_info);
909                 free(account_name);
910                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
911
912                 if (!NT_STATUS_IS_OK(nt_status)) {
913                         talloc_free(mem_ctx);
914                         return nt_status;
915                 }
916         }
917
918         /* references the server_info into the session_info */
919         nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
920         talloc_free(mem_ctx);
921         talloc_free(server_info);
922         NT_STATUS_NOT_OK_RETURN(nt_status);
923
924         nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
925         NT_STATUS_NOT_OK_RETURN(nt_status);
926
927         *_session_info = session_info;
928
929         return NT_STATUS_OK;
930 }
931
932 static const char *gensec_krb5_oids[] = { 
933         GENSEC_OID_KERBEROS5,
934         GENSEC_OID_KERBEROS5_OLD,
935         NULL 
936 };
937
938 /* As a server, this could in theory accept any GSSAPI mech */
939 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
940         .name           = "gssapi_krb5",
941         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
942         .oid            = gensec_krb5_oids,
943         .client_start   = gensec_gssapi_client_start,
944         .server_start   = gensec_gssapi_server_start,
945         .magic          = gensec_gssapi_magic,
946         .update         = gensec_gssapi_update,
947         .session_key    = gensec_gssapi_session_key,
948         .session_info   = gensec_gssapi_session_info,
949         .sig_size       = gensec_gssapi_sig_size,
950         .sign_packet    = gensec_gssapi_sign_packet,
951         .check_packet   = gensec_gssapi_check_packet,
952         .seal_packet    = gensec_gssapi_seal_packet,
953         .unseal_packet  = gensec_gssapi_unseal_packet,
954         .wrap           = gensec_gssapi_wrap,
955         .unwrap         = gensec_gssapi_unwrap,
956         .have_feature   = gensec_gssapi_have_feature,
957         .enabled        = False
958 };
959
960 NTSTATUS gensec_gssapi_init(void)
961 {
962         NTSTATUS ret;
963
964         ret = gensec_register(&gensec_gssapi_krb5_security_ops);
965         if (!NT_STATUS_IS_OK(ret)) {
966                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
967                         gensec_gssapi_krb5_security_ops.name));
968                 return ret;
969         }
970
971         return ret;
972 }