r10565: Try to make Kerberos authentication a bit more friendly.
[kamenim/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         gss_OID name_type;
233         OM_uint32 maj_stat, min_stat;
234         const char *hostname = gensec_get_target_hostname(gensec_security);
235         const char *principal;
236
237         if (!hostname) {
238                 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
239                 return NT_STATUS_INVALID_PARAMETER;
240         }
241         if (is_ipaddress(hostname)) {
242                 DEBUG(2, ("Cannot do GSSAPI to an IP address\n"));
243                 return NT_STATUS_INVALID_PARAMETER;
244         }
245         if (strequal(hostname, "localhost")) {
246                 DEBUG(2, ("GSSAPI to 'localhost' does not make sense\n"));
247                 return NT_STATUS_INVALID_PARAMETER;
248         }
249
250         nt_status = gensec_gssapi_start(gensec_security);
251         if (!NT_STATUS_IS_OK(nt_status)) {
252                 return nt_status;
253         }
254
255         gensec_gssapi_state = gensec_security->private_data;
256
257         ret = cli_credentials_get_ccache(creds, 
258                                          &ccache);
259         if (ret) {
260                 DEBUG(1, ("Failed to get CCACHE for gensec_gssapi: %s\n", error_message(ret)));
261                 return NT_STATUS_UNSUCCESSFUL;
262         }
263
264         name_token.value = cli_credentials_get_principal(creds, 
265                                                          gensec_gssapi_state);
266         name_token.length = strlen(name_token.value);
267
268         maj_stat = gss_import_name (&min_stat,
269                                     &name_token,
270                                     GSS_C_NT_USER_NAME,
271                                     &gensec_gssapi_state->client_name);
272         if (maj_stat) {
273                 DEBUG(2, ("GSS Import name of %s failed: %s\n",
274                           (char *)name_token.value,
275                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
276                 return NT_STATUS_INVALID_PARAMETER;
277         }
278
279         principal = gensec_get_target_principal(gensec_security);
280         if (principal && lp_client_use_spnego_principal()) {
281                 name_token.value = gensec_get_target_principal(gensec_security);
282                 name_token.length = strlen(name_token.value);
283                 name_type = GSS_C_NULL_OID;
284         } else {
285                 name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", 
286                                                    gensec_get_target_service(gensec_security), 
287                                                    hostname);
288                 name_token.length = strlen(name_token.value);
289                 name_type = GSS_C_NT_HOSTBASED_SERVICE;
290         }               
291
292         maj_stat = gss_import_name (&min_stat,
293                                     &name_token,
294                                     name_type,
295                                     &gensec_gssapi_state->server_name);
296         if (maj_stat) {
297                 DEBUG(2, ("GSS Import name of %s failed: %s\n",
298                           (char *)name_token.value,
299                           gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
300                 return NT_STATUS_INVALID_PARAMETER;
301         }
302
303         maj_stat = gsskrb5_acquire_cred(&min_stat, 
304                                         NULL, ccache->ccache,
305                                         gensec_gssapi_state->client_name,
306                                         GSS_C_INDEFINITE,
307                                         GSS_C_NULL_OID_SET,
308                                         GSS_C_INITIATE,
309                                         &gensec_gssapi_state->cred,
310                                         NULL, 
311                                         NULL);
312         if (maj_stat) {
313                 switch (min_stat) {
314                 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
315                         DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
316                                   hostname, gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
317                         return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
318                 default:
319                         DEBUG(1, ("Aquiring initiator credentails failed: %s\n", 
320                                   gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
321                         return NT_STATUS_UNSUCCESSFUL;
322                 }
323         }
324
325         return NT_STATUS_OK;
326 }
327
328
329 /**
330  * Check if the packet is one for this mechansim
331  * 
332  * @param gensec_security GENSEC state
333  * @param in The request, as a DATA_BLOB
334  * @return Error, INVALID_PARAMETER if it's not a packet for us
335  *                or NT_STATUS_OK if the packet is ok. 
336  */
337
338 static NTSTATUS gensec_gssapi_magic(struct gensec_security *gensec_security, 
339                                     const DATA_BLOB *in) 
340 {
341         if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
342                 return NT_STATUS_OK;
343         } else {
344                 return NT_STATUS_INVALID_PARAMETER;
345         }
346 }
347
348
349 /**
350  * Next state function for the GSSAPI GENSEC mechanism
351  * 
352  * @param gensec_gssapi_state GSSAPI State
353  * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
354  * @param in The request, as a DATA_BLOB
355  * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
356  * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
357  *                or NT_STATUS_OK if the user is authenticated. 
358  */
359
360 static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
361                                    TALLOC_CTX *out_mem_ctx, 
362                                    const DATA_BLOB in, DATA_BLOB *out) 
363 {
364         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
365         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
366         OM_uint32 maj_stat, min_stat;
367         OM_uint32 min_stat2;
368         gss_buffer_desc input_token, output_token;
369         gss_OID gss_oid_p;
370         input_token.length = in.length;
371         input_token.value = in.data;
372
373         switch (gensec_security->gensec_role) {
374         case GENSEC_CLIENT:
375         {
376                 maj_stat = gss_init_sec_context(&min_stat, 
377                                                 gensec_gssapi_state->cred,
378                                                 &gensec_gssapi_state->gssapi_context, 
379                                                 gensec_gssapi_state->server_name, 
380                                                 discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid),
381                                                 gensec_gssapi_state->want_flags, 
382                                                 0, 
383                                                 gensec_gssapi_state->input_chan_bindings,
384                                                 &input_token, 
385                                                 NULL, 
386                                                 &output_token, 
387                                                 &gensec_gssapi_state->got_flags, /* ret flags */
388                                                 NULL);
389                 break;
390         }
391         case GENSEC_SERVER:
392         {
393                 maj_stat = gss_accept_sec_context(&min_stat, 
394                                                   &gensec_gssapi_state->gssapi_context, 
395                                                   gensec_gssapi_state->cred,
396                                                   &input_token, 
397                                                   gensec_gssapi_state->input_chan_bindings,
398                                                   &gensec_gssapi_state->client_name, 
399                                                   &gss_oid_p,
400                                                   &output_token, 
401                                                   &gensec_gssapi_state->got_flags, 
402                                                   NULL, 
403                                                   NULL);
404                 gensec_gssapi_state->gss_oid = gss_oid_p;
405                 break;
406         }
407         default:
408                 return NT_STATUS_INVALID_PARAMETER;
409                 
410         }
411
412         if (maj_stat == GSS_S_COMPLETE) {
413                 *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
414                 gss_release_buffer(&min_stat2, &output_token);
415
416                 return NT_STATUS_OK;
417         } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
418                 *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
419                 gss_release_buffer(&min_stat2, &output_token);
420
421                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
422         } else if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
423             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
424                        gensec_gssapi_state->gss_oid->length) == 0)) {
425                 switch (min_stat) {
426                 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
427                         DEBUG(3, ("Server is not registered with our KDC: %s\n", 
428                                   gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
429                         return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
430                 case KRB5KRB_AP_ERR_MSG_TYPE:
431                         /* garbage input, possibly from the auto-mech detection */
432                         return NT_STATUS_INVALID_PARAMETER;
433                 default:
434                         DEBUG(1, ("GSS(krb5) Update failed: %s\n", 
435                                   gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
436                         return nt_status;
437                 }
438         } else {
439                 DEBUG(1, ("GSS Update failed: %s\n", 
440                           gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
441                 return nt_status;
442         }
443 }
444
445 static NTSTATUS gensec_gssapi_wrap(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         input_token.length = in->length;
455         input_token.value = in->data;
456         
457         maj_stat = gss_wrap(&min_stat, 
458                             gensec_gssapi_state->gssapi_context, 
459                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
460                             GSS_C_QOP_DEFAULT,
461                             &input_token,
462                             &conf_state,
463                             &output_token);
464         if (GSS_ERROR(maj_stat)) {
465                 DEBUG(1, ("gensec_gssapi_wrap: GSS Wrap failed: %s\n", 
466                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
467                 return NT_STATUS_ACCESS_DENIED;
468         }
469
470         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
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 NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, 
481                                      TALLOC_CTX *mem_ctx, 
482                                      const DATA_BLOB *in, 
483                                      DATA_BLOB *out)
484 {
485         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
486         OM_uint32 maj_stat, min_stat;
487         gss_buffer_desc input_token, output_token;
488         int conf_state;
489         gss_qop_t qop_state;
490         input_token.length = in->length;
491         input_token.value = in->data;
492         
493         maj_stat = gss_unwrap(&min_stat, 
494                               gensec_gssapi_state->gssapi_context, 
495                               &input_token,
496                               &output_token, 
497                               &conf_state,
498                               &qop_state);
499         if (GSS_ERROR(maj_stat)) {
500                 DEBUG(1, ("gensec_gssapi_unwrap: GSS UnWrap failed: %s\n", 
501                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
502                 return NT_STATUS_ACCESS_DENIED;
503         }
504
505         *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
506         gss_release_buffer(&min_stat, &output_token);
507         
508         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
509             && !conf_state) {
510                 return NT_STATUS_ACCESS_DENIED;
511         }
512         return NT_STATUS_OK;
513 }
514
515 static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size) 
516 {
517         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
518         OM_uint32 maj_stat, min_stat;
519         OM_uint32 output_size;
520         if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
521             || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
522                        gensec_gssapi_state->gss_oid->length) != 0)) {
523                 DEBUG(1, ("NO sig size available for this mech\n"));
524                 return 0;
525         }
526                 
527         maj_stat = gsskrb5_wrap_size(&min_stat, 
528                                      gensec_gssapi_state->gssapi_context,
529                                      gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
530                                      GSS_C_QOP_DEFAULT,
531                                      data_size, 
532                                      &output_size);
533         if (GSS_ERROR(maj_stat)) {
534                 TALLOC_CTX *mem_ctx = talloc_new(NULL); 
535                 DEBUG(1, ("gensec_gssapi_seal_packet: determinaing signature size with gss_wrap_size_limit failed: %s\n", 
536                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
537                 talloc_free(mem_ctx);
538                 return 0;
539         }
540
541         if (output_size < data_size) {
542                 return 0;
543         }
544
545         /* The difference between the max output and the max input must be the signature */
546         return output_size - data_size;
547 }
548
549 static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security, 
550                                           TALLOC_CTX *mem_ctx, 
551                                           uint8_t *data, size_t length, 
552                                           const uint8_t *whole_pdu, size_t pdu_length, 
553                                           DATA_BLOB *sig)
554 {
555         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
556         OM_uint32 maj_stat, min_stat;
557         gss_buffer_desc input_token, output_token;
558         int conf_state;
559         ssize_t sig_length;
560
561         input_token.length = length;
562         input_token.value = data;
563         
564         maj_stat = gss_wrap(&min_stat, 
565                             gensec_gssapi_state->gssapi_context,
566                             gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
567                             GSS_C_QOP_DEFAULT,
568                             &input_token,
569                             &conf_state,
570                             &output_token);
571         if (GSS_ERROR(maj_stat)) {
572                 DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap failed: %s\n", 
573                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
574                 return NT_STATUS_ACCESS_DENIED;
575         }
576
577         sig_length = gensec_gssapi_sig_size(gensec_security, length);
578
579         /* Caller must pad to right boundary */
580         if (output_token.length != (length + sig_length)) {
581                 DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap length [%d] does not match caller length [%d] plus sig size [%d] = [%d]\n", 
582                           output_token.length, length, sig_length, length + sig_length));
583                 return NT_STATUS_INTERNAL_ERROR;
584         }
585
586         memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
587         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
588
589         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
590         dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
591         dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
592
593         gss_release_buffer(&min_stat, &output_token);
594
595         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
596             && !conf_state) {
597                 return NT_STATUS_ACCESS_DENIED;
598         }
599         return NT_STATUS_OK;
600 }
601
602 static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security, 
603                                             TALLOC_CTX *mem_ctx, 
604                                             uint8_t *data, size_t length, 
605                                             const uint8_t *whole_pdu, size_t pdu_length,
606                                             const DATA_BLOB *sig)
607 {
608         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
609         OM_uint32 maj_stat, min_stat;
610         gss_buffer_desc input_token, output_token;
611         int conf_state;
612         gss_qop_t qop_state;
613         DATA_BLOB in;
614
615         dump_data_pw("gensec_gssapi_unseal_packet: sig\n", sig->data, sig->length);
616
617         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
618
619         memcpy(in.data, sig->data, sig->length);
620         memcpy(in.data + sig->length, data, length);
621
622         input_token.length = in.length;
623         input_token.value = in.data;
624         
625         maj_stat = gss_unwrap(&min_stat, 
626                               gensec_gssapi_state->gssapi_context, 
627                               &input_token,
628                               &output_token, 
629                               &conf_state,
630                               &qop_state);
631         if (GSS_ERROR(maj_stat)) {
632                 DEBUG(1, ("gensec_gssapi_unseal_packet: GSS UnWrap failed: %s\n", 
633                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
634                 return NT_STATUS_ACCESS_DENIED;
635         }
636
637         if (output_token.length != length) {
638                 return NT_STATUS_INTERNAL_ERROR;
639         }
640
641         memcpy(data, output_token.value, length);
642
643         gss_release_buffer(&min_stat, &output_token);
644         
645         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
646             && !conf_state) {
647                 return NT_STATUS_ACCESS_DENIED;
648         }
649         return NT_STATUS_OK;
650 }
651
652 static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security, 
653                                           TALLOC_CTX *mem_ctx, 
654                                           const uint8_t *data, size_t length, 
655                                           const uint8_t *whole_pdu, size_t pdu_length, 
656                                           DATA_BLOB *sig)
657 {
658         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
659         OM_uint32 maj_stat, min_stat;
660         gss_buffer_desc input_token, output_token;
661         int conf_state;
662         ssize_t sig_length = 0;
663
664         input_token.length = length;
665         input_token.value = discard_const_p(uint8_t *, data);
666
667         maj_stat = gss_wrap(&min_stat, 
668                             gensec_gssapi_state->gssapi_context,
669                             0,
670                             GSS_C_QOP_DEFAULT,
671                             &input_token,
672                             &conf_state,
673                             &output_token);
674         if (GSS_ERROR(maj_stat)) {
675                 DEBUG(1, ("GSS Wrap failed: %s\n", 
676                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
677                 return NT_STATUS_ACCESS_DENIED;
678         }
679
680         if (output_token.length < length) {
681                 return NT_STATUS_INTERNAL_ERROR;
682         }
683
684         sig_length = gensec_gssapi_sig_size(gensec_security, length);
685
686         /* Caller must pad to right boundary */
687         if (output_token.length != (length + sig_length)) {
688                 DEBUG(1, ("gensec_gssapi_sign_packet: GSS Wrap length [%d] does not match caller length [%d] plus sig size [%d] = [%d]\n", 
689                           output_token.length, length, sig_length, length + sig_length));
690                 return NT_STATUS_INTERNAL_ERROR;
691         }
692
693         *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
694
695         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
696
697         gss_release_buffer(&min_stat, &output_token);
698
699         return NT_STATUS_OK;
700 }
701
702 static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security, 
703                                            TALLOC_CTX *mem_ctx, 
704                                            const uint8_t *data, size_t length, 
705                                            const uint8_t *whole_pdu, size_t pdu_length, 
706                                            const DATA_BLOB *sig)
707 {
708         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
709         OM_uint32 maj_stat, min_stat;
710         gss_buffer_desc input_token, output_token;
711         int conf_state;
712         gss_qop_t qop_state;
713         DATA_BLOB in;
714
715         dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
716
717         in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
718
719         memcpy(in.data, sig->data, sig->length);
720         memcpy(in.data + sig->length, data, length);
721
722         input_token.length = in.length;
723         input_token.value = in.data;
724         
725         maj_stat = gss_unwrap(&min_stat, 
726                               gensec_gssapi_state->gssapi_context, 
727                               &input_token,
728                               &output_token, 
729                               &conf_state,
730                               &qop_state);
731         if (GSS_ERROR(maj_stat)) {
732                 DEBUG(1, ("GSS UnWrap failed: %s\n", 
733                           gssapi_error_string(mem_ctx, maj_stat, min_stat)));
734                 return NT_STATUS_ACCESS_DENIED;
735         }
736
737         if (output_token.length != length) {
738                 return NT_STATUS_INTERNAL_ERROR;
739         }
740
741         gss_release_buffer(&min_stat, &output_token);
742
743         return NT_STATUS_OK;
744 }
745
746 static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, 
747                                        uint32_t feature) 
748 {
749         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
750         if (feature & GENSEC_FEATURE_SIGN) {
751                 return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
752         }
753         if (feature & GENSEC_FEATURE_SEAL) {
754                 return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
755         }
756         if (feature & GENSEC_FEATURE_SESSION_KEY) {
757                 if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
758                     && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, gensec_gssapi_state->gss_oid->length) == 0)) {
759                         return True;
760                 }
761         }
762         if (feature & GENSEC_FEATURE_DCE_STYLE) {
763                 return gensec_gssapi_state->got_flags & GSS_C_DCE_STYLE;
764         }
765         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
766                 return True;
767         }
768         return False;
769 }
770
771 static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security, 
772                                           DATA_BLOB *session_key) 
773 {
774         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
775         
776         if (gensec_gssapi_state->session_key.data) {
777                 *session_key = gensec_gssapi_state->session_key;
778                 return NT_STATUS_OK;
779         }
780
781         /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
782         if ((gensec_gssapi_state->gss_oid->length == gss_mech_krb5->length)
783             && (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
784                        gensec_gssapi_state->gss_oid->length) == 0)) {
785                 OM_uint32 maj_stat, min_stat;
786                 gss_buffer_desc skey;
787                 
788                 maj_stat = gsskrb5_get_initiator_subkey(&min_stat, 
789                                                         gensec_gssapi_state->gssapi_context, 
790                                                         &skey);
791                 
792                 if (maj_stat == 0) {
793                         DEBUG(10, ("Got KRB5 session key of length %d\n",  
794                                    (int)skey.length));
795                         gensec_gssapi_state->session_key = data_blob_talloc(gensec_gssapi_state, 
796                                                                             skey.value, skey.length);
797                         *session_key = gensec_gssapi_state->session_key;
798                         dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
799                         
800                         gss_release_buffer(&min_stat, &skey);
801                         return NT_STATUS_OK;
802                 }
803                 return NT_STATUS_NO_USER_SESSION_KEY;
804         }
805         
806         DEBUG(1, ("NO session key for this mech\n"));
807         return NT_STATUS_NO_USER_SESSION_KEY;
808 }
809
810 static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
811                                          struct auth_session_info **_session_info) 
812 {
813         NTSTATUS nt_status;
814         TALLOC_CTX *mem_ctx;
815         struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data;
816         struct auth_serversupplied_info *server_info = NULL;
817         struct auth_session_info *session_info = NULL;
818         struct PAC_LOGON_INFO *logon_info;
819         OM_uint32 maj_stat, min_stat;
820         gss_buffer_desc name_token;
821         gss_buffer_desc pac;
822         krb5_keyblock *keyblock;
823         time_t authtime;
824         krb5_principal principal;
825         char *principal_string;
826         
827         if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
828             || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements, 
829                        gensec_gssapi_state->gss_oid->length) != 0)) {
830                 DEBUG(1, ("NO session info available for this mech\n"));
831                 return NT_STATUS_INVALID_PARAMETER;
832         }
833                 
834         mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context"); 
835         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
836
837         maj_stat = gss_display_name (&min_stat,
838                                      gensec_gssapi_state->client_name,
839                                      &name_token,
840                                      NULL);
841         if (maj_stat) {
842                 return NT_STATUS_FOOBAR;
843         }
844
845         principal_string = talloc_strndup(mem_ctx, name_token.value, name_token.length);
846
847         gss_release_buffer(&min_stat, &name_token);
848
849         if (!principal_string) {
850                 talloc_free(mem_ctx);
851                 return NT_STATUS_NO_MEMORY;
852         }
853
854         maj_stat = gss_krb5_copy_service_keyblock(&min_stat, 
855                                                   gensec_gssapi_state->gssapi_context, 
856                                                   &keyblock);
857
858         if (maj_stat == 0) {
859                 maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
860                                                                      gensec_gssapi_state->gssapi_context, 
861                                                                      &authtime);
862         }
863
864         if (maj_stat == 0) {
865                 maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, 
866                                                                        gensec_gssapi_state->gssapi_context, 
867                                                                        KRB5_AUTHDATA_IF_RELEVANT,
868                                                                        &pac);
869         }
870         
871         if (maj_stat == 0) {
872                 krb5_error_code ret;
873                 DATA_BLOB pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
874                 pac_blob = unwrap_pac(mem_ctx, &pac_blob);
875                 gss_release_buffer(&min_stat, &pac);
876
877                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
878                                       principal_string, &principal);
879                 if (ret) {
880                         talloc_free(mem_ctx);
881                         return NT_STATUS_INVALID_PARAMETER;
882                 }
883                 
884                 /* decode and verify the pac */
885                 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info, pac_blob,
886                                                     gensec_gssapi_state->smb_krb5_context->krb5_context,
887                                                     NULL, keyblock, principal, authtime);
888                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
889
890                 if (NT_STATUS_IS_OK(nt_status)) {
891                         union netr_Validation validation;
892                         validation.sam3 = &logon_info->info3;
893                         nt_status = make_server_info_netlogon_validation(gensec_gssapi_state, 
894                                                                          NULL,
895                                                                          3, &validation,
896                                                                          &server_info); 
897                         if (!NT_STATUS_IS_OK(nt_status)) {
898                                 talloc_free(mem_ctx);
899                                 return nt_status;
900                         }
901                 } else {
902                         maj_stat = 1;
903                 }
904         }
905         
906         if (maj_stat) {
907                 krb5_error_code ret;
908                 DATA_BLOB user_sess_key = data_blob(NULL, 0);
909                 DATA_BLOB lm_sess_key = data_blob(NULL, 0);
910                 /* IF we have the PAC - otherwise we need to get this
911                  * data from elsewere - local ldb, or (TODO) lookup of some
912                  * kind... 
913                  *
914                  * when heimdal can generate the PAC, we should fail if there's
915                  * no PAC present
916                  */
917
918                 char *account_name;
919                 const char *realm;
920                 ret = krb5_parse_name(gensec_gssapi_state->smb_krb5_context->krb5_context,
921                                       principal_string, &principal);
922                 if (ret) {
923                         talloc_free(mem_ctx);
924                         return NT_STATUS_INVALID_PARAMETER;
925                 }
926                 
927                 realm = krb5_principal_get_realm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
928                                                  principal);
929                 ret = krb5_unparse_name_norealm(gensec_gssapi_state->smb_krb5_context->krb5_context, 
930                                                 principal, &account_name);
931                 if (ret) {
932                         krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
933                         talloc_free(mem_ctx);
934                         return NT_STATUS_NO_MEMORY;
935                 }
936
937                 DEBUG(1, ("Unable to use PAC, resorting to local user lookup!\n"));
938                 nt_status = sam_get_server_info(mem_ctx, account_name, realm,
939                                                 user_sess_key, lm_sess_key,
940                                                 &server_info);
941                 free(account_name);
942                 krb5_free_principal(gensec_gssapi_state->smb_krb5_context->krb5_context, principal);
943
944                 if (!NT_STATUS_IS_OK(nt_status)) {
945                         talloc_free(mem_ctx);
946                         return nt_status;
947                 }
948         }
949
950         /* references the server_info into the session_info */
951         nt_status = auth_generate_session_info(gensec_gssapi_state, server_info, &session_info);
952         talloc_free(mem_ctx);
953         talloc_free(server_info);
954         NT_STATUS_NOT_OK_RETURN(nt_status);
955
956         nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
957         NT_STATUS_NOT_OK_RETURN(nt_status);
958
959         *_session_info = session_info;
960
961         return NT_STATUS_OK;
962 }
963
964 static const char *gensec_gssapi_krb5_oids[] = { 
965         GENSEC_OID_KERBEROS5,
966         GENSEC_OID_KERBEROS5_OLD,
967         NULL 
968 };
969
970 /* As a server, this could in theory accept any GSSAPI mech */
971 static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
972         .name           = "gssapi_krb5",
973         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
974         .oid            = gensec_gssapi_krb5_oids,
975         .client_start   = gensec_gssapi_client_start,
976         .server_start   = gensec_gssapi_server_start,
977         .magic          = gensec_gssapi_magic,
978         .update         = gensec_gssapi_update,
979         .session_key    = gensec_gssapi_session_key,
980         .session_info   = gensec_gssapi_session_info,
981         .sig_size       = gensec_gssapi_sig_size,
982         .sign_packet    = gensec_gssapi_sign_packet,
983         .check_packet   = gensec_gssapi_check_packet,
984         .seal_packet    = gensec_gssapi_seal_packet,
985         .unseal_packet  = gensec_gssapi_unseal_packet,
986         .wrap           = gensec_gssapi_wrap,
987         .unwrap         = gensec_gssapi_unwrap,
988         .have_feature   = gensec_gssapi_have_feature,
989         .enabled        = True
990 };
991
992 NTSTATUS gensec_gssapi_init(void)
993 {
994         NTSTATUS ret;
995
996         ret = gensec_register(&gensec_gssapi_krb5_security_ops);
997         if (!NT_STATUS_IS_OK(ret)) {
998                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
999                         gensec_gssapi_krb5_security_ops.name));
1000                 return ret;
1001         }
1002
1003         return ret;
1004 }