auth: Split out make_user_info_SamBaseInfo and add authenticated argument
[ddiss/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 3 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, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/kerberos.h"
28 #include "auth/kerberos/kerberos.h"
29 #include "auth/auth.h"
30 #include "lib/socket/socket.h"
31 #include "lib/tsocket/tsocket.h"
32 #include "librpc/rpc/dcerpc.h"
33 #include "auth/credentials/credentials.h"
34 #include "auth/credentials/credentials_krb5.h"
35 #include "auth/kerberos/kerberos_credentials.h"
36 #include "auth/gensec/gensec.h"
37 #include "auth/gensec/gensec_proto.h"
38 #include "param/param.h"
39 #include "auth/auth_sam_reply.h"
40 #include "lib/util/util_net.h"
41
42 _PUBLIC_ NTSTATUS gensec_krb5_init(void);
43
44 enum GENSEC_KRB5_STATE {
45         GENSEC_KRB5_SERVER_START,
46         GENSEC_KRB5_CLIENT_START,
47         GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
48         GENSEC_KRB5_DONE
49 };
50
51 struct gensec_krb5_state {
52         DATA_BLOB session_key;
53         DATA_BLOB pac;
54         enum GENSEC_KRB5_STATE state_position;
55         struct smb_krb5_context *smb_krb5_context;
56         krb5_auth_context auth_context;
57         krb5_data enc_ticket;
58         krb5_keyblock *keyblock;
59         krb5_ticket *ticket;
60         bool gssapi;
61         krb5_flags ap_req_options;
62 };
63
64 static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
65 {
66         if (!gensec_krb5_state->smb_krb5_context) {
67                 /* We can't clean anything else up unless we started up this far */
68                 return 0;
69         }
70         if (gensec_krb5_state->enc_ticket.length) { 
71                 kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context, 
72                                             &gensec_krb5_state->enc_ticket); 
73         }
74
75         if (gensec_krb5_state->ticket) {
76                 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context, 
77                                  gensec_krb5_state->ticket);
78         }
79
80         /* ccache freed in a child destructor */
81
82         krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context, 
83                            gensec_krb5_state->keyblock);
84                 
85         if (gensec_krb5_state->auth_context) {
86                 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context, 
87                                    gensec_krb5_state->auth_context);
88         }
89
90         return 0;
91 }
92
93 static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
94 {
95         krb5_error_code ret;
96         struct gensec_krb5_state *gensec_krb5_state;
97         struct cli_credentials *creds;
98         const struct tsocket_address *tlocal_addr, *tremote_addr;
99         krb5_address my_krb5_addr, peer_krb5_addr;
100         
101         creds = gensec_get_credentials(gensec_security);
102         if (!creds) {
103                 return NT_STATUS_INVALID_PARAMETER;
104         }
105
106         gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
107         if (!gensec_krb5_state) {
108                 return NT_STATUS_NO_MEMORY;
109         }
110
111         gensec_security->private_data = gensec_krb5_state;
112         gensec_krb5_state->smb_krb5_context = NULL;
113         gensec_krb5_state->auth_context = NULL;
114         gensec_krb5_state->ticket = NULL;
115         ZERO_STRUCT(gensec_krb5_state->enc_ticket);
116         gensec_krb5_state->keyblock = NULL;
117         gensec_krb5_state->session_key = data_blob(NULL, 0);
118         gensec_krb5_state->pac = data_blob(NULL, 0);
119         gensec_krb5_state->gssapi = gssapi;
120
121         talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); 
122
123         if (cli_credentials_get_krb5_context(creds, 
124                                              gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
125                 talloc_free(gensec_krb5_state);
126                 return NT_STATUS_INTERNAL_ERROR;
127         }
128
129         ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
130         if (ret) {
131                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 
132                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
133                                                     ret, gensec_krb5_state)));
134                 talloc_free(gensec_krb5_state);
135                 return NT_STATUS_INTERNAL_ERROR;
136         }
137
138         ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, 
139                                      gensec_krb5_state->auth_context,
140                                      KRB5_AUTH_CONTEXT_DO_SEQUENCE);
141         if (ret) {
142                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", 
143                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
144                                                     ret, gensec_krb5_state)));
145                 talloc_free(gensec_krb5_state);
146                 return NT_STATUS_INTERNAL_ERROR;
147         }
148
149         tlocal_addr = gensec_get_local_address(gensec_security);
150         if (tlocal_addr) {
151                 ssize_t socklen;
152                 struct sockaddr_storage ss;
153
154                 socklen = tsocket_address_bsd_sockaddr(tlocal_addr,
155                                 (struct sockaddr *) &ss,
156                                 sizeof(struct sockaddr_storage));
157                 if (socklen < 0) {
158                         talloc_free(gensec_krb5_state);
159                         return NT_STATUS_INTERNAL_ERROR;
160                 }
161                 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
162                                 (const struct sockaddr *) &ss, &my_krb5_addr);
163                 if (ret) {
164                         DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
165                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
166                                                             ret, gensec_krb5_state)));
167                         talloc_free(gensec_krb5_state);
168                         return NT_STATUS_INTERNAL_ERROR;
169                 }
170         }
171
172         tremote_addr = gensec_get_remote_address(gensec_security);
173         if (tremote_addr) {
174                 ssize_t socklen;
175                 struct sockaddr_storage ss;
176
177                 socklen = tsocket_address_bsd_sockaddr(tremote_addr,
178                                 (struct sockaddr *) &ss,
179                                 sizeof(struct sockaddr_storage));
180                 if (socklen < 0) {
181                         talloc_free(gensec_krb5_state);
182                         return NT_STATUS_INTERNAL_ERROR;
183                 }
184                 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
185                                 (const struct sockaddr *) &ss, &peer_krb5_addr);
186                 if (ret) {
187                         DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
188                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
189                                                             ret, gensec_krb5_state)));
190                         talloc_free(gensec_krb5_state);
191                         return NT_STATUS_INTERNAL_ERROR;
192                 }
193         }
194
195         ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, 
196                                      gensec_krb5_state->auth_context,
197                                      tlocal_addr ? &my_krb5_addr : NULL,
198                                      tremote_addr ? &peer_krb5_addr : NULL);
199         if (ret) {
200                 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", 
201                          smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
202                                                     ret, gensec_krb5_state)));
203                 talloc_free(gensec_krb5_state);
204                 return NT_STATUS_INTERNAL_ERROR;
205         }
206
207         return NT_STATUS_OK;
208 }
209
210 static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi)
211 {
212         NTSTATUS nt_status;
213         struct gensec_krb5_state *gensec_krb5_state;
214
215         nt_status = gensec_krb5_start(gensec_security, gssapi);
216         if (!NT_STATUS_IS_OK(nt_status)) {
217                 return nt_status;
218         }
219         
220         gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
221         gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
222
223         return NT_STATUS_OK;
224 }
225
226 static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
227 {
228         return gensec_krb5_common_server_start(gensec_security, false);
229 }
230
231 static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
232 {
233         return gensec_krb5_common_server_start(gensec_security, true);
234 }
235
236 static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
237 {
238         struct gensec_krb5_state *gensec_krb5_state;
239         krb5_error_code ret;
240         NTSTATUS nt_status;
241         struct ccache_container *ccache_container;
242         const char *hostname;
243         const char *error_string;
244         const char *principal;
245         krb5_data in_data;
246         struct tevent_context *previous_ev;
247
248         hostname = gensec_get_target_hostname(gensec_security);
249         if (!hostname) {
250                 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
251                 return NT_STATUS_INVALID_PARAMETER;
252         }
253         if (is_ipaddress(hostname)) {
254                 DEBUG(2, ("Cannot do krb5 to an IP address"));
255                 return NT_STATUS_INVALID_PARAMETER;
256         }
257         if (strcmp(hostname, "localhost") == 0) {
258                 DEBUG(2, ("krb5 to 'localhost' does not make sense"));
259                 return NT_STATUS_INVALID_PARAMETER;
260         }
261                         
262         nt_status = gensec_krb5_start(gensec_security, gssapi);
263         if (!NT_STATUS_IS_OK(nt_status)) {
264                 return nt_status;
265         }
266
267         gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
268         gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
269         gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;
270
271         if (gensec_krb5_state->gssapi) {
272                 /* The Fake GSSAPI modal emulates Samba3, which does not do mutual authentication */
273                 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
274                         gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
275                 }
276         } else {
277                 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
278                 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
279                         gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
280                 }
281         }
282
283         principal = gensec_get_target_principal(gensec_security);
284
285         ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), 
286                                          gensec_security->event_ctx, 
287                                          gensec_security->settings->lp_ctx, &ccache_container, &error_string);
288         switch (ret) {
289         case 0:
290                 break;
291         case KRB5KDC_ERR_PREAUTH_FAILED:
292         case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
293                 return NT_STATUS_LOGON_FAILURE;
294         case KRB5_KDC_UNREACH:
295                 DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
296                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
297         case KRB5_CC_NOTFOUND:
298         case KRB5_CC_END:
299                 DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
300                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
301         default:
302                 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
303                 return NT_STATUS_UNSUCCESSFUL;
304         }
305         in_data.length = 0;
306         
307         /* Do this every time, in case we have weird recursive issues here */
308         ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, gensec_security->event_ctx, &previous_ev);
309         if (ret != 0) {
310                 DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
311                 return NT_STATUS_NO_MEMORY;
312         }
313         if (principal) {
314                 krb5_principal target_principal;
315                 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
316                                       &target_principal);
317                 if (ret == 0) {
318                         ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, 
319                                                 &gensec_krb5_state->auth_context,
320                                                 gensec_krb5_state->ap_req_options, 
321                                                 target_principal,
322                                                 &in_data, ccache_container->ccache, 
323                                                 &gensec_krb5_state->enc_ticket);
324                         krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, 
325                                             target_principal);
326                 }
327         } else {
328                 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 
329                                   &gensec_krb5_state->auth_context,
330                                   gensec_krb5_state->ap_req_options,
331                                   gensec_get_target_service(gensec_security),
332                                   hostname,
333                                   &in_data, ccache_container->ccache, 
334                                   &gensec_krb5_state->enc_ticket);
335         }
336
337         smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, gensec_security->event_ctx);
338
339         switch (ret) {
340         case 0:
341                 return NT_STATUS_OK;
342         case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
343                 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
344                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
345                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
346         case KRB5_KDC_UNREACH:
347                 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
348                           hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
349                 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
350         case KRB5KDC_ERR_PREAUTH_FAILED:
351         case KRB5KRB_AP_ERR_TKT_EXPIRED:
352         case KRB5_CC_END:
353                 /* Too much clock skew - we will need to kinit to re-skew the clock */
354         case KRB5KRB_AP_ERR_SKEW:
355         case KRB5_KDCREP_SKEW:
356         {
357                 DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
358                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
359                 /*fall through*/
360         }
361         
362         /* just don't print a message for these really ordinary messages */
363         case KRB5_FCC_NOFILE:
364         case KRB5_CC_NOTFOUND:
365         case ENOENT:
366                 
367                 return NT_STATUS_UNSUCCESSFUL;
368                 break;
369                 
370         default:
371                 DEBUG(0, ("kerberos: %s\n", 
372                           smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
373                 return NT_STATUS_UNSUCCESSFUL;
374         }
375 }
376
377 static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
378 {
379         return gensec_krb5_common_client_start(gensec_security, false);
380 }
381
382 static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
383 {
384         return gensec_krb5_common_client_start(gensec_security, true);
385 }
386
387 /**
388  * Check if the packet is one for this mechansim
389  * 
390  * @param gensec_security GENSEC state
391  * @param in The request, as a DATA_BLOB
392  * @return Error, INVALID_PARAMETER if it's not a packet for us
393  *                or NT_STATUS_OK if the packet is ok. 
394  */
395
396 static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security, 
397                                   const DATA_BLOB *in) 
398 {
399         if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
400                 return NT_STATUS_OK;
401         } else {
402                 return NT_STATUS_INVALID_PARAMETER;
403         }
404 }
405
406
407 /**
408  * Next state function for the Krb5 GENSEC mechanism
409  * 
410  * @param gensec_krb5_state KRB5 State
411  * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
412  * @param in The request, as a DATA_BLOB
413  * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
414  * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
415  *                or NT_STATUS_OK if the user is authenticated. 
416  */
417
418 static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, 
419                                    TALLOC_CTX *out_mem_ctx, 
420                                    const DATA_BLOB in, DATA_BLOB *out) 
421 {
422         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
423         krb5_error_code ret = 0;
424         NTSTATUS nt_status;
425
426         switch (gensec_krb5_state->state_position) {
427         case GENSEC_KRB5_CLIENT_START:
428         {
429                 DATA_BLOB unwrapped_out;
430                 
431                 if (gensec_krb5_state->gssapi) {
432                         unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
433                         
434                         /* wrap that up in a nice GSS-API wrapping */
435                         *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
436                 } else {
437                         *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
438                 }
439                 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
440                         gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
441                         nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
442                 } else {
443                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
444                         nt_status = NT_STATUS_OK;
445                 }
446                 return nt_status;
447         }
448                 
449         case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
450         {
451                 DATA_BLOB unwrapped_in;
452                 krb5_data inbuf;
453                 krb5_ap_rep_enc_part *repl = NULL;
454                 uint8_t tok_id[2];
455
456                 if (gensec_krb5_state->gssapi) {
457                         if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
458                                 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
459                                 dump_data_pw("Mutual authentication message:\n", in.data, in.length);
460                                 return NT_STATUS_INVALID_PARAMETER;
461                         }
462                 } else {
463                         unwrapped_in = in;
464                 }
465                 /* TODO: check the tok_id */
466
467                 inbuf.data = unwrapped_in.data;
468                 inbuf.length = unwrapped_in.length;
469                 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, 
470                                   gensec_krb5_state->auth_context,
471                                   &inbuf, &repl);
472                 if (ret) {
473                         DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
474                                  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
475                         dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
476                         nt_status = NT_STATUS_ACCESS_DENIED;
477                 } else {
478                         *out = data_blob(NULL, 0);
479                         nt_status = NT_STATUS_OK;
480                         gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
481                 }
482                 if (repl) {
483                         krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
484                 }
485                 return nt_status;
486         }
487
488         case GENSEC_KRB5_SERVER_START:
489         {
490                 DATA_BLOB unwrapped_in;
491                 DATA_BLOB unwrapped_out = data_blob(NULL, 0);
492                 krb5_data inbuf, outbuf;
493                 uint8_t tok_id[2];
494                 struct keytab_container *keytab;
495                 krb5_principal server_in_keytab;
496                 const char *error_string;
497                 enum credentials_obtained obtained;
498
499                 if (!in.data) {
500                         return NT_STATUS_INVALID_PARAMETER;
501                 }       
502
503                 /* Grab the keytab, however generated */
504                 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), 
505                                                  gensec_security->settings->lp_ctx, &keytab);
506                 if (ret) {
507                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
508                 }
509                 
510                 /* This ensures we lookup the correct entry in that keytab */
511                 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), 
512                                                  gensec_krb5_state->smb_krb5_context, 
513                                                  &server_in_keytab, &obtained, &error_string);
514
515                 if (ret) {
516                         DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
517                         return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
518                 }
519
520                 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
521                 if (gensec_krb5_state->gssapi
522                     && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
523                         inbuf.data = unwrapped_in.data;
524                         inbuf.length = unwrapped_in.length;
525                 } else {
526                         inbuf.data = in.data;
527                         inbuf.length = in.length;
528                 }
529
530                 ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
531                                               &gensec_krb5_state->auth_context, 
532                                               &inbuf, keytab->keytab, server_in_keytab,  
533                                               &outbuf, 
534                                               &gensec_krb5_state->ticket, 
535                                               &gensec_krb5_state->keyblock);
536
537                 if (ret) {
538                         return NT_STATUS_LOGON_FAILURE;
539                 }
540                 unwrapped_out.data = (uint8_t *)outbuf.data;
541                 unwrapped_out.length = outbuf.length;
542                 gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
543                 /* wrap that up in a nice GSS-API wrapping */
544                 if (gensec_krb5_state->gssapi) {
545                         *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
546                 } else {
547                         *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
548                 }
549                 krb5_data_free(&outbuf);
550                 return NT_STATUS_OK;
551         }
552
553         case GENSEC_KRB5_DONE:
554         default:
555                 /* Asking too many times... */
556                 return NT_STATUS_INVALID_PARAMETER;
557         }
558 }
559
560 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, 
561                                         DATA_BLOB *session_key) 
562 {
563         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
564         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
565         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
566         krb5_keyblock *skey;
567         krb5_error_code err = -1;
568
569         if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
570                 return NT_STATUS_NO_USER_SESSION_KEY;
571         }
572
573         if (gensec_krb5_state->session_key.data) {
574                 *session_key = gensec_krb5_state->session_key;
575                 return NT_STATUS_OK;
576         }
577
578         switch (gensec_security->gensec_role) {
579         case GENSEC_CLIENT:
580                 err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
581                 break;
582         case GENSEC_SERVER:
583                 err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
584                 break;
585         }
586         if (err == 0 && skey != NULL) {
587                 DEBUG(10, ("Got KRB5 session key of length %d\n",  
588                            (int)KRB5_KEY_LENGTH(skey)));
589                 gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, 
590                                                 KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
591                 *session_key = gensec_krb5_state->session_key;
592                 dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
593
594                 krb5_free_keyblock(context, skey);
595                 return NT_STATUS_OK;
596         } else {
597                 DEBUG(10, ("KRB5 error getting session key %d\n", err));
598                 return NT_STATUS_NO_USER_SESSION_KEY;
599         }
600 }
601
602 static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
603                                          struct auth_session_info **_session_info) 
604 {
605         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
606         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
607         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
608         struct auth_user_info_dc *user_info_dc = NULL;
609         struct auth_session_info *session_info = NULL;
610         struct PAC_LOGON_INFO *logon_info;
611
612         krb5_principal client_principal;
613         char *principal_string;
614         
615         DATA_BLOB pac;
616         krb5_data pac_data;
617
618         krb5_error_code ret;
619
620         TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
621         if (!mem_ctx) {
622                 return NT_STATUS_NO_MEMORY;
623         }
624         
625         ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
626         if (ret) {
627                 DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 
628                           smb_get_krb5_error_message(context, 
629                                                      ret, mem_ctx)));
630                 talloc_free(mem_ctx);
631                 return NT_STATUS_NO_MEMORY;
632         }
633         
634         ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 
635                                 client_principal, &principal_string);
636         if (ret) {
637                 DEBUG(1, ("Unable to parse client principal: %s\n",
638                           smb_get_krb5_error_message(context, 
639                                                      ret, mem_ctx)));
640                 krb5_free_principal(context, client_principal);
641                 talloc_free(mem_ctx);
642                 return NT_STATUS_NO_MEMORY;
643         }
644
645         ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 
646                                                       KRB5_AUTHDATA_WIN2K_PAC, 
647                                                       &pac_data);
648         
649         if (ret && gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
650                 DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
651                           principal_string,
652                           smb_get_krb5_error_message(context, 
653                                                      ret, mem_ctx)));
654                 free(principal_string);
655                 krb5_free_principal(context, client_principal);
656                 talloc_free(mem_ctx);
657                 return NT_STATUS_ACCESS_DENIED;
658         } else if (ret) {
659                 /* NO pac */
660                 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 
661                           smb_get_krb5_error_message(context, 
662                                                      ret, mem_ctx)));
663                 if (gensec_security->auth_context && 
664                     !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
665                         DEBUG(1, ("Unable to find PAC for %s, resorting to local user lookup: %s",
666                                   principal_string, smb_get_krb5_error_message(context, 
667                                                      ret, mem_ctx)));
668                         nt_status = gensec_security->auth_context->get_user_info_dc_principal(mem_ctx,
669                                                                                              gensec_security->auth_context, 
670                                                                                              principal_string,
671                                                                                              NULL, &user_info_dc);
672                         if (!NT_STATUS_IS_OK(nt_status)) {
673                                 free(principal_string);
674                                 krb5_free_principal(context, client_principal);
675                                 talloc_free(mem_ctx);
676                                 return nt_status;
677                         }
678                 } else {
679                         DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n",
680                                   principal_string));
681                         free(principal_string);
682                         krb5_free_principal(context, client_principal);
683                         talloc_free(mem_ctx);
684                         return NT_STATUS_ACCESS_DENIED;
685                 }
686         } else {
687                 /* Found pac */
688                 union netr_Validation validation;
689
690                 pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
691                 if (!pac.data) {
692                         free(principal_string);
693                         krb5_free_principal(context, client_principal);
694                         talloc_free(mem_ctx);
695                         return NT_STATUS_NO_MEMORY;
696                 }
697
698                 /* decode and verify the pac */
699                 nt_status = kerberos_pac_logon_info(gensec_krb5_state, 
700                                                     pac,
701                                                     gensec_krb5_state->smb_krb5_context->krb5_context,
702                                                     NULL, gensec_krb5_state->keyblock,
703                                                     client_principal,
704                                                     gensec_krb5_state->ticket->ticket.authtime, &logon_info);
705
706                 if (!NT_STATUS_IS_OK(nt_status)) {
707                         free(principal_string);
708                         krb5_free_principal(context, client_principal);
709                         talloc_free(mem_ctx);
710                         return nt_status;
711                 }
712
713                 validation.sam3 = &logon_info->info3;
714                 nt_status = make_user_info_dc_netlogon_validation(mem_ctx,
715                                                                  NULL,
716                                                                  3, &validation,
717                                                                   true, /* This user was authenticated */
718                                                                  &user_info_dc);
719                 if (!NT_STATUS_IS_OK(nt_status)) {
720                         free(principal_string);
721                         krb5_free_principal(context, client_principal);
722                         talloc_free(mem_ctx);
723                         return nt_status;
724                 }
725         }
726
727         free(principal_string);
728         krb5_free_principal(context, client_principal);
729
730         /* references the user_info_dc into the session_info */
731         nt_status = gensec_generate_session_info(mem_ctx, gensec_security, user_info_dc, &session_info);
732
733         if (!NT_STATUS_IS_OK(nt_status)) {
734                 talloc_free(mem_ctx);
735                 return nt_status;
736         }
737
738         nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
739
740         if (!NT_STATUS_IS_OK(nt_status)) {
741                 talloc_free(mem_ctx);
742                 return nt_status;
743         }
744
745         *_session_info = session_info;
746
747         talloc_steal(gensec_krb5_state, session_info);
748         talloc_free(mem_ctx);
749         return NT_STATUS_OK;
750 }
751
752 static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security, 
753                                    TALLOC_CTX *mem_ctx, 
754                                    const DATA_BLOB *in, 
755                                    DATA_BLOB *out)
756 {
757         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
758         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
759         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
760         krb5_error_code ret;
761         krb5_data input, output;
762         input.length = in->length;
763         input.data = in->data;
764         
765         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
766                 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
767                 if (ret) {
768                         DEBUG(1, ("krb5_mk_priv failed: %s\n", 
769                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
770                                                              ret, mem_ctx)));
771                         return NT_STATUS_ACCESS_DENIED;
772                 }
773                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
774                 
775                 krb5_data_free(&output);
776         } else {
777                 return NT_STATUS_ACCESS_DENIED;
778         }
779         return NT_STATUS_OK;
780 }
781
782 static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, 
783                                      TALLOC_CTX *mem_ctx, 
784                                      const DATA_BLOB *in, 
785                                      DATA_BLOB *out)
786 {
787         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
788         krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
789         krb5_auth_context auth_context = gensec_krb5_state->auth_context;
790         krb5_error_code ret;
791         krb5_data input, output;
792         krb5_replay_data replay;
793         input.length = in->length;
794         input.data = in->data;
795         
796         if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
797                 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
798                 if (ret) {
799                         DEBUG(1, ("krb5_rd_priv failed: %s\n", 
800                                   smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
801                                                              ret, mem_ctx)));
802                         return NT_STATUS_ACCESS_DENIED;
803                 }
804                 *out = data_blob_talloc(mem_ctx, output.data, output.length);
805                 
806                 krb5_data_free(&output);
807         } else {
808                 return NT_STATUS_ACCESS_DENIED;
809         }
810         return NT_STATUS_OK;
811 }
812
813 static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
814                                      uint32_t feature)
815 {
816         struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
817         if (feature & GENSEC_FEATURE_SESSION_KEY) {
818                 return true;
819         } 
820         if (!gensec_krb5_state->gssapi && 
821             (feature & GENSEC_FEATURE_SEAL)) {
822                 return true;
823         } 
824         
825         return false;
826 }
827
828 static const char *gensec_krb5_oids[] = { 
829         GENSEC_OID_KERBEROS5,
830         GENSEC_OID_KERBEROS5_OLD,
831         NULL 
832 };
833
834 static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
835         .name           = "fake_gssapi_krb5",
836         .auth_type      = DCERPC_AUTH_TYPE_KRB5,
837         .oid            = gensec_krb5_oids,
838         .client_start   = gensec_fake_gssapi_krb5_client_start,
839         .server_start   = gensec_fake_gssapi_krb5_server_start,
840         .update         = gensec_krb5_update,
841         .magic          = gensec_fake_gssapi_krb5_magic,
842         .session_key    = gensec_krb5_session_key,
843         .session_info   = gensec_krb5_session_info,
844         .have_feature   = gensec_krb5_have_feature,
845         .enabled        = false,
846         .kerberos       = true,
847         .priority       = GENSEC_KRB5
848 };
849
850 static const struct gensec_security_ops gensec_krb5_security_ops = {
851         .name           = "krb5",
852         .client_start   = gensec_krb5_client_start,
853         .server_start   = gensec_krb5_server_start,
854         .update         = gensec_krb5_update,
855         .session_key    = gensec_krb5_session_key,
856         .session_info   = gensec_krb5_session_info,
857         .have_feature   = gensec_krb5_have_feature,
858         .wrap           = gensec_krb5_wrap,
859         .unwrap         = gensec_krb5_unwrap,
860         .enabled        = true,
861         .kerberos       = true,
862         .priority       = GENSEC_KRB5
863 };
864
865 _PUBLIC_ NTSTATUS gensec_krb5_init(void)
866 {
867         NTSTATUS ret;
868
869         ret = gensec_register(&gensec_krb5_security_ops);
870         if (!NT_STATUS_IS_OK(ret)) {
871                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
872                         gensec_krb5_security_ops.name));
873                 return ret;
874         }
875
876         ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
877         if (!NT_STATUS_IS_OK(ret)) {
878                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
879                         gensec_krb5_security_ops.name));
880                 return ret;
881         }
882
883         return ret;
884 }