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