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