r14380: Reduce the size of structs.h
[samba.git] / source4 / auth / gensec / gensec_krb5.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Kerberos backend for GENSEC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
7    Copyright (C) Andrew Tridgell 2001
8    Copyright (C) Luke Howard 2002-2003
9    Copyright (C) Stefan Metzmacher 2004-2005
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 #include "includes.h"
28 #include "system/kerberos.h"
29 #include "auth/kerberos/kerberos.h"
30 #include "librpc/gen_ndr/ndr_krb5pac.h"
31 #include "auth/auth.h"
32 #include "ldb.h"
33 #include "auth/auth_sam.h"
34 #include "system/network.h"
35 #include "lib/socket/socket.h"
36
37 enum GENSEC_KRB5_STATE {
38         GENSEC_KRB5_SERVER_START,
39         GENSEC_KRB5_CLIENT_START,
40         GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
41         GENSEC_KRB5_DONE
42 };
43
44 struct gensec_krb5_state {
45         DATA_BLOB session_key;
46         DATA_BLOB pac;
47         enum GENSEC_KRB5_STATE state_position;
48         struct smb_krb5_context *smb_krb5_context;
49         krb5_auth_context auth_context;
50         krb5_data enc_ticket;
51         krb5_keyblock *keyblock;
52         krb5_ticket *ticket;
53         BOOL gssapi;
54 };
55
56 static int gensec_krb5_destroy(void *ptr) 
57 {
58         struct gensec_krb5_state *gensec_krb5_state = ptr;
59
60         if (!gensec_krb5_state->smb_krb5_context) {
61                 /* We can't clean anything else up unless we started up this far */
62                 return 0;
63         }
64         if (gensec_krb5_state->enc_ticket.length) { 
65                 kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context, 
66                                             &gensec_krb5_state->enc_ticket); 
67         }
68
69         if (gensec_krb5_state->ticket) {
70                 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context, 
71                                  gensec_krb5_state->ticket);
72         }
73
74         /* ccache freed in a child destructor */
75
76         krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context, 
77                            gensec_krb5_state->keyblock);
78                 
79         if (gensec_krb5_state->auth_context) {
80                 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context, 
81                                    gensec_krb5_state->auth_context);
82         }
83
84         return 0;
85 }
86
87 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
88 {
89         krb5_error_code ret;
90         struct gensec_krb5_state *gensec_krb5_state;
91         struct cli_credentials *creds;
92         const struct socket_address *my_addr, *peer_addr;
93         krb5_address my_krb5_addr, peer_krb5_addr;
94         
95         creds = gensec_get_credentials(gensec_security);
96         if (!creds) {
97                 return NT_STATUS_INVALID_PARAMETER;
98         }
99
100         gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
101         if (!gensec_krb5_state) {
102                 return NT_STATUS_NO_MEMORY;
103         }
104
105         gensec_security->private_data = gensec_krb5_state;
106         gensec_krb5_state->smb_krb5_context = NULL;
107         gensec_krb5_state->auth_context = NULL;
108         gensec_krb5_state->ticket = NULL;
109         ZERO_STRUCT(gensec_krb5_state->enc_ticket);
110         gensec_krb5_state->keyblock = NULL;
111         gensec_krb5_state->session_key = data_blob(NULL, 0);
112         gensec_krb5_state->pac = data_blob(NULL, 0);
113         gensec_krb5_state->gssapi = False;
114
115         talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); 
116
117         if (cli_credentials_get_krb5_context(creds, &gensec_krb5_state->smb_krb5_context)) {
118                 talloc_free(gensec_krb5_state);
119                 return NT_STATUS_INTERNAL_ERROR;
120         }
121
122         ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
123         if (ret) {
124                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 
125                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
126                                                     ret, gensec_krb5_state)));
127                 talloc_free(gensec_krb5_state);
128                 return NT_STATUS_INTERNAL_ERROR;
129         }
130
131         ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, 
132                                      gensec_krb5_state->auth_context,
133                                      KRB5_AUTH_CONTEXT_DO_SEQUENCE);
134         if (ret) {
135                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", 
136                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
137                                                     ret, gensec_krb5_state)));
138                 talloc_free(gensec_krb5_state);
139                 return NT_STATUS_INTERNAL_ERROR;
140         }
141
142         my_addr = gensec_get_my_addr(gensec_security);
143         if (my_addr && my_addr->sockaddr) {
144                 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, 
145                                             my_addr->sockaddr, &my_krb5_addr);
146                 if (ret) {
147                         DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
148                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
149                                                             ret, gensec_krb5_state)));
150                         talloc_free(gensec_krb5_state);
151                         return NT_STATUS_INTERNAL_ERROR;
152                 }
153         }
154
155         peer_addr = gensec_get_my_addr(gensec_security);
156         if (peer_addr && peer_addr->sockaddr) {
157                 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, 
158                                             peer_addr->sockaddr, &peer_krb5_addr);
159                 if (ret) {
160                         DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
161                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
162                                                             ret, gensec_krb5_state)));
163                         talloc_free(gensec_krb5_state);
164                         return NT_STATUS_INTERNAL_ERROR;
165                 }
166         }
167
168         ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, 
169                                      gensec_krb5_state->auth_context,
170                                      my_addr ? &my_krb5_addr : NULL, 
171                                      peer_addr ? &peer_krb5_addr : NULL);
172         if (ret) {
173                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", 
174                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
175                                                     ret, gensec_krb5_state)));
176                 talloc_free(gensec_krb5_state);
177                 return NT_STATUS_INTERNAL_ERROR;
178         }
179
180         return NT_STATUS_OK;
181 }
182
183 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
184 {
185         NTSTATUS nt_status;
186         struct gensec_krb5_state *gensec_krb5_state;
187
188         nt_status = gensec_krb5_start(gensec_security);
189         if (!NT_STATUS_IS_OK(nt_status)) {
190                 return nt_status;
191         }
192         
193         gensec_krb5_state = gensec_security->private_data;
194         gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
195
196         return NT_STATUS_OK;
197 }
198
199 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
200 {
201         NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
202
203         if (NT_STATUS_IS_OK(nt_status)) {
204                 struct gensec_krb5_state *gensec_krb5_state;
205                 gensec_krb5_state = gensec_security->private_data;
206                 gensec_krb5_state->gssapi = True;
207         }
208         return nt_status;
209 }
210
211 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
212 {
213         struct gensec_krb5_state *gensec_krb5_state;
214         krb5_error_code ret;
215         NTSTATUS nt_status;
216         struct ccache_container *ccache_container;
217         const char *hostname;
218         krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
219
220         const char *principal;
221         krb5_data in_data;
222
223         hostname = gensec_get_target_hostname(gensec_security);
224         if (!hostname) {
225                 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
226                 return NT_STATUS_INVALID_PARAMETER;
227         }
228         if (is_ipaddress(hostname)) {
229                 DEBUG(2, ("Cannot do krb5 to an IP address"));
230                 return NT_STATUS_INVALID_PARAMETER;
231         }
232         if (strequal(hostname, "localhost")) {
233                 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
234                 return NT_STATUS_INVALID_PARAMETER;
235         }
236                         
237         nt_status = gensec_krb5_start(gensec_security);
238         if (!NT_STATUS_IS_OK(nt_status)) {
239                 return nt_status;
240         }
241
242         gensec_krb5_state = gensec_security->private_data;
243         gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
244
245         ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container);
246         if (ret) {
247                 DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n", 
248                          error_message(ret)));
249                 return NT_STATUS_UNSUCCESSFUL;
250         }
251
252         in_data.length = 0;
253         
254         principal = gensec_get_target_principal(gensec_security);
255         if (principal && lp_client_use_spnego_principal()) {
256                 krb5_principal target_principal;
257                 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
258                                       &target_principal);
259                 if (ret == 0) {
260                         ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, 
261                                                 &gensec_krb5_state->auth_context,
262                                                 ap_req_options, 
263                                                 target_principal,
264                                                 &in_data, ccache_container->ccache, 
265                                                 &gensec_krb5_state->enc_ticket);
266                         krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, 
267                                             target_principal);
268                 }
269         } else {
270                 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 
271                                   &gensec_krb5_state->auth_context,
272                                   ap_req_options,
273                                   gensec_get_target_service(gensec_security),
274                                   hostname,
275                                   &in_data, ccache_container->ccache, 
276                                   &gensec_krb5_state->enc_ticket);
277         }
278         switch (ret) {
279         case 0:
280                 return NT_STATUS_OK;
281         case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
282                 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
283                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
284                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
285         case KRB5_KDC_UNREACH:
286                 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
287                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
288                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
289         case KRB5KDC_ERR_PREAUTH_FAILED:
290         case KRB5KRB_AP_ERR_TKT_EXPIRED:
291         case KRB5_CC_END:
292                 /* Too much clock skew - we will need to kinit to re-skew the clock */
293         case KRB5KRB_AP_ERR_SKEW:
294         case KRB5_KDCREP_SKEW:
295         {
296                 DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
297                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
298                 /* fall down to remaining code */
299         }
300         
301         
302         /* just don't print a message for these really ordinary messages */
303         case KRB5_FCC_NOFILE:
304         case KRB5_CC_NOTFOUND:
305         case ENOENT:
306                 
307                 return NT_STATUS_UNSUCCESSFUL;
308                 break;
309                 
310         default:
311                 DEBUG(0, ("kerberos: %s\n", 
312                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
313                 return NT_STATUS_UNSUCCESSFUL;
314         }
315 }
316
317 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
318 {
319         NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
320
321         if (NT_STATUS_IS_OK(nt_status)) {
322                 struct gensec_krb5_state *gensec_krb5_state;
323                 gensec_krb5_state = gensec_security->private_data;
324                 gensec_krb5_state->gssapi = True;
325         }
326         return nt_status;
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_fake_gssapi_krb5_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 Krb5 GENSEC mechanism
351  * 
352  * @param gensec_krb5_state KRB5 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_krb5_update(struct gensec_security *gensec_security, 
361                                    TALLOC_CTX *out_mem_ctx, 
362                                    const DATA_BLOB in, DATA_BLOB *out) 
363 {
364         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
365         krb5_error_code ret = 0;
366         NTSTATUS nt_status;
367
368         switch (gensec_krb5_state->state_position) {
369         case GENSEC_KRB5_CLIENT_START:
370         {
371                 DATA_BLOB unwrapped_out;
372                 
373                 if (gensec_krb5_state->gssapi) {
374                         unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
375                         
376                         /* wrap that up in a nice GSS-API wrapping */
377                         *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
378                 } else {
379                         *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
380                 }
381                 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
382                 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
383                 return nt_status;
384         }
385                 
386         case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
387         {
388                 DATA_BLOB unwrapped_in;
389                 krb5_data inbuf;
390                 krb5_ap_rep_enc_part *repl = NULL;
391                 uint8_t tok_id[2];
392
393                 if (gensec_krb5_state->gssapi) {
394                         if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
395                                 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
396                                 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
397                                 return NT_STATUS_INVALID_PARAMETER;
398                         }
399                 } else {
400                         unwrapped_in = in;
401                 }
402                 /* TODO: check the tok_id */
403
404                 inbuf.data = unwrapped_in.data;
405                 inbuf.length = unwrapped_in.length;
406                 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, 
407                                   gensec_krb5_state->auth_context,
408                                   &inbuf, &repl);
409                 if (ret) {
410                         DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
411                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
412                         dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
413                         nt_status = NT_STATUS_ACCESS_DENIED;
414                 } else {
415                         *out = data_blob(NULL, 0);
416                         nt_status = NT_STATUS_OK;
417                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
418                 }
419                 if (repl) {
420                         krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
421                 }
422                 return nt_status;
423         }
424
425         case GENSEC_KRB5_SERVER_START:
426         {
427                 DATA_BLOB unwrapped_in;
428                 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
429                 uint8_t tok_id[2];
430
431                 if (!in.data) {
432                         return NT_STATUS_INVALID_PARAMETER;
433                 }       
434
435                 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
436                 if (gensec_krb5_state->gssapi
437                     && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
438                         nt_status = ads_verify_ticket(out_mem_ctx, 
439                                                       gensec_krb5_state->smb_krb5_context,
440                                                       &gensec_krb5_state->auth_context, 
441                                                       gensec_get_credentials(gensec_security),
442                                                       gensec_get_target_service(gensec_security), &unwrapped_in, 
443                                                       &gensec_krb5_state->ticket, &unwrapped_out,
444                                                       &gensec_krb5_state->keyblock);
445                 } else {
446                         /* TODO: check the tok_id */
447                         nt_status = ads_verify_ticket(out_mem_ctx, 
448                                                       gensec_krb5_state->smb_krb5_context,
449                                                       &gensec_krb5_state->auth_context, 
450                                                       gensec_get_credentials(gensec_security),
451                                                       gensec_get_target_service(gensec_security), 
452                                                       &in, 
453                                                       &gensec_krb5_state->ticket, &unwrapped_out,
454                                                       &gensec_krb5_state->keyblock);
455                 }
456
457                 if (!NT_STATUS_IS_OK(nt_status)) {
458                         return nt_status;
459                 }
460
461                 if (NT_STATUS_IS_OK(nt_status)) {
462                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
463                         /* wrap that up in a nice GSS-API wrapping */
464                         if (gensec_krb5_state->gssapi) {
465                                 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
466                         } else {
467                                 *out = unwrapped_out;
468                         }
469                 }
470                 return nt_status;
471         }
472
473         case GENSEC_KRB5_DONE:
474         default:
475                 /* Asking too many times... */
476                 return NT_STATUS_INVALID_PARAMETER;
477         }
478 }
479
480 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, 
481                                         DATA_BLOB *session_key) 
482 {
483         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
484         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
485         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
486         krb5_keyblock *skey;
487         krb5_error_code err = -1;
488
489         if (gensec_krb5_state->session_key.data) {
490                 *session_key = gensec_krb5_state->session_key;
491                 return NT_STATUS_OK;
492         }
493
494         switch (gensec_security->gensec_role) {
495         case GENSEC_CLIENT:
496                 err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
497                 break;
498         case GENSEC_SERVER:
499                 err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
500                 break;
501         }
502         if (err == 0 && skey != NULL) {
503                 DEBUG(10, ("Got KRB5 session key of length %d\n",  
504                            (int)KRB5_KEY_LENGTH(skey)));
505                 gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, 
506                                                 KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
507                 *session_key = gensec_krb5_state->session_key;
508                 dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
509
510                 krb5_free_keyblock(context, skey);
511                 return NT_STATUS_OK;
512         } else {
513                 DEBUG(10, ("KRB5 error getting session key %d\n", err));
514                 return NT_STATUS_NO_USER_SESSION_KEY;
515         }
516 }
517
518 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
519                                          struct auth_session_info **_session_info) 
520 {
521         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
522         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
523         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
524         struct auth_serversupplied_info *server_info = NULL;
525         struct auth_session_info *session_info = NULL;
526         struct PAC_LOGON_INFO *logon_info;
527
528         krb5_principal client_principal;
529         
530         DATA_BLOB pac;
531         krb5_data pac_data;
532
533         krb5_error_code ret;
534
535         TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
536         if (!mem_ctx) {
537                 return NT_STATUS_NO_MEMORY;
538         }
539         
540         ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 
541                                                       KRB5_AUTHDATA_WIN2K_PAC, 
542                                                       &pac_data);
543         
544         if (ret) {
545                 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 
546                           smb_get_krb5_error_message(context, 
547                                                      ret, mem_ctx)));
548         } else {
549                 pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
550                 if (!pac.data) {
551                         talloc_free(mem_ctx);
552                         return NT_STATUS_NO_MEMORY;
553                 }
554
555                 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
556                 if (ret) {
557                         DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 
558                                   smb_get_krb5_error_message(context, 
559                                                              ret, mem_ctx)));
560                         talloc_free(mem_ctx);
561                         return NT_STATUS_NO_MEMORY;
562                 }
563                 
564                 /* decode and verify the pac */
565                 nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac,
566                                                     gensec_krb5_state->smb_krb5_context->krb5_context,
567                                                     NULL, gensec_krb5_state->keyblock,
568                                                     client_principal,
569                                                     gensec_krb5_state->ticket->ticket.authtime, NULL);
570                 krb5_free_principal(context, client_principal);
571
572                 if (NT_STATUS_IS_OK(nt_status)) {
573                         union netr_Validation validation;
574                         validation.sam3 = &logon_info->info3;
575                         nt_status = make_server_info_netlogon_validation(mem_ctx, 
576                                                                          NULL,
577                                                                          3, &validation,
578                                                                          &server_info); 
579                 }
580         }
581
582                 
583                 
584         /* IF we have the PAC - otherwise we need to get this
585          * data from elsewere - local ldb, or (TODO) lookup of some
586          * kind... 
587          */
588         if (!NT_STATUS_IS_OK(nt_status)) {
589                 /* NO pac, or can't parse or verify it */
590                 char *principal_string;
591                 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
592                 if (ret) {
593                         DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 
594                                   smb_get_krb5_error_message(context, 
595                                                              ret, mem_ctx)));
596                         talloc_free(mem_ctx);
597                         return NT_STATUS_NO_MEMORY;
598                 }
599                 
600                 ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 
601                                         client_principal, &principal_string);
602                 krb5_free_principal(context, client_principal);
603                 if (ret) {
604                         talloc_free(mem_ctx);
605                         return NT_STATUS_NO_MEMORY;
606                 }
607
608                 nt_status = sam_get_server_info_principal(mem_ctx, principal_string,
609                                                           &server_info);
610                 free(principal_string);
611                 
612                 if (!NT_STATUS_IS_OK(nt_status)) {
613                         talloc_free(mem_ctx);
614                         return nt_status;
615                 }
616         }
617
618         /* references the server_info into the session_info */
619         nt_status = auth_generate_session_info(mem_ctx, server_info, &session_info);
620
621         if (!NT_STATUS_IS_OK(nt_status)) {
622                 talloc_free(mem_ctx);
623                 return nt_status;
624         }
625
626         nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
627
628         if (!NT_STATUS_IS_OK(nt_status)) {
629                 talloc_free(mem_ctx);
630                 return nt_status;
631         }
632
633         *_session_info = session_info;
634
635         talloc_steal(gensec_krb5_state, session_info);
636         talloc_free(mem_ctx);
637         return NT_STATUS_OK;
638 }
639
640 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security, 
641                                    TALLOC_CTX *mem_ctx, 
642                                    const DATA_BLOB *in, 
643                                    DATA_BLOB *out)
644 {
645         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
646         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
647         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
648         krb5_error_code ret;
649         krb5_data input, output;
650         input.length = in->length;
651         input.data = in->data;
652         
653         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
654                 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
655                 if (ret) {
656                         DEBUG(1, ("krb5_mk_priv failed: %s\n", 
657                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
658                                                              ret, mem_ctx)));
659                         return NT_STATUS_ACCESS_DENIED;
660                 }
661                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
662                 
663                 krb5_data_free(&output);
664         } else {
665                 return NT_STATUS_ACCESS_DENIED;
666         }
667         return NT_STATUS_OK;
668 }
669
670 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, 
671                                      TALLOC_CTX *mem_ctx, 
672                                      const DATA_BLOB *in, 
673                                      DATA_BLOB *out)
674 {
675         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
676         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
677         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
678         krb5_error_code ret;
679         krb5_data input, output;
680         krb5_replay_data replay;
681         input.length = in->length;
682         input.data = in->data;
683         
684         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
685                 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
686                 if (ret) {
687                         DEBUG(1, ("krb5_rd_priv failed: %s\n", 
688                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
689                                                              ret, mem_ctx)));
690                         return NT_STATUS_ACCESS_DENIED;
691                 }
692                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
693                 
694                 krb5_data_free(&output);
695         } else {
696                 return NT_STATUS_ACCESS_DENIED;
697         }
698         return NT_STATUS_OK;
699 }
700
701 static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
702                                      uint32_t feature)
703 {
704         struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
705         if (feature & GENSEC_FEATURE_SESSION_KEY) {
706                 return True;
707         } 
708         if (!gensec_krb5_state->gssapi && 
709             (feature & GENSEC_FEATURE_SEAL)) {
710                 return True;
711         } 
712         
713         return False;
714 }
715
716 static const char *gensec_krb5_oids[] = { 
717         GENSEC_OID_KERBEROS5,
718         GENSEC_OID_KERBEROS5_OLD,
719         NULL 
720 };
721
722 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
723         .name           = "fake_gssapi_krb5",
724         .sasl_name      = "GSSAPI",
725         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
726         .oid            = gensec_krb5_oids,
727         .client_start   = gensec_fake_gssapi_krb5_client_start,
728         .server_start   = gensec_fake_gssapi_krb5_server_start,
729         .update         = gensec_krb5_update,
730         .magic          = gensec_fake_gssapi_krb5_magic,
731         .session_key    = gensec_krb5_session_key,
732         .session_info   = gensec_krb5_session_info,
733         .have_feature   = gensec_krb5_have_feature,
734         .enabled        = False,
735         .kerberos       = True
736 };
737
738 static const struct gensec_security_ops gensec_krb5_security_ops = {
739         .name           = "krb5",
740         .client_start   = gensec_krb5_client_start,
741         .server_start   = gensec_krb5_server_start,
742         .update         = gensec_krb5_update,
743         .session_key    = gensec_krb5_session_key,
744         .session_info   = gensec_krb5_session_info,
745         .have_feature   = gensec_krb5_have_feature,
746         .wrap           = gensec_krb5_wrap,
747         .unwrap         = gensec_krb5_unwrap,
748         .enabled        = True,
749         .kerberos       = True
750 };
751
752 NTSTATUS gensec_krb5_init(void)
753 {
754         NTSTATUS ret;
755
756         ret = gensec_register(&gensec_krb5_security_ops);
757         if (!NT_STATUS_IS_OK(ret)) {
758                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
759                         gensec_krb5_security_ops.name));
760                 return ret;
761         }
762
763         ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
764         if (!NT_STATUS_IS_OK(ret)) {
765                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
766                         gensec_krb5_security_ops.name));
767                 return ret;
768         }
769
770         return ret;
771 }