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