~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/auth/kerberos/kerberos_pac.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   Unix SMB/CIFS implementation.
 
3
 
 
4
   Create and parse the krb5 PAC
 
5
   
 
6
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008
 
7
   Copyright (C) Andrew Tridgell 2001
 
8
   Copyright (C) Luke Howard 2002-2003
 
9
   Copyright (C) Stefan Metzmacher 2004-2005
 
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
   
 
22
   You should have received a copy of the GNU General Public License
 
23
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
24
*/
 
25
 
 
26
#include "includes.h"
 
27
#include "system/kerberos.h"
 
28
#include "auth/auth.h"
 
29
#include "auth/kerberos/kerberos.h"
 
30
#include "librpc/gen_ndr/ndr_krb5pac.h"
 
31
#include "lib/ldb/include/ldb.h"
 
32
#include "auth/auth_sam_reply.h"
 
33
 
 
34
krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx, 
 
35
                                   DATA_BLOB pac_data,
 
36
                                   struct PAC_SIGNATURE_DATA *sig,
 
37
                                   krb5_context context,
 
38
                                   const krb5_keyblock *keyblock)
 
39
{
 
40
        krb5_error_code ret;
 
41
        krb5_crypto crypto;
 
42
        Checksum cksum;
 
43
 
 
44
        cksum.cksumtype         = (CKSUMTYPE)sig->type;
 
45
        cksum.checksum.length   = sig->signature.length;
 
46
        cksum.checksum.data     = sig->signature.data;
 
47
 
 
48
        ret = krb5_crypto_init(context,
 
49
                               keyblock,
 
50
                               0,
 
51
                               &crypto);
 
52
        if (ret) {
 
53
                DEBUG(0,("krb5_crypto_init() failed: %s\n", 
 
54
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
55
                return ret;
 
56
        }
 
57
        ret = krb5_verify_checksum(context,
 
58
                                   crypto,
 
59
                                   KRB5_KU_OTHER_CKSUM,
 
60
                                   pac_data.data,
 
61
                                   pac_data.length,
 
62
                                   &cksum);
 
63
        krb5_crypto_destroy(context, crypto);
 
64
 
 
65
        return ret;
 
66
}
 
67
 
 
68
 NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
 
69
                              struct smb_iconv_convenience *iconv_convenience,
 
70
                              struct PAC_DATA **pac_data_out,
 
71
                              DATA_BLOB blob,
 
72
                              krb5_context context,
 
73
                              const krb5_keyblock *krbtgt_keyblock,
 
74
                              const krb5_keyblock *service_keyblock,
 
75
                              krb5_const_principal client_principal,
 
76
                              time_t tgs_authtime,
 
77
                              krb5_error_code *k5ret)
 
78
{
 
79
        krb5_error_code ret;
 
80
        NTSTATUS status;
 
81
        enum ndr_err_code ndr_err;
 
82
        struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
 
83
        struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
 
84
        struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
 
85
        struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
 
86
        struct PAC_LOGON_INFO *logon_info = NULL;
 
87
        struct PAC_LOGON_NAME *logon_name = NULL;
 
88
        struct PAC_DATA *pac_data;
 
89
        struct PAC_DATA_RAW *pac_data_raw;
 
90
 
 
91
        DATA_BLOB *srv_sig_blob = NULL;
 
92
        DATA_BLOB *kdc_sig_blob = NULL;
 
93
 
 
94
        DATA_BLOB modified_pac_blob;
 
95
        NTTIME tgs_authtime_nttime;
 
96
        krb5_principal client_principal_pac;
 
97
        int i;
 
98
 
 
99
        krb5_clear_error_string(context);
 
100
 
 
101
        if (k5ret) {
 
102
                *k5ret = KRB5_PARSE_MALFORMED;
 
103
        }
 
104
 
 
105
        pac_data = talloc(mem_ctx, struct PAC_DATA);
 
106
        pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
 
107
        kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
 
108
        srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
 
109
        if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
 
110
                if (k5ret) {
 
111
                        *k5ret = ENOMEM;
 
112
                }
 
113
                return NT_STATUS_NO_MEMORY;
 
114
        }
 
115
 
 
116
        ndr_err = ndr_pull_struct_blob(&blob, pac_data, 
 
117
                        iconv_convenience, pac_data,
 
118
                       (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
 
119
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
120
                status = ndr_map_error2ntstatus(ndr_err);
 
121
                DEBUG(0,("can't parse the PAC: %s\n",
 
122
                        nt_errstr(status)));
 
123
                return status;
 
124
        }
 
125
 
 
126
        if (pac_data->num_buffers < 4) {
 
127
                /* we need logon_ingo, service_key and kdc_key */
 
128
                DEBUG(0,("less than 4 PAC buffers\n"));
 
129
                return NT_STATUS_INVALID_PARAMETER;
 
130
        }
 
131
 
 
132
        ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw, 
 
133
                                       iconv_convenience, pac_data_raw,
 
134
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
 
135
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
136
                status = ndr_map_error2ntstatus(ndr_err);
 
137
                DEBUG(0,("can't parse the PAC: %s\n",
 
138
                        nt_errstr(status)));
 
139
                return status;
 
140
        }
 
141
 
 
142
        if (pac_data_raw->num_buffers < 4) {
 
143
                /* we need logon_ingo, service_key and kdc_key */
 
144
                DEBUG(0,("less than 4 PAC buffers\n"));
 
145
                return NT_STATUS_INVALID_PARAMETER;
 
146
        }
 
147
 
 
148
        if (pac_data->num_buffers != pac_data_raw->num_buffers) {
 
149
                /* we need logon_ingo, service_key and kdc_key */
 
150
                DEBUG(0,("misparse!  PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
 
151
                         pac_data->num_buffers, pac_data_raw->num_buffers));
 
152
                return NT_STATUS_INVALID_PARAMETER;
 
153
        }
 
154
 
 
155
        for (i=0; i < pac_data->num_buffers; i++) {
 
156
                if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
 
157
                        DEBUG(0,("misparse!  PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
 
158
                                 i, pac_data->buffers[i].type, pac_data->buffers[i].type));
 
159
                        return NT_STATUS_INVALID_PARAMETER;
 
160
                }
 
161
                switch (pac_data->buffers[i].type) {
 
162
                        case PAC_TYPE_LOGON_INFO:
 
163
                                if (!pac_data->buffers[i].info) {
 
164
                                        break;
 
165
                                }
 
166
                                logon_info = pac_data->buffers[i].info->logon_info.info;
 
167
                                break;
 
168
                        case PAC_TYPE_SRV_CHECKSUM:
 
169
                                if (!pac_data->buffers[i].info) {
 
170
                                        break;
 
171
                                }
 
172
                                srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
 
173
                                srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
 
174
                                break;
 
175
                        case PAC_TYPE_KDC_CHECKSUM:
 
176
                                if (!pac_data->buffers[i].info) {
 
177
                                        break;
 
178
                                }
 
179
                                kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
 
180
                                kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
 
181
                                break;
 
182
                        case PAC_TYPE_LOGON_NAME:
 
183
                                logon_name = &pac_data->buffers[i].info->logon_name;
 
184
                                break;
 
185
                        default:
 
186
                                break;
 
187
                }
 
188
        }
 
