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