auth-kerberos: add the credentials.h so that enum credentials_obtained is defined
[nivanova/samba-autobuild/.git] / source4 / auth / kerberos / kerberos_pac.c
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 <ldb.h>
32 #include "auth/auth_sam_reply.h"
33 #include "auth/credentials/credentials.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36
37  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
38                                     struct PAC_DATA *pac_data,
39                                     krb5_context context,
40                                     const krb5_keyblock *krbtgt_keyblock,
41                                     const krb5_keyblock *service_keyblock,
42                                     DATA_BLOB *pac)
43 {
44         NTSTATUS nt_status;
45         krb5_error_code ret;
46         enum ndr_err_code ndr_err;
47         DATA_BLOB zero_blob = data_blob(NULL, 0);
48         DATA_BLOB tmp_blob = data_blob(NULL, 0);
49         struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
50         struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
51         int i;
52
53         /* First, just get the keytypes filled in (and lengths right, eventually) */
54         for (i=0; i < pac_data->num_buffers; i++) {
55                 if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
56                         continue;
57                 }
58                 kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
59                 ret = smb_krb5_make_pac_checksum(mem_ctx,
60                                                  &zero_blob,
61                                                  context,
62                                                  krbtgt_keyblock,
63                                                  &kdc_checksum->type,
64                                                  &kdc_checksum->signature);
65                 if (ret) {
66                         DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
67                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
68                         talloc_free(pac_data);
69                         return ret;
70                 }
71         }
72
73         for (i=0; i < pac_data->num_buffers; i++) {
74                 if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
75                         continue;
76                 }
77                 srv_checksum = &pac_data->buffers[i].info->srv_cksum;
78                 ret = smb_krb5_make_pac_checksum(mem_ctx,
79                                                  &zero_blob,
80                                                  context,
81                                                  service_keyblock,
82                                                  &srv_checksum->type,
83                                                  &srv_checksum->signature);
84                 if (ret) {
85                         DEBUG(2, ("making service PAC checksum failed: %s\n",
86                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
87                         talloc_free(pac_data);
88                         return ret;
89                 }
90         }
91
92         if (!kdc_checksum) {
93                 DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
94                 return EINVAL;
95         }
96         if (!srv_checksum) {
97                 DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
98                 return EINVAL;
99         }
100
101         /* But wipe out the actual signatures */
102         memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
103         memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
104
105         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
106                                        pac_data,
107                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
108         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
109                 nt_status = ndr_map_error2ntstatus(ndr_err);
110                 DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
111                 talloc_free(pac_data);
112                 return EINVAL;
113         }
114
115         /* Then sign the result of the previous push, where the sig was zero'ed out */
116         ret = smb_krb5_make_pac_checksum(mem_ctx,
117                                          &tmp_blob,
118                                          context,
119                                          service_keyblock,
120                                          &srv_checksum->type,
121                                          &srv_checksum->signature);
122
123         /* Then sign Server checksum */
124         ret = smb_krb5_make_pac_checksum(mem_ctx,
125                                          &srv_checksum->signature,
126                                          context,
127                                          krbtgt_keyblock,
128                                          &kdc_checksum->type,
129                                          &kdc_checksum->signature);
130         if (ret) {
131                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
132                           smb_get_krb5_error_message(context, ret, mem_ctx)));
133                 talloc_free(pac_data);
134                 return ret;
135         }
136
137         /* And push it out again, this time to the world.  This relies on determanistic pointer values */
138         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
139                                        pac_data,
140                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
141         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
142                 nt_status = ndr_map_error2ntstatus(ndr_err);
143                 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
144                 talloc_free(pac_data);
145                 return EINVAL;
146         }
147
148         *pac = tmp_blob;
149
150         return ret;
151 }
152
153
154  krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
155                                      struct auth_user_info_dc *user_info_dc,
156                                      krb5_context context,
157                                      const krb5_keyblock *krbtgt_keyblock,
158                                      const krb5_keyblock *service_keyblock,
159                                      krb5_principal client_principal,
160                                      time_t tgs_authtime,
161                                      DATA_BLOB *pac)
162 {
163         NTSTATUS nt_status;
164         krb5_error_code ret;
165         struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
166         struct netr_SamInfo3 *sam3;
167         union PAC_INFO *u_LOGON_INFO;
168         struct PAC_LOGON_INFO *LOGON_INFO;
169         union PAC_INFO *u_LOGON_NAME;
170         struct PAC_LOGON_NAME *LOGON_NAME;
171         union PAC_INFO *u_KDC_CHECKSUM;
172         union PAC_INFO *u_SRV_CHECKSUM;
173
174         char *name;
175
176         enum {
177                 PAC_BUF_LOGON_INFO = 0,
178                 PAC_BUF_LOGON_NAME = 1,
179                 PAC_BUF_SRV_CHECKSUM = 2,
180                 PAC_BUF_KDC_CHECKSUM = 3,
181                 PAC_BUF_NUM_BUFFERS = 4
182         };
183
184         if (!pac_data) {
185                 return ENOMEM;
186         }
187
188         pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
189         pac_data->version = 0;
190
191         pac_data->buffers = talloc_array(pac_data,
192                                          struct PAC_BUFFER,
193                                          pac_data->num_buffers);
194         if (!pac_data->buffers) {
195                 talloc_free(pac_data);
196                 return ENOMEM;
197         }
198
199         /* LOGON_INFO */
200         u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
201         if (!u_LOGON_INFO) {
202                 talloc_free(pac_data);
203                 return ENOMEM;
204         }
205         pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
206         pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
207
208         /* LOGON_NAME */
209         u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
210         if (!u_LOGON_NAME) {
211                 talloc_free(pac_data);
212                 return ENOMEM;
213         }
214         pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
215         pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
216         LOGON_NAME = &u_LOGON_NAME->logon_name;
217
218         /* SRV_CHECKSUM */
219         u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
220         if (!u_SRV_CHECKSUM) {
221                 talloc_free(pac_data);
222                 return ENOMEM;
223         }
224         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
225         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
226
227         /* KDC_CHECKSUM */
228         u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
229         if (!u_KDC_CHECKSUM) {
230                 talloc_free(pac_data);
231                 return ENOMEM;
232         }
233         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
234         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
235
236         /* now the real work begins... */
237
238         LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
239         if (!LOGON_INFO) {
240                 talloc_free(pac_data);
241                 return ENOMEM;
242         }
243         nt_status = auth_convert_user_info_dc_saminfo3(LOGON_INFO, user_info_dc, &sam3);
244         if (!NT_STATUS_IS_OK(nt_status)) {
245                 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
246                 talloc_free(pac_data);
247                 return EINVAL;
248         }
249
250         u_LOGON_INFO->logon_info.info           = LOGON_INFO;
251         LOGON_INFO->info3 = *sam3;
252
253         ret = krb5_unparse_name_flags(context, client_principal,
254                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
255         if (ret) {
256                 return ret;
257         }
258         LOGON_NAME->account_name        = talloc_strdup(LOGON_NAME, name);
259         free(name);
260         /*
261           this logon_time field is absolutely critical. This is what
262           caused all our PAC troubles :-)
263         */
264         unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
265
266         ret = kerberos_encode_pac(mem_ctx,
267                                   pac_data,
268                                   context,
269                                   krbtgt_keyblock,
270                                   service_keyblock,
271                                   pac);
272         talloc_free(pac_data);
273         return ret;
274 }
275
276 krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx,
277                                              krb5_pac pac,
278                                              krb5_context context,
279                                              struct auth_user_info_dc **user_info_dc,
280                                              struct PAC_SIGNATURE_DATA *pac_srv_sig,
281                                              struct PAC_SIGNATURE_DATA *pac_kdc_sig)
282 {
283         NTSTATUS nt_status;
284         enum ndr_err_code ndr_err;
285         krb5_error_code ret;
286
287         DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
288         krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
289
290         union PAC_INFO info;
291         struct auth_user_info_dc *user_info_dc_out;
292
293         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
294
295         if (!tmp_ctx) {
296                 return ENOMEM;
297         }
298
299         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
300         if (ret != 0) {
301                 talloc_free(tmp_ctx);
302                 return EINVAL;
303         }
304
305         pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
306
307         ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, &info,
308                                       PAC_TYPE_LOGON_INFO,
309                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
310         kerberos_free_data_contents(context, &k5pac_logon_info_in);
311         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
312                 nt_status = ndr_map_error2ntstatus(ndr_err);
313                 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
314                 talloc_free(tmp_ctx);
315                 return EINVAL;
316         }
317
318         /* Pull this right into the normal auth sysstem structures */
319         nt_status = make_user_info_dc_pac(mem_ctx,
320                                          info.logon_info.info,
321                                          &user_info_dc_out);
322         if (!NT_STATUS_IS_OK(nt_status)) {
323                 talloc_free(tmp_ctx);
324                 return EINVAL;
325         }
326
327         if (pac_srv_sig) {
328                 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
329                 if (ret != 0) {
330                         talloc_free(tmp_ctx);
331                         return ret;
332                 }
333
334                 pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
335
336                 ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, pac_srv_sig,
337                                                pac_srv_sig,
338                                                (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
339                 kerberos_free_data_contents(context, &k5pac_srv_checksum_in);
340                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
341                         nt_status = ndr_map_error2ntstatus(ndr_err);
342                         DEBUG(0,("can't parse the KDC signature: %s\n",
343                                  nt_errstr(nt_status)));
344                         return EINVAL;
345                 }
346         }
347
348         if (pac_kdc_sig) {
349                 ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
350                 if (ret != 0) {
351                         talloc_free(tmp_ctx);
352                         return ret;
353                 }
354
355                 pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
356
357                 ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, pac_kdc_sig,
358                                                pac_kdc_sig,
359                                                (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
360                 kerberos_free_data_contents(context, &k5pac_kdc_checksum_in);
361                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
362                         nt_status = ndr_map_error2ntstatus(ndr_err);
363                         DEBUG(0,("can't parse the KDC signature: %s\n",
364                                  nt_errstr(nt_status)));
365                         return EINVAL;
366                 }
367         }
368         *user_info_dc = user_info_dc_out;
369
370         return 0;
371 }
372
373
374 NTSTATUS kerberos_pac_blob_to_user_info_dc(TALLOC_CTX *mem_ctx,
375                                            DATA_BLOB pac_blob,
376                                            krb5_context context,
377                                            struct auth_user_info_dc **user_info_dc,
378                                            struct PAC_SIGNATURE_DATA *pac_srv_sig,
379                                            struct PAC_SIGNATURE_DATA *pac_kdc_sig)
380 {
381         krb5_error_code ret;
382         krb5_pac pac;
383         ret = krb5_pac_parse(context,
384                              pac_blob.data, pac_blob.length,
385                              &pac);
386         if (ret) {
387                 return map_nt_error_from_unix_common(ret);
388         }
389
390
391         ret = kerberos_pac_to_user_info_dc(mem_ctx, pac, context, user_info_dc, pac_srv_sig, pac_kdc_sig);
392         krb5_pac_free(context, pac);
393         if (ret) {
394                 return map_nt_error_from_unix_common(ret);
395         }
396         return NT_STATUS_OK;
397 }