s3:kerberos Return PAC_LOGON_INFO rather than the full PAC_DATA
[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 "authdata.h"
29
30 #ifdef HAVE_KRB5
31
32 /****************************************************************
33 ****************************************************************/
34
35 static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
36                                           DATA_BLOB pac_data,
37                                           struct PAC_SIGNATURE_DATA *sig,
38                                           krb5_context context,
39                                           krb5_keyblock *keyblock)
40 {
41         krb5_error_code ret;
42         krb5_checksum cksum;
43         krb5_keyusage usage = 0;
44
45         smb_krb5_checksum_from_pac_sig(&cksum, sig);
46
47 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
48         usage = KRB5_KU_OTHER_CKSUM;
49 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
50         usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
51 #else
52 #error UNKNOWN_KRB5_KEYUSAGE
53 #endif
54
55         ret = smb_krb5_verify_checksum(context,
56                                        keyblock,
57                                        usage,
58                                        &cksum,
59                                        pac_data.data,
60                                        pac_data.length);
61
62         if (ret) {
63                 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n",
64                         error_message(ret), ret));
65                 return ret;
66         }
67
68         return ret;
69 }
70
71 /****************************************************************
72 ****************************************************************/
73
74  NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx,
75                          DATA_BLOB *pac_data_blob,
76                          krb5_context context,
77                          krb5_keyblock *service_keyblock,
78                          krb5_const_principal client_principal,
79                          time_t tgs_authtime,
80                          struct PAC_DATA **pac_data_out)
81 {
82         NTSTATUS status;
83         enum ndr_err_code ndr_err;
84         krb5_error_code ret;
85         DATA_BLOB modified_pac_blob;
86
87         NTTIME tgs_authtime_nttime;
88         krb5_principal client_principal_pac = NULL;
89         int i;
90
91         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
92         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
93         struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
94         struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
95         struct PAC_LOGON_NAME *logon_name = NULL;
96         struct PAC_LOGON_INFO *logon_info = NULL;
97         struct PAC_DATA *pac_data = NULL;
98         struct PAC_DATA_RAW *pac_data_raw = NULL;
99
100         DATA_BLOB *srv_sig_blob = NULL;
101         DATA_BLOB *kdc_sig_blob = NULL;
102
103         bool bool_ret;
104
105         *pac_data_out = NULL;
106
107         pac_data = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA);
108         pac_data_raw = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA_RAW);
109         kdc_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
110         srv_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
111         if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
112                 return NT_STATUS_NO_MEMORY;
113         }
114
115         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data,
116                         NULL, pac_data,
117                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
118         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
119                 status = ndr_map_error2ntstatus(ndr_err);
120                 DEBUG(0,("can't parse the PAC: %s\n",
121                         nt_errstr(status)));
122                 return status;
123         }
124
125         if (pac_data->num_buffers < 4) {
126                 /* we need logon_ingo, service_key and kdc_key */
127                 DEBUG(0,("less than 4 PAC buffers\n"));
128                 return NT_STATUS_INVALID_PARAMETER;
129         }
130
131         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data_raw,
132                                        NULL, pac_data_raw,
133                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
134         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
135                 status = ndr_map_error2ntstatus(ndr_err);
136                 DEBUG(0,("can't parse the PAC: %s\n",
137                         nt_errstr(status)));
138                 return status;
139         }
140
141         if (pac_data_raw->num_buffers < 4) {
142                 /* we need logon_ingo, service_key and kdc_key */
143                 DEBUG(0,("less than 4 PAC buffers\n"));
144                 return NT_STATUS_INVALID_PARAMETER;
145         }
146
147         if (pac_data->num_buffers != pac_data_raw->num_buffers) {
148                 /* we need logon_ingo, service_key and kdc_key */
149                 DEBUG(0,("misparse!  PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
150                          pac_data->num_buffers, pac_data_raw->num_buffers));
151                 return NT_STATUS_INVALID_PARAMETER;
152         }
153
154         for (i=0; i < pac_data->num_buffers; i++) {
155                 if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
156                         DEBUG(0,("misparse!  PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
157                                  i, pac_data->buffers[i].type, pac_data->buffers[i].type));
158                         return NT_STATUS_INVALID_PARAMETER;
159                 }
160                 switch (pac_data->buffers[i].type) {
161                         case PAC_TYPE_LOGON_INFO:
162                                 if (!pac_data->buffers[i].info) {
163                                         break;
164                                 }
165                                 logon_info = pac_data->buffers[i].info->logon_info.info;
166                                 break;
167                         case PAC_TYPE_SRV_CHECKSUM:
168                                 if (!pac_data->buffers[i].info) {
169                                         break;
170                                 }
171                                 srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
172                                 srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
173                                 break;
174                         case PAC_TYPE_KDC_CHECKSUM:
175                                 if (!pac_data->buffers[i].info) {
176                                         break;
177                                 }
178                                 kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
179                                 kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
180                                 break;
181                         case PAC_TYPE_LOGON_NAME:
182                                 logon_name = &pac_data->buffers[i].info->logon_name;
183                                 break;
184                         default:
185                                 break;
186                 }
187         }
188
189         if (!logon_info) {
190                 DEBUG(0,("PAC no logon_info\n"));
191                 return NT_STATUS_INVALID_PARAMETER;
192         }
193
194         if (!logon_name) {
195                 DEBUG(0,("PAC no logon_name\n"));
196                 return NT_STATUS_INVALID_PARAMETER;
197         }
198
199         if (!srv_sig_ptr || !srv_sig_blob) {
200                 DEBUG(0,("PAC no srv_key\n"));
201                 return NT_STATUS_INVALID_PARAMETER;
202         }
203
204         if (!kdc_sig_ptr || !kdc_sig_blob) {
205                 DEBUG(0,("PAC no kdc_key\n"));
206                 return NT_STATUS_INVALID_PARAMETER;
207         }
208
209         /* Find and zero out the signatures, as required by the signing algorithm */
210
211         /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
212         ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe,
213                                        NULL, kdc_sig_wipe,
214                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
215         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
216                 status = ndr_map_error2ntstatus(ndr_err);
217                 DEBUG(0,("can't parse the KDC signature: %s\n",
218                         nt_errstr(status)));
219                 return status;
220         }
221
222         ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe,
223                                        NULL, srv_sig_wipe,
224                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
225         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
226                 status = ndr_map_error2ntstatus(ndr_err);
227                 DEBUG(0,("can't parse the SRV signature: %s\n",
228                         nt_errstr(status)));
229                 return status;
230         }
231
232         /* Now zero the decoded structure */
233         memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
234         memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
235
236         /* and reencode, back into the same place it came from */
237         ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw,
238                                        NULL, kdc_sig_wipe,
239                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
240         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
241                 status = ndr_map_error2ntstatus(ndr_err);
242                 DEBUG(0,("can't repack the KDC signature: %s\n",
243                         nt_errstr(status)));
244                 return status;
245         }
246         ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw,
247                                        NULL, srv_sig_wipe,
248                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
249         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
250                 status = ndr_map_error2ntstatus(ndr_err);
251                 DEBUG(0,("can't repack the SRV signature: %s\n",
252                         nt_errstr(status)));
253                 return status;
254         }
255
256         /* push out the whole structure, but now with zero'ed signatures */
257         ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw,
258                                        NULL, pac_data_raw,
259                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
260         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
261                 status = ndr_map_error2ntstatus(ndr_err);
262                 DEBUG(0,("can't repack the RAW PAC: %s\n",
263                         nt_errstr(status)));
264                 return status;
265         }
266
267         /* verify by service_key */
268         ret = check_pac_checksum(mem_ctx,
269                                  modified_pac_blob, srv_sig_ptr,
270                                  context,
271                                  service_keyblock);
272         if (ret) {
273                 DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
274                           error_message(ret)));
275                 return NT_STATUS_ACCESS_DENIED;
276         }
277
278         /* Convert to NT time, so as not to loose accuracy in comparison */
279         unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
280
281         if (tgs_authtime_nttime != logon_name->logon_time) {
282                 DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
283                 DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
284                 DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
285                 return NT_STATUS_ACCESS_DENIED;
286         }
287
288         ret = smb_krb5_parse_name_norealm(context, logon_name->account_name,
289                                     &client_principal_pac);
290         if (ret) {
291                 DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
292                           logon_name->account_name,
293                           error_message(ret)));
294                 return NT_STATUS_INVALID_PARAMETER;
295         }
296
297         bool_ret = smb_krb5_principal_compare_any_realm(
298                 context, client_principal, client_principal_pac);
299
300         krb5_free_principal(context, client_principal_pac);
301
302         if (!bool_ret) {
303                 DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
304                           logon_name->account_name));
305                 return NT_STATUS_ACCESS_DENIED;
306         }
307
308         DEBUG(3,("Found account name from PAC: %s [%s]\n",
309                  logon_info->info3.base.account_name.string,
310                  logon_info->info3.base.full_name.string));
311
312         DEBUG(10,("Successfully validated Kerberos PAC\n"));
313
314         if (DEBUGLEVEL >= 10) {
315                 const char *s;
316                 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_DATA, pac_data);
317                 if (s) {
318                         DEBUGADD(10,("%s\n", s));
319                 }
320         }
321
322         *pac_data_out = pac_data;
323
324         return NT_STATUS_OK;
325 }
326
327 /****************************************************************
328 Given a username, password and other details, return the
329 PAC_LOGON_INFO (the structure containing the important user
330 information such as groups).
331 ****************************************************************/
332
333 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
334                              const char *name,
335                              const char *pass,
336                              time_t time_offset,
337                              time_t *expire_time,
338                              time_t *renew_till_time,
339                              const char *cache_name,
340                              bool request_pac,
341                              bool add_netbios_addr,
342                              time_t renewable_time,
343                              const char *impersonate_princ_s,
344                              struct PAC_LOGON_INFO **logon_info)
345 {
346         krb5_error_code ret;
347         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
348         DATA_BLOB tkt, ap_rep, sesskey1, sesskey2;
349         char *client_princ_out = NULL;
350         const char *auth_princ = NULL;
351         const char *local_service = NULL;
352         const char *cc = "MEMORY:kerberos_return_pac";
353
354         ZERO_STRUCT(tkt);
355         ZERO_STRUCT(ap_rep);
356         ZERO_STRUCT(sesskey1);
357         ZERO_STRUCT(sesskey2);
358
359         if (!name || !pass) {
360                 return NT_STATUS_INVALID_PARAMETER;
361         }
362
363         if (cache_name) {
364                 cc = cache_name;
365         }
366
367         if (!strchr_m(name, '@')) {
368                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
369                         lp_realm());
370         } else {
371                 auth_princ = name;
372         }
373         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
374
375         local_service = talloc_asprintf(mem_ctx, "%s$@%s",
376                                         global_myname(), lp_realm());
377         NT_STATUS_HAVE_NO_MEMORY(local_service);
378
379         ret = kerberos_kinit_password_ext(auth_princ,
380                                           pass,
381                                           time_offset,
382                                           expire_time,
383                                           renew_till_time,
384                                           cc,
385                                           request_pac,
386                                           add_netbios_addr,
387                                           renewable_time,
388                                           &status);
389         if (ret) {
390                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
391                         auth_princ, error_message(ret), ret));
392                 /* status already set */
393                 goto out;
394         }
395
396         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
397         if (expire_time) {
398                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
399                         http_timestring(talloc_tos(), *expire_time),
400                         (int)*expire_time));
401         }
402         if (renew_till_time) {
403                 DEBUGADD(10,("\trenewable till: %s (%d)\n",
404                         http_timestring(talloc_tos(), *renew_till_time),
405                         (int)*renew_till_time));
406         }
407
408         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
409          * in that case fallback to NTLM - gd */
410
411         if (expire_time && renew_till_time &&
412             (*expire_time == 0) && (*renew_till_time == 0)) {
413                 return NT_STATUS_INVALID_LOGON_TYPE;
414         }
415
416         ret = cli_krb5_get_ticket(local_service,
417                                   time_offset,
418                                   &tkt,
419                                   &sesskey1,
420                                   0,
421                                   cc,
422                                   NULL,
423                                   impersonate_princ_s);
424         if (ret) {
425                 DEBUG(1,("failed to get ticket for %s: %s\n",
426                         local_service, error_message(ret)));
427                 if (impersonate_princ_s) {
428                         DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
429                                 impersonate_princ_s));
430                 }
431                 status = krb5_to_nt_status(ret);
432                 goto out;
433         }
434         status = ads_verify_ticket(mem_ctx,
435                                    lp_realm(),
436                                    time_offset,
437                                    &tkt,
438                                    &client_princ_out,
439                                    logon_info,
440                                    &ap_rep,
441                                    &sesskey2,
442                                    False);
443         if (!NT_STATUS_IS_OK(status)) {
444                 DEBUG(1,("ads_verify_ticket failed: %s\n",
445                         nt_errstr(status)));
446                 goto out;
447         }
448
449         if (!*logon_info) {
450                 DEBUG(1,("no PAC\n"));
451                 status = NT_STATUS_INVALID_PARAMETER;
452                 goto out;
453         }
454
455 out:
456         if (cc != cache_name) {
457                 ads_kdestroy(cc);
458         }
459
460         data_blob_free(&tkt);
461         data_blob_free(&ap_rep);
462         data_blob_free(&sesskey1);
463         data_blob_free(&sesskey2);
464
465         TALLOC_FREE(client_princ_out);
466
467         return status;
468 }
469
470 #endif