f4f4b4f1898543446be6ce79fc5af6e0176266a9
[vlendec/samba-autobuild/.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 "auth/gensec/gensec.h"
33 #include "auth/gensec/gensec_internal.h" /* TODO: remove this */
34 #include "../libcli/auth/spnego.h"
35
36 #ifdef HAVE_KRB5
37
38 #include "auth/kerberos/pac_utils.h"
39
40 struct smb_krb5_context;
41
42 /****************************************************************
43 Callback to get the PAC_LOGON_INFO from the blob
44 ****************************************************************/
45 static NTSTATUS kerberos_fetch_pac(struct auth4_context *auth_ctx,
46                                    TALLOC_CTX *mem_ctx,
47                                    struct smb_krb5_context *smb_krb5_context,
48                                    DATA_BLOB *pac_blob,
49                                    const char *princ_name,
50                                    const struct tsocket_address *remote_address,
51                                    uint32_t session_info_flags,
52                                    struct auth_session_info **session_info)
53 {
54         TALLOC_CTX *tmp_ctx;
55         struct PAC_DATA *pac_data = NULL;
56         struct PAC_DATA_CTR *pac_data_ctr = NULL;
57         NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
58
59         tmp_ctx = talloc_new(mem_ctx);
60         if (!tmp_ctx) {
61                 return NT_STATUS_NO_MEMORY;
62         }
63
64         if (pac_blob != NULL) {
65                 status = kerberos_decode_pac(tmp_ctx,
66                                              *pac_blob,
67                                              NULL,
68                                              NULL,
69                                              NULL,
70                                              NULL,
71                                              0,
72                                              &pac_data);
73                 if (!NT_STATUS_IS_OK(status)) {
74                         goto done;
75                 }
76
77                 pac_data_ctr = talloc(mem_ctx, struct PAC_DATA_CTR);
78                 if (pac_data_ctr == NULL) {
79                         status = NT_STATUS_NO_MEMORY;
80                         goto done;
81                 }
82
83                 talloc_set_name_const(pac_data_ctr, "struct PAC_DATA_CTR");
84
85                 pac_data_ctr->pac_data = talloc_steal(pac_data_ctr, pac_data);
86                 pac_data_ctr->pac_blob = data_blob_talloc(pac_data_ctr,
87                                                           pac_blob->data,
88                                                           pac_blob->length);
89
90                 auth_ctx->private_data = talloc_steal(auth_ctx, pac_data_ctr);
91         }
92
93         *session_info = talloc_zero(mem_ctx, struct auth_session_info);
94         if (!*session_info) {
95                 status = NT_STATUS_NO_MEMORY;
96                 goto done;
97         }
98         status = NT_STATUS_OK;
99
100 done:
101         TALLOC_FREE(tmp_ctx);
102
103         return status;
104 }
105
106 /*
107  * Given the username/password, do a kinit, store the ticket in
108  * cache_name if specified, and return the PAC_LOGON_INFO (the
109  * structure containing the important user information such as
110  * groups).
111  */
112 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
113                              const char *name,
114                              const char *pass,
115                              time_t time_offset,
116                              time_t *expire_time,
117                              time_t *renew_till_time,
118                              const char *cache_name,
119                              bool request_pac,
120                              bool add_netbios_addr,
121                              time_t renewable_time,
122                              const char *impersonate_princ_s,
123                              const char *local_service,
124                              struct PAC_DATA_CTR **_pac_data_ctr)
125 {
126         krb5_error_code ret;
127         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
128         DATA_BLOB tkt, tkt_wrapped, ap_rep, sesskey1;
129         const char *auth_princ = NULL;
130         const char *cc = "MEMORY:kerberos_return_pac";
131         struct auth_session_info *session_info;
132         struct gensec_security *gensec_server_context;
133         const struct gensec_security_ops **backends;
134         struct gensec_settings *gensec_settings;
135         size_t idx = 0;
136         struct auth4_context *auth_context;
137         struct loadparm_context *lp_ctx;
138         struct PAC_DATA_CTR *pac_data_ctr = NULL;
139
140         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
141         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
142
143         ZERO_STRUCT(tkt);
144         ZERO_STRUCT(ap_rep);
145         ZERO_STRUCT(sesskey1);
146
147         if (!name || !pass) {
148                 return NT_STATUS_INVALID_PARAMETER;
149         }
150
151         if (cache_name) {
152                 cc = cache_name;
153         }
154
155         if (!strchr_m(name, '@')) {
156                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
157                         lp_realm());
158         } else {
159                 auth_princ = name;
160         }
161         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
162
163         ret = kerberos_kinit_password_ext(auth_princ,
164                                           pass,
165                                           time_offset,
166                                           expire_time,
167                                           renew_till_time,
168                                           cc,
169                                           request_pac,
170                                           add_netbios_addr,
171                                           renewable_time,
172                                           &status);
173         if (ret) {
174                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
175                         auth_princ, error_message(ret), ret));
176                 /* status already set */
177                 goto out;
178         }
179
180         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
181         if (expire_time) {
182                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
183                         http_timestring(talloc_tos(), *expire_time),
184                         (int)*expire_time));
185         }
186         if (renew_till_time) {
187                 DEBUGADD(10,("\trenewable till: %s (%d)\n",
188                         http_timestring(talloc_tos(), *renew_till_time),
189                         (int)*renew_till_time));
190         }
191
192         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
193          * in that case fallback to NTLM - gd */
194
195         if (expire_time && renew_till_time &&
196             (*expire_time == 0) && (*renew_till_time == 0)) {
197                 return NT_STATUS_INVALID_LOGON_TYPE;
198         }
199
200         ret = ads_krb5_cli_get_ticket(mem_ctx,
201                                       local_service,
202                                       time_offset,
203                                       &tkt,
204                                       &sesskey1,
205                                       0,
206                                       cc,
207                                       NULL,
208                                       impersonate_princ_s);
209         if (ret) {
210                 DEBUG(1,("failed to get ticket for %s: %s\n",
211                         local_service, error_message(ret)));
212                 if (impersonate_princ_s) {
213                         DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
214                                 impersonate_princ_s));
215                 }
216                 status = krb5_to_nt_status(ret);
217                 goto out;
218         }
219
220         /* wrap that up in a nice GSS-API wrapping */
221         tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ);
222         if (tkt_wrapped.data == NULL) {
223                 status = NT_STATUS_NO_MEMORY;
224                 goto out;
225         }
226
227         auth_context = talloc_zero(tmp_ctx, struct auth4_context);
228         if (auth_context == NULL) {
229                 status = NT_STATUS_NO_MEMORY;
230                 goto out;
231         }
232         auth_context->generate_session_info_pac = kerberos_fetch_pac;
233
234         lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_helpers());
235         if (lp_ctx == NULL) {
236                 status = NT_STATUS_INVALID_SERVER_STATE;
237                 DEBUG(10, ("loadparm_init_s3 failed\n"));
238                 goto out;
239         }
240
241         gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
242         if (gensec_settings == NULL) {
243                 status = NT_STATUS_NO_MEMORY;
244                 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
245                 goto out;
246         }
247
248         backends = talloc_zero_array(gensec_settings,
249                                      const struct gensec_security_ops *, 2);
250         if (backends == NULL) {
251                 status = NT_STATUS_NO_MEMORY;
252                 goto out;
253         }
254         gensec_settings->backends = backends;
255
256         gensec_init();
257
258         backends[idx++] = &gensec_gse_krb5_security_ops;
259
260         status = gensec_server_start(tmp_ctx, gensec_settings,
261                                         auth_context, &gensec_server_context);
262
263         if (!NT_STATUS_IS_OK(status)) {
264                 DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status)));
265                 goto out;
266         }
267
268         talloc_unlink(tmp_ctx, lp_ctx);
269         talloc_unlink(tmp_ctx, gensec_settings);
270         talloc_unlink(tmp_ctx, auth_context);
271
272         status = gensec_start_mech_by_oid(gensec_server_context, GENSEC_OID_KERBEROS5);
273         if (!NT_STATUS_IS_OK(status)) {
274                 DEBUG(1, (__location__ "Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s\n", nt_errstr(status)));
275                 goto out;
276         }
277
278         /* Do a client-server update dance */
279         status = gensec_update(gensec_server_context, tmp_ctx, tkt_wrapped, &ap_rep);
280         if (!NT_STATUS_IS_OK(status)) {
281                 DEBUG(1, ("gensec_update() failed: %s\n", nt_errstr(status)));
282                 goto out;
283         }
284
285         /* Now return the PAC information to the callers.  We ingore
286          * the session_info and instead pick out the PAC via the
287          * private_data on the auth_context */
288         status = gensec_session_info(gensec_server_context, tmp_ctx, &session_info);
289         if (!NT_STATUS_IS_OK(status)) {
290                 DEBUG(1, ("Unable to obtain PAC via gensec_session_info\n"));
291                 goto out;
292         }
293
294         pac_data_ctr = talloc_get_type_abort(gensec_server_context->auth_context->private_data,
295                                              struct PAC_DATA_CTR);
296         if (pac_data_ctr == NULL) {
297                 DEBUG(1,("no PAC\n"));
298                 status = NT_STATUS_INVALID_PARAMETER;
299                 goto out;
300         }
301
302         *_pac_data_ctr = talloc_move(mem_ctx, &pac_data_ctr);
303
304 out:
305         talloc_free(tmp_ctx);
306         if (cc != cache_name) {
307                 ads_kdestroy(cc);
308         }
309
310         data_blob_free(&tkt);
311         data_blob_free(&ap_rep);
312         data_blob_free(&sesskey1);
313
314         return status;
315 }
316
317 #endif