kerberos_return_pac backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
[metze/samba/wip.git] / source3 / libads / authdata.c
1 /*
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
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    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/>.
23 */
24
25 #include "includes.h"
26 #include "librpc/gen_ndr/ndr_krb5pac.h"
27 #include "smb_krb5.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"
38
39 #ifdef HAVE_KRB5
40
41 #include "auth/kerberos/pac_utils.h"
42
43 struct smb_krb5_context;
44
45 /*
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
49  * groups).
50  */
51 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
52                              const char *name,
53                              const char *pass,
54                              time_t time_offset,
55                              time_t *expire_time,
56                              time_t *renew_till_time,
57                              const char *cache_name,
58                              bool request_pac,
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)
64 {
65         krb5_error_code ret;
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;
74         size_t idx = 0;
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;
79
80         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
81         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
82
83         ZERO_STRUCT(tkt);
84         ZERO_STRUCT(ap_rep);
85         ZERO_STRUCT(sesskey1);
86
87         if (!name || !pass) {
88                 return NT_STATUS_INVALID_PARAMETER;
89         }
90
91         if (cache_name) {
92                 cc = cache_name;
93         }
94
95         if (!strchr_m(name, '@')) {
96                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
97                         lp_realm());
98         } else {
99                 auth_princ = name;
100         }
101         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
102
103         ret = kerberos_kinit_password_ext(auth_princ,
104                                           pass,
105                                           time_offset,
106                                           expire_time,
107                                           renew_till_time,
108                                           cc,
109                                           request_pac,
110                                           add_netbios_addr,
111                                           renewable_time,
112                                           NULL, NULL, NULL,
113                                           &status);
114         if (ret) {
115                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
116                         auth_princ, error_message(ret), ret));
117                 /* status already set */
118                 goto out;
119         }
120
121         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
122         if (expire_time) {
123                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
124                         http_timestring(talloc_tos(), *expire_time),
125                         (int)*expire_time));
126         }
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));
131         }
132
133         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
134          * in that case fallback to NTLM - gd */
135
136         if (expire_time && renew_till_time &&
137             (*expire_time == 0) && (*renew_till_time == 0)) {
138                 return NT_STATUS_INVALID_LOGON_TYPE;
139         }
140
141         ret = ads_krb5_cli_get_ticket(mem_ctx,
142                                       local_service,
143                                       time_offset,
144                                       &tkt,
145                                       &sesskey1,
146                                       0,
147                                       cc,
148                                       NULL,
149                                       impersonate_princ_s);
150         if (ret) {
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));
156                 }
157                 status = krb5_to_nt_status(ret);
158                 goto out;
159         }
160
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;
165                 goto out;
166         }
167
168         auth_context = auth4_context_for_PAC_DATA_CTR(tmp_ctx);
169         if (auth_context == NULL) {
170                 status = NT_STATUS_NO_MEMORY;
171                 goto out;
172         }
173
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"));
178                 goto out;
179         }
180
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"));
185                 goto out;
186         }
187
188         backends = talloc_zero_array(gensec_settings,
189                                      const struct gensec_security_ops *, 2);
190         if (backends == NULL) {
191                 status = NT_STATUS_NO_MEMORY;
192                 goto out;
193         }
194         gensec_settings->backends = backends;
195
196         gensec_init();
197
198         //backends[idx++] = &gensec_gse_krb5_security_ops;
199         backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
200
201         /*
202          * This is anonymous for now, because we just use it
203          * to set the kerberos state at the moment
204          */
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;
209                 goto out;
210         }
211
212         cli_credentials_set_conf(server_credentials, lp_ctx);
213
214         {
215                 struct smb_krb5_context *smb_k5ctx = NULL;
216                 char *keytab_name = NULL;
217                 krb5_error_code k5ret;
218                 krb5_keytab k5keytab = NULL;
219
220                 keytab_name = talloc_asprintf(server_credentials,
221                                 "MEMORY:kerberos_return_pac_%u_%p",
222                                 (unsigned int)getpid(),
223                                 server_credentials);
224                 if (keytab_name == NULL) {
225                         status = NT_STATUS_NO_MEMORY;
226                         goto out;
227                 }
228
229                 k5ret = cli_credentials_get_krb5_context(server_credentials,
230                                                          lp_ctx,
231                                                          &smb_k5ctx);
232                 status = krb5_to_nt_status(k5ret);
233                 if (!NT_STATUS_IS_OK(status)) {
234                         goto out;
235                 }
236
237                 k5ret = gse_krb5_create_server_keytab(smb_k5ctx->krb5_context,
238                                                       keytab_name,
239                                                       &k5keytab);
240                 status = krb5_to_nt_status(k5ret);
241                 if (!NT_STATUS_IS_OK(status)) {
242                         goto out;
243                 }
244
245                 k5ret = cli_credentials_set_keytab_name(server_credentials,
246                                                         lp_ctx,
247                                                         keytab_name,
248                                                         CRED_SPECIFIED);
249                 krb5_kt_close(smb_k5ctx->krb5_context, k5keytab);
250                 status = krb5_to_nt_status(k5ret);
251                 if (!NT_STATUS_IS_OK(status)) {
252                         goto out;
253                 }
254
255                 cli_credentials_set_kerberos_state(server_credentials, CRED_MUST_USE_KERBEROS);
256         }
257
258         status = gensec_server_start(tmp_ctx, gensec_settings,
259                                         auth_context, &gensec_server_context);
260
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)));
263                 goto out;
264         }
265
266         gensec_set_credentials(gensec_server_context, server_credentials);
267
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);
272
273         /* Session info is not complete, do not pass to auth log */
274         gensec_want_feature(gensec_server_context, GENSEC_FEATURE_NO_AUTHZ_LOG);
275
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)));
279                 goto out;
280         }
281
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)));
286                 goto out;
287         }
288
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"));
295                 goto out;
296         }
297
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;
302                 goto out;
303         }
304
305         *_pac_data_ctr = talloc_move(mem_ctx, &pac_data_ctr);
306
307 out:
308         talloc_free(tmp_ctx);
309         if (cc != cache_name) {
310                 ads_kdestroy(cc);
311         }
312
313         data_blob_free(&tkt);
314         data_blob_free(&ap_rep);
315         data_blob_free(&sesskey1);
316
317         return status;
318 }
319
320 #endif