189
 
 
190
        if (!logon_info) {
 
191
                DEBUG(0,("PAC no logon_info\n"));
 
192
                return NT_STATUS_INVALID_PARAMETER;
 
193
        }
 
194
 
 
195
        if (!logon_name) {
 
196
                DEBUG(0,("PAC no logon_name\n"));
 
197
                return NT_STATUS_INVALID_PARAMETER;
 
198
        }
 
199
 
 
200
        if (!srv_sig_ptr || !srv_sig_blob) {
 
201
                DEBUG(0,("PAC no srv_key\n"));
 
202
                return NT_STATUS_INVALID_PARAMETER;
 
203
        }
 
204
 
 
205
        if (!kdc_sig_ptr || !kdc_sig_blob) {
 
206
                DEBUG(0,("PAC no kdc_key\n"));
 
207
                return NT_STATUS_INVALID_PARAMETER;
 
208
        }
 
209
 
 
210
        /* Find and zero out the signatures, as required by the signing algorithm */
 
211
 
 
212
        /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
 
213
        ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, 
 
214
                                       iconv_convenience, kdc_sig_wipe,
 
215
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 
216
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
217
                status = ndr_map_error2ntstatus(ndr_err);
 
218
                DEBUG(0,("can't parse the KDC signature: %s\n",
 
219
                        nt_errstr(status)));
 
