libads: Give krb5_errs.c its own header
[samba.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                                           &status);
174         if (ret) {
175                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
176                         auth_princ, error_message(ret), ret));
177                 /* status already set */
178                 goto out;
179         }
180
181         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
182         if (expire_time) {
183                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
184                         http_timestring(talloc_tos(), *expire_time),
185                         (int)*expire_time));
186         }
187         if (renew_till_time) {
188                 DEBUGADD(10,("\trenewable till: %s (%d)\n",
189                         http_timestring(talloc_tos(), *renew_till_time),
190                         (int)*renew_till_time));
191         }
192
193         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
194          * in that case fallback to NTLM - gd */
195
196         if (expire_time && renew_till_time &&
197             (*expire_time == 0) && (*renew_till_time == 0)) {
198                 return NT_STATUS_INVALID_LOGON_TYPE;
199         }
200
201         ret = ads_krb5_cli_get_ticket(mem_ctx,
202                                       local_service,
203                                       time_offset,
204                                       &tkt,
205                                       &sesskey1,
206                                       0,
207                                       cc,
208                                       NULL,
209                                       impersonate_princ_s);
210         if (ret) {
211                 DEBUG(1,("failed to get ticket for %s: %s\n",
212                         local_service, error_message(ret)));
213                 if (impersonate_princ_s) {
214                         DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
215                                 impersonate_princ_s));
216                 }
217                 status = krb5_to_nt_status(ret);
218                 goto out;
219         }
220
221         /* wrap that up in a nice GSS-API wrapping */
222         tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ);
223         if (tkt_wrapped.data == NULL) {
224                 status = NT_STATUS_NO_MEMORY;
225                 goto out;
226         }
227
228         auth_context = talloc_zero(tmp_ctx, struct auth4_context);
229         if (auth_context == NULL) {
230                 status = NT_STATUS_NO_MEMORY;
231                 goto out;
232         }
233         auth_context->generate_session_info_pac = kerberos_fetch_pac;
234
235         lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_helpers());
236         if (lp_ctx == NULL) {
237                 status = NT_STATUS_INVALID_SERVER_STATE;
238                 DEBUG(10, ("loadparm_init_s3 failed\n"));
239                 goto out;
240         }
241
242         gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
243         if (gensec_settings == NULL) {
244                 status = NT_STATUS_NO_MEMORY;
245                 DEBUG(10, ("lpcfg_gensec_settings failed\n"));
246                 goto out;
247         }
248
249         backends = talloc_zero_array(gensec_settings,
250                                      const struct gensec_security_ops *, 2);
251         if (backends == NULL) {
252                 status = NT_STATUS_NO_MEMORY;
253                 goto out;
254         }
255         gensec_settings->backends = backends;
256
257         gensec_init();
258
259         backends[idx++] = &gensec_gse_krb5_security_ops;
260
261         status = gensec_server_start(tmp_ctx, gensec_settings,
262                                         auth_context, &gensec_server_context);
263
264         if (!NT_STATUS_IS_OK(status)) {
265                 DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status)));
266                 goto out;
267         }
268
269         talloc_unlink(tmp_ctx, lp_ctx);
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 = talloc_get_type_abort(gensec_server_context->auth_context->private_data,
299                                              struct PAC_DATA_CTR);
300         if (pac_data_ctr == NULL) {
301                 DEBUG(1,("no PAC\n"));
302                 status = NT_STATUS_INVALID_PARAMETER;
303                 goto out;
304         }
305
306         *_pac_data_ctr = talloc_move(mem_ctx, &pac_data_ctr);
307
308 out:
309         talloc_free(tmp_ctx);
310         if (cc != cache_name) {
311                 ads_kdestroy(cc);
312         }
313
314         data_blob_free(&tkt);
315         data_blob_free(&ap_rep);
316         data_blob_free(&sesskey1);
317
318         return status;
319 }
320
321 #endif