2 Unix SMB/CIFS implementation.
3 kerberos authorization data (PAC) utility library
4 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Luke Howard 2002-2003
8 Copyright (C) Stefan Metzmacher 2004-2005
9 Copyright (C) Guenther Deschner 2005,2007,2008
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.
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.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "librpc/gen_ndr/ndr_krb5pac.h"
28 #include "libads/kerberos_proto.h"
29 #include "auth/common_auth.h"
30 #include "lib/param/param.h"
31 #include "librpc/crypto/gse.h"
32 #include "librpc/crypto/gse_krb5.h"
33 #include "auth/credentials/credentials.h"
34 #include "../source4/auth/kerberos/krb5_init_context.h"
35 #include "auth/gensec/gensec.h"
36 #include "auth/gensec/gensec_internal.h" /* TODO: remove this */
37 #include "../libcli/auth/spnego.h"
41 #include "auth/kerberos/pac_utils.h"
43 struct smb_krb5_context;
46 * Given the username/password, do a kinit, store the ticket in
47 * cache_name if specified, and return the PAC_LOGON_INFO (the
48 * structure containing the important user information such as
51 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
56 time_t *renew_till_time,
57 const char *cache_name,
59 bool add_netbios_addr,
60 time_t renewable_time,
61 const char *impersonate_princ_s,
62 const char *local_service,
63 struct PAC_DATA_CTR **_pac_data_ctr)
66 NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
67 DATA_BLOB tkt, tkt_wrapped, ap_rep, sesskey1;
68 const char *auth_princ = NULL;
69 const char *cc = "MEMORY:kerberos_return_pac";
70 struct auth_session_info *session_info;
71 struct gensec_security *gensec_server_context;
72 const struct gensec_security_ops **backends;
73 struct gensec_settings *gensec_settings;
75 struct auth4_context *auth_context;
76 struct loadparm_context *lp_ctx;
77 struct cli_credentials *server_credentials = NULL;
78 struct PAC_DATA_CTR *pac_data_ctr = NULL;
80 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
81 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
85 ZERO_STRUCT(sesskey1);
88 return NT_STATUS_INVALID_PARAMETER;
95 if (!strchr_m(name, '@')) {
96 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
101 NT_STATUS_HAVE_NO_MEMORY(auth_princ);
103 ret = kerberos_kinit_password_ext(auth_princ,
115 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
116 auth_princ, error_message(ret), ret));
117 /* status already set */
121 DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
123 DEBUGADD(10,("\tvalid until: %s (%d)\n",
124 http_timestring(talloc_tos(), *expire_time),
127 if (renew_till_time) {
128 DEBUGADD(10,("\trenewable till: %s (%d)\n",
129 http_timestring(talloc_tos(), *renew_till_time),
130 (int)*renew_till_time));
133 /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
134 * in that case fallback to NTLM - gd */
136 if (expire_time && renew_till_time &&
137 (*expire_time == 0) && (*renew_till_time == 0)) {
138 return NT_STATUS_INVALID_LOGON_TYPE;
141 ret = ads_krb5_cli_get_ticket(mem_ctx,
149 impersonate_princ_s);
151 DEBUG(1,("failed to get ticket for %s: %s\n",
152 local_service, error_message(ret)));
153 if (impersonate_princ_s) {
154 DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
155 impersonate_princ_s));
157 status = krb5_to_nt_status(ret);
161 /* wrap that up in a nice GSS-API wrapping */
162 tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ);
163 if (tkt_wrapped.data == NULL) {
164 status = NT_STATUS_NO_MEMORY;
168 auth_context = auth4_context_for_PAC_DATA_CTR(tmp_ctx);
169 if (auth_context == NULL) {
170 status = NT_STATUS_NO_MEMORY;
174 lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_helpers());
175 if (lp_ctx == NULL) {
176 status = NT_STATUS_INVALID_SERVER_STATE;
177 DEBUG(10, ("loadparm_init_s3 failed\n"));
181 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
182 if (gensec_settings == NULL) {
183 status = NT_STATUS_NO_MEMORY;
184 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
188 backends = talloc_zero_array(gensec_settings,
189 const struct gensec_security_ops *, 2);
190 if (backends == NULL) {
191 status = NT_STATUS_NO_MEMORY;
194 gensec_settings->backends = backends;
198 //backends[idx++] = &gensec_gse_krb5_security_ops;
199 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
202 * This is anonymous for now, because we just use it
203 * to set the kerberos state at the moment
205 server_credentials = cli_credentials_init_anon(tmp_ctx);
206 if (!server_credentials) {
207 DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n"));
208 status = NT_STATUS_NO_MEMORY;
212 cli_credentials_set_conf(server_credentials, lp_ctx);
215 struct smb_krb5_context *smb_k5ctx = NULL;
216 char *keytab_name = NULL;
217 krb5_error_code k5ret;
218 krb5_keytab k5keytab = NULL;
220 keytab_name = talloc_asprintf(server_credentials,
221 "MEMORY:kerberos_return_pac_%u_%p",
222 (unsigned int)getpid(),
224 if (keytab_name == NULL) {
225 status = NT_STATUS_NO_MEMORY;
229 k5ret = cli_credentials_get_krb5_context(server_credentials,
232 status = krb5_to_nt_status(k5ret);
233 if (!NT_STATUS_IS_OK(status)) {
237 k5ret = gse_krb5_create_server_keytab(smb_k5ctx->krb5_context,
240 status = krb5_to_nt_status(k5ret);
241 if (!NT_STATUS_IS_OK(status)) {
245 k5ret = cli_credentials_set_keytab_name(server_credentials,
249 krb5_kt_close(smb_k5ctx->krb5_context, k5keytab);
250 status = krb5_to_nt_status(k5ret);
251 if (!NT_STATUS_IS_OK(status)) {
255 cli_credentials_set_kerberos_state(server_credentials, CRED_MUST_USE_KERBEROS);
258 status = gensec_server_start(tmp_ctx, gensec_settings,
259 auth_context, &gensec_server_context);
261 if (!NT_STATUS_IS_OK(status)) {
262 DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status)));
266 gensec_set_credentials(gensec_server_context, server_credentials);
268 talloc_unlink(tmp_ctx, lp_ctx);
269 talloc_unlink(tmp_ctx, server_credentials);
270 talloc_unlink(tmp_ctx, gensec_settings);
271 talloc_unlink(tmp_ctx, auth_context);
273 /* Session info is not complete, do not pass to auth log */
274 gensec_want_feature(gensec_server_context, GENSEC_FEATURE_NO_AUTHZ_LOG);
276 status = gensec_start_mech_by_oid(gensec_server_context, GENSEC_OID_KERBEROS5);
277 if (!NT_STATUS_IS_OK(status)) {
278 DEBUG(1, (__location__ "Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s\n", nt_errstr(status)));
282 /* Do a client-server update dance */
283 status = gensec_update(gensec_server_context, tmp_ctx, tkt_wrapped, &ap_rep);
284 if (!NT_STATUS_IS_OK(status)) {
285 DEBUG(1, ("gensec_update() failed: %s\n", nt_errstr(status)));
289 /* Now return the PAC information to the callers. We ingore
290 * the session_info and instead pick out the PAC via the
291 * private_data on the auth_context */
292 status = gensec_session_info(gensec_server_context, tmp_ctx, &session_info);
293 if (!NT_STATUS_IS_OK(status)) {
294 DEBUG(1, ("Unable to obtain PAC via gensec_session_info\n"));
298 pac_data_ctr = auth4_context_get_PAC_DATA_CTR(auth_context, mem_ctx);
299 if (pac_data_ctr == NULL) {
300 DEBUG(1,("no PAC\n"));
301 status = NT_STATUS_INVALID_PARAMETER;
305 *_pac_data_ctr = talloc_move(mem_ctx, &pac_data_ctr);
308 talloc_free(tmp_ctx);
309 if (cc != cache_name) {
313 data_blob_free(&tkt);
314 data_blob_free(&ap_rep);
315 data_blob_free(&sesskey1);