220
                return status;
 
221
        }
 
222
        
 
223
        ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, 
 
224
                                       iconv_convenience, srv_sig_wipe,
 
225
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 
226
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
227
                status = ndr_map_error2ntstatus(ndr_err);
 
228
                DEBUG(0,("can't parse the SRV signature: %s\n",
 
229
                        nt_errstr(status)));
 
230
                return status;
 
231
        }
 
232
 
 
233
        /* Now zero the decoded structure */
 
234
        memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
 
235
        memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
 
236
        
 
237
        /* and reencode, back into the same place it came from */
 
238
        ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, 
 
239
                                       iconv_convenience,
 
240
                                       kdc_sig_wipe,
 
241
                                       (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
 
242
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
243
                status = ndr_map_error2ntstatus(ndr_err);
 
244
                DEBUG(0,("can't repack the KDC signature: %s\n",
 
245
                        nt_errstr(status)));
 
246
                return status;
 
247
        }
 
248
        ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, 
 
249
                                       iconv_convenience,
 
250
                                       srv_sig_wipe,
 
251
                                       (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
 
252
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
253
                status = ndr_map_error2ntstatus(ndr_err);
 
254
                DEBUG(0,("can't repack the SRV signature: %s\n",
 
255
                        nt_errstr(status)));
 
256
                return status;
 
257
        }
 
258
 
 
259
        /* push out the whole structure, but now with zero'ed signatures */
 
260
        ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, 
 
261
                                       iconv_convenience,
 
262
                                       pac_data_raw,
 
263
                                       (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
 
264
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
265
                status = ndr_map_error2ntstatus(ndr_err);
 
266
                DEBUG(0,("can't repack the RAW PAC: %s\n",
 
267
                        nt_errstr(status)));
 
268
                return status;
 
269
        }
 
270
 
 
271
        /* verify by service_key */
 
272
        ret = check_pac_checksum(mem_ctx, 
 
273
                                 modified_pac_blob, srv_sig_ptr, 
 
274
                                 context, 
 
275
                                 service_keyblock);
 
276
        if (ret) {
 
277
                DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
 
278
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
279
                if (k5ret) {
 
280
                        *k5ret = ret;
 
281
                }
 
282
                return NT_STATUS_ACCESS_DENIED;
 
283
        }
 
284
 
 
285
        if (krbtgt_keyblock) {
 
286
                ret = check_pac_checksum(mem_ctx, 
 
287
                                            srv_sig_ptr->signature, kdc_sig_ptr, 
 
288
                                            context, krbtgt_keyblock);
 
289
                if (ret) {
 
290
                        DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
 
291
                                  smb_get_krb5_error_message(context, ret, mem_ctx)));
 
292
                        if (k5ret) {
 
293
                                *k5ret = ret;
 
294
                        }
 
295
                        return NT_STATUS_ACCESS_DENIED;
 
296
                }
 
297
        }
 
298
 
 
299
        /* Convert to NT time, so as not to loose accuracy in comparison */
 
300
        unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
 
301
 
 
302
        if (tgs_authtime_nttime != logon_name->logon_time) {
 
303
                DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
 
304
                DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
 
305
                DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
 
306
                return NT_STATUS_ACCESS_DENIED;
 
307
        }
 
308
 
 
309
        ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM, 
 
310
                                    &client_principal_pac);
 
311
        if (ret) {
 
312
                DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n", 
 
313
                          logon_name->account_name, 
 
314
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
315
                if (k5ret) {
 
316
                        *k5ret = ret;
 
317
                }
 
318
                return NT_STATUS_INVALID_PARAMETER;
 
319
        }
 
320
 
 
321
        if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
 
322
                DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n", 
 
323
                          logon_name->account_name));
 
324
                return NT_STATUS_ACCESS_DENIED;
 
325
        }
 
326
        
 
327
#if 0
 
328
        if (strcasecmp(logon_info->info3.base.account_name.string, 
 
329
                       "Administrator")== 0) {
 
330
                file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
 
331
        }
 
332
#endif
 
333
 
 
334
        DEBUG(3,("Found account name from PAC: %s [%s]\n",
 
335
                 logon_info->info3.base.account_name.string, 
 
336
                 logon_info->info3.base.full_name.string));
 
337
        *pac_data_out = pac_data;
 
338
 
 
339
        return NT_STATUS_OK;
 
340
}
 
341
 
 
342
_PUBLIC_  NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
 
343
                                  struct smb_iconv_convenience *iconv_convenience,
 
344
                                  struct PAC_LOGON_INFO **logon_info,
 
345
                                  DATA_BLOB blob,
 
346
                                  krb5_context context,
 
347
                                  const krb5_keyblock *krbtgt_keyblock,
 
348
                                  const krb5_keyblock *service_keyblock,
 
349
                                  krb5_const_principal client_principal,
 
350
                                  time_t tgs_authtime, 
 
351
                                  krb5_error_code *k5ret)
 
352
{
 
353
        NTSTATUS nt_status;
 
354
        struct PAC_DATA *pac_data;
 
355
        int i;
 
356
        nt_status = kerberos_decode_pac(mem_ctx, 
 
357
                                        iconv_convenience,
 
358
                                        &pac_data,
 
359
                                        blob,
 
360
                                        context,
 
361
                                        krbtgt_keyblock,
 
362
                                        service_keyblock,
 
363
                                        client_principal, 
 
364
                                        tgs_authtime,
 
365
                                        k5ret);
 
366
        if (!NT_STATUS_IS_OK(nt_status)) {
 
367
                return nt_status;
 
368
        }
 
369
 
 
370
        *logon_info = NULL;
 
371
        for (i=0; i < pac_data->num_buffers; i++) {
 
372
                if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
 
373
                        continue;
 
374
                }
 
375
                *logon_info = pac_data->buffers[i].info->logon_info.info; 
 
376
        }
 
377
        if (!*logon_info) {
 
378
                return NT_STATUS_INVALID_PARAMETER;
 
379
        }
 
380
        return NT_STATUS_OK;
 
381
}
 
382
 
 
383
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, 
 
384
                                         DATA_BLOB *pac_data,
 
385
                                         struct PAC_SIGNATURE_DATA *sig,
 
386
                                         krb5_context context,
 
387
                                         const krb5_keyblock *keyblock)
 
388
{
 
389
        krb5_error_code ret;
 
390
        krb5_crypto crypto;
 
391
        Checksum cksum;
 
392
 
 
393
 
 
394
        ret = krb5_crypto_init(context,
 
395
                               keyblock,
 
396
                               0,
 
397
                               &crypto);
 
398
        if (ret) {
 
399
                DEBUG(0,("krb5_crypto_init() failed: %s\n",
 
400
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
401
                return ret;
 
402
        }
 
403
        ret = krb5_create_checksum(context,
 
404
                                   crypto,
 
405
                                   KRB5_KU_OTHER_CKSUM,
 
406
                                   0,
 
407
                                   pac_data->data,
 
408
                                   pac_data->length,
 
409
                                   &cksum);
 
410
        if (ret) {
 
411
                DEBUG(2, ("PAC Verification failed: %s\n", 
 
412
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
413
        }
 
414
 
 
415
        krb5_crypto_destroy(context, crypto);
 
416
 
 
417
        if (ret) {
 
418
                return ret;
 
419
        }
 
420
 
 
421
        sig->type = cksum.cksumtype;
 
422
        sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
 
423
        free_Checksum(&cksum);
 
424
 
 
425
        return 0;
 
426
}
 
427
 
 
428
 krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
 
429
                                     struct smb_iconv_convenience *iconv_convenience,
 
430
                                    struct PAC_DATA *pac_data,
 
431
                                    krb5_context context,
 
432
                                    const krb5_keyblock *krbtgt_keyblock,
 
433
                                    const krb5_keyblock *service_keyblock,
 
434
                                    DATA_BLOB *pac) 
 
435
{
 
436
        NTSTATUS nt_status;
 
437
        krb5_error_code ret;
 
438
        enum ndr_err_code ndr_err;
 
439
        DATA_BLOB zero_blob = data_blob(NULL, 0);
 
440
        DATA_BLOB tmp_blob = data_blob(NULL, 0);
 
441
        struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
 
442
        struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
 
443
        int i;
 
444
 
 
445
        /* First, just get the keytypes filled in (and lengths right, eventually) */
 
446
        for (i=0; i < pac_data->num_buffers; i++) {
 
447
                if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
 
448
                        continue;
 
449
                }
 
450
                kdc_checksum = &pac_data->buffers[i].info->kdc_cksum, 
 
451
                ret = make_pac_checksum(mem_ctx, &zero_blob,
 
452
                                        kdc_checksum, 
 
453
                                        context, krbtgt_keyblock);
 
454
                if (ret) {
 
455
                        DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
 
456
                                  smb_get_krb5_error_message(context, ret, mem_ctx)));
 
457
                        talloc_free(pac_data);
 
458
                        return ret;
 
459
                }
 
460
        }
 
461
        
 
462
        for (i=0; i < pac_data->num_buffers; i++) {
 
463
                if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
 
464
                        continue;
 
465
                }
 
466
                srv_checksum = &pac_data->buffers[i].info->srv_cksum; 
 
467
                ret = make_pac_checksum(mem_ctx, &zero_blob, 
 
468
                                        srv_checksum, 
 
469
                                        context, service_keyblock);
 
470
                if (ret) {
 
471
                        DEBUG(2, ("making service PAC checksum failed: %s\n", 
 
472
                                  smb_get_krb5_error_message(context, ret, mem_ctx)));
 
473
                        talloc_free(pac_data);
 
474
                        return ret;
 
475
                }
 
476
        }
 
477
 
 
478
        if (!kdc_checksum) {
 
479
                DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
 
480
                return EINVAL;
 
481
        }
 
482
        if (!srv_checksum) {
 
483
                DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
 
484
                return EINVAL;
 
485
        }
 
486
 
 
487
        /* But wipe out the actual signatures */
 
488
        memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
 
489
        memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
 
490
 
 
491
        ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
 
492
                                       iconv_convenience,
 
493
                                       pac_data,
 
494
                                       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
 
495
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
496
                nt_status = ndr_map_error2ntstatus(ndr_err);
 
497
                DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
 
498
                talloc_free(pac_data);
 
499
                return EINVAL;
 
500
        }
 
501
 
 
502
        /* Then sign the result of the previous push, where the sig was zero'ed out */
 
503
        ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
 
504
                                context, service_keyblock);
 
505
 
 
506
        /* Then sign Server checksum */
 
507
        ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
 
508
        if (ret) {
 
509
                DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
 
510
                          smb_get_krb5_error_message(context, ret, mem_ctx)));
 
511
                talloc_free(pac_data);
 
512
                return ret;
 
513
        }
 
514
 
 
515
        /* And push it out again, this time to the world.  This relies on determanistic pointer values */
 
516
        ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
 
517
                                       iconv_convenience,
 
518
                                       pac_data,
 
519
                                       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
 
520
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
521
                nt_status = ndr_map_error2ntstatus(ndr_err);
 
522
                DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
 
523
                talloc_free(pac_data);
 
524
                return EINVAL;
 
525
        }
 
526
 
 
527
        *pac = tmp_blob;
 
528
 
 
529
        return ret;
 
530
}
 
531
 
 
532
 
 
533
 krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
 
534
                                     struct smb_iconv_convenience *iconv_convenience,
 
535
                                     struct auth_serversupplied_info *server_info,
 
536
                                     krb5_context context,
 
537
                                     const krb5_keyblock *krbtgt_keyblock,
 
538
                                     const krb5_keyblock *service_keyblock,
 
539
                                     krb5_principal client_principal,
 
540
                                     time_t tgs_authtime,
 
541
                                     DATA_BLOB *pac)
 
542
{
 
543
        NTSTATUS nt_status;
 
544
        krb5_error_code ret;
 
545
        struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
 
546
        struct netr_SamInfo3 *sam3;
 
547
        union PAC_INFO *u_LOGON_INFO;
 
548
        struct PAC_LOGON_INFO *LOGON_INFO;
 
549
        union PAC_INFO *u_LOGON_NAME;
 
550
        struct PAC_LOGON_NAME *LOGON_NAME;
 
551
        union PAC_INFO *u_KDC_CHECKSUM;
 
552
        union PAC_INFO *u_SRV_CHECKSUM;
 
553
 
 
554
        char *name;
 
555
                
 
556
        enum {
 
557
                PAC_BUF_LOGON_INFO = 0,
 
558
                PAC_BUF_LOGON_NAME = 1,
 
559
                PAC_BUF_SRV_CHECKSUM = 2,
 
560
                PAC_BUF_KDC_CHECKSUM = 3,
 
561
                PAC_BUF_NUM_BUFFERS = 4
 
562
        };
 
563
 
 
564
        if (!pac_data) {
 
565
                return ENOMEM;
 
566
        }
 
567
 
 
568
        pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
 
569
        pac_data->version = 0;
 
570
 
 
571
        pac_data->buffers = talloc_array(pac_data, 
 
572
                                         struct PAC_BUFFER,
 
573
                                         pac_data->num_buffers);
 
574
        if (!pac_data->buffers) {
 
575
                talloc_free(pac_data);
 
576
                return ENOMEM;
 
577
        }
 
578
 
 
579
        /* LOGON_INFO */
 
580
        u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
 
581
        if (!u_LOGON_INFO) {
 
582
                talloc_free(pac_data);
 
583
                return ENOMEM;
 
584
        }
 
585
        pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
 
586
        pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
 
587
 
 
588
        /* LOGON_NAME */
 
589
        u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
 
590
        if (!u_LOGON_NAME) {
 
591
                talloc_free(pac_data);
 
592
                return ENOMEM;
 
593
        }
 
594
        pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
 
595
        pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
 
596
        LOGON_NAME = &u_LOGON_NAME->logon_name;
 
597
 
 
598
        /* SRV_CHECKSUM */
 
599
        u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
 
600
        if (!u_SRV_CHECKSUM) {
 
601
                talloc_free(pac_data);
 
602
                return ENOMEM;
 
603
        }
 
604
        pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
 
605
        pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
 
606
 
 
607
        /* KDC_CHECKSUM */
 
608
        u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
 
609
        if (!u_KDC_CHECKSUM) {
 
610
                talloc_free(pac_data);
 
611
                return ENOMEM;
 
612
        }
 
613
        pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
 
614
        pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
 
615
 
 
616
        /* now the real work begins... */
 
617
 
 
618
        LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
 
619
        if (!LOGON_INFO) {
 
620
                talloc_free(pac_data);
 
621
                return ENOMEM;
 
622
        }
 
623
        nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
 
624
        if (!NT_STATUS_IS_OK(nt_status)) {
 
625
                DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
 
626
                talloc_free(pac_data);
 
627
                return EINVAL;
 
628
        }
 
629
 
 
630
        u_LOGON_INFO->logon_info.info           = LOGON_INFO;
 
631
        LOGON_INFO->info3 = *sam3;
 
632
 
 
633
        ret = krb5_unparse_name_flags(context, client_principal, 
 
634
                                      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
 
635
        if (ret) {
 
636
                return ret;
 
637
        }
 
638
        LOGON_NAME->account_name        = talloc_strdup(LOGON_NAME, name);
 
639
        free(name);
 
640
        /*
 
641
          this logon_time field is absolutely critical. This is what
 
642
          caused all our PAC troubles :-)
 
643
        */
 
644
        unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
 
645
 
 
646
        ret = kerberos_encode_pac(mem_ctx, 
 
647
                                  iconv_convenience,
 
648
                                  pac_data, 
 
649
                                  context,
 
650
                                  krbtgt_keyblock,
 
651
                                  service_keyblock,
 
652
                                  pac);
 
653
        talloc_free(pac_data);
 
654
        return ret;
 
655
}
 
656
 
 
657
krb5_error_code kerberos_pac_to_server_info(TALLOC_CTX *mem_ctx,
 
658
                                                struct smb_iconv_convenience *iconv_convenience,
 
659
                                                krb5_pac pac,
 
660
                                                krb5_context context,
 
661
                                                struct auth_serversupplied_info **server_info) 
 
662
{
 
663
        NTSTATUS nt_status;
 
664
        enum ndr_err_code ndr_err;
 
665
        krb5_error_code ret;
 
666
 
 
667
        DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
 
668
        krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
 
669
 
 
670
        union PAC_INFO info;
 
671
        union netr_Validation validation;
 
672
        struct auth_serversupplied_info *server_info_out;
 
673
 
 
674
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
 
675
 
 
676
        if (!tmp_ctx) {
 
677
                return ENOMEM;
 
678
        }
 
679
 
 
680
        ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
 
681
        if (ret != 0) {
 
682
                talloc_free(tmp_ctx);
 
683
                return EINVAL;
 
684
        }
 
685
 
 
686
        pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
 
687
 
 
688
        ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, iconv_convenience, &info,
 
689
                                      PAC_TYPE_LOGON_INFO,
 
690
                                      (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
 
691
        krb5_data_free(&k5pac_logon_info_in);
 
692
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
 
693
                nt_status = ndr_map_error2ntstatus(ndr_err);
 
694
                DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
 
695
                talloc_free(tmp_ctx);
 
696
                return EINVAL;
 
697
        }
 
698
 
 
699
        /* Pull this right into the normal auth sysstem structures */
 
700
        validation.sam3 = &info.logon_info.info->info3;
 
701
        nt_status = make_server_info_netlogon_validation(mem_ctx,
 
702
                                                         "",
 
703
                                                         3, &validation,
 
704
                                                         &server_info_out); 
 
705
        if (!NT_STATUS_IS_OK(nt_status)) {
 
706
                talloc_free(tmp_ctx);
 
707
                return EINVAL;
 
708
        }
 
709
        
 
710
        ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
 
711
        if (ret != 0) {
 
712
                talloc_free(tmp_ctx);
 
713
                return ret;
 
714
        }
 
715
 
 
716
        pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
 
717
                
 
718
        ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, server_info_out, 
 
719
                                       iconv_convenience, &server_info_out->pac_srv_sig,
 
720
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 
721
        krb5_data_free(&k5pac_srv_checksum_in);
 
722
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
723
                nt_status = ndr_map_error2ntstatus(ndr_err);
 
724
                DEBUG(0,("can't parse the KDC signature: %s\n",
 
725
                        nt_errstr(nt_status)));
 
726
                return EINVAL;
 
727
        }
 
728
 
 
729
        ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
 
730
        if (ret != 0) {
 
731
                talloc_free(tmp_ctx);
 
732
                return ret;
 
733
        }
 
734
 
 
735
        pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
 
736
                
 
737
        ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, server_info_out, 
 
738
                                       iconv_convenience, &server_info_out->pac_kdc_sig,
 
739
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
 
740
        krb5_data_free(&k5pac_kdc_checksum_in);
 
741
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
 
742
                nt_status = ndr_map_error2ntstatus(ndr_err);
 
743
                DEBUG(0,("can't parse the KDC signature: %s\n",
 
744
                        nt_errstr(nt_status)));
 
745
                return EINVAL;
 
746
        }
 
747
 
 
748
        *server_info = server_info_out;
 
749
        
 
750
        return 0;
 
751
}
 
752
 
 
753
 
 
754
NTSTATUS kerberos_pac_blob_to_server_info(TALLOC_CTX *mem_ctx,
 
755
                                                     struct smb_iconv_convenience *iconv_convenience,
 
756
                                                     DATA_BLOB pac_blob, 
 
757
                                                     krb5_context context,
 
758
                                                     struct auth_serversupplied_info **server_info) 
 
759
{
 
760
        krb5_error_code ret;
 
761
        krb5_pac pac;
 
762
        ret = krb5_pac_parse(context, 
 
763
                             pac_blob.data, pac_blob.length, 
 
764
                             &pac);
 
765
        if (ret) {
 
766
                return map_nt_error_from_unix(ret);
 
767
        }
 
768
 
 
769
 
 
770
        ret = kerberos_pac_to_server_info(mem_ctx, iconv_convenience, pac, context, server_info);
 
771
        krb5_pac_free(context, pac);
 
772
        if (ret) {
 
773
                return map_nt_error_from_unix(ret);
 
774
        }
 
775
        return NT_STATUS_OK;
 
776
}