pac: Fix wrong memory allocation check
[idra/samba.git] / source4 / kdc / pac-glue.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    PAC Glue between Samba and the KDC
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
7    Copyright (C) Simo Sorce <idra@samba.org> 2010
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "../libds/common/flags.h"
26 #include <ldb.h>
27 #include "auth/auth.h"
28 #include "auth/auth_sam_reply.h"
29 #include "kdc/kdc-glue.h"
30 #include "kdc/pac-glue.h"
31 #include "param/param.h"
32 #include "librpc/gen_ndr/ndr_krb5pac.h"
33
34 static
35 NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
36                                        struct auth_user_info_dc *info,
37                                        DATA_BLOB *pac_data)
38 {
39         struct netr_SamInfo3 *info3;
40         union PAC_INFO pac_info;
41         enum ndr_err_code ndr_err;
42         NTSTATUS nt_status;
43
44         ZERO_STRUCT(pac_info);
45
46         nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx, info, &info3);
47         if (!NT_STATUS_IS_OK(nt_status)) {
48                 DEBUG(1, ("Getting Samba info failed: %s\n",
49                           nt_errstr(nt_status)));
50                 return nt_status;
51         }
52
53         pac_info.logon_info.info = talloc_zero(mem_ctx, struct PAC_LOGON_INFO);
54         if (!pac_info.logon_info.info) {
55                 return NT_STATUS_NO_MEMORY;
56         }
57
58         pac_info.logon_info.info->info3 = *info3;
59
60         ndr_err = ndr_push_union_blob(pac_data, mem_ctx, &pac_info,
61                                       PAC_TYPE_LOGON_INFO,
62                                       (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
63         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
64                 nt_status = ndr_map_error2ntstatus(ndr_err);
65                 DEBUG(1, ("PAC (presig) push failed: %s\n",
66                           nt_errstr(nt_status)));
67                 return nt_status;
68         }
69
70         return NT_STATUS_OK;
71 }
72
73 krb5_error_code samba_make_krb5_pac(krb5_context context,
74                                     DATA_BLOB *pac_blob,
75                                     DATA_BLOB *deleg_blob,
76                                     krb5_pac *pac)
77 {
78         krb5_data pac_data;
79         krb5_data deleg_data;
80         krb5_error_code ret;
81
82         /* The user account may be set not to want the PAC */
83         if (!pac_blob) {
84                 return 0;
85         }
86
87         ret = krb5_data_copy(&pac_data, pac_blob->data, pac_blob->length);
88         if (ret != 0) {
89                 return ret;
90         }
91
92         ZERO_STRUCT(deleg_data);
93         if (deleg_blob) {
94                 ret = krb5_data_copy(&deleg_data,
95                                      deleg_blob->data,
96                                      deleg_blob->length);
97                 if (ret != 0) {
98                         krb5_data_free(&pac_data);
99                         return ret;
100                 }
101         }
102
103         ret = krb5_pac_init(context, pac);
104         if (ret != 0) {
105                 krb5_data_free(&pac_data);
106                 krb5_data_free(&deleg_data);
107                 return ret;
108         }
109
110         ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &pac_data);
111         krb5_data_free(&pac_data);
112         if (ret != 0) {
113                 krb5_data_free(&deleg_data);
114                 return ret;
115         }
116
117         if (deleg_blob) {
118                 ret = krb5_pac_add_buffer(context, *pac,
119                                           PAC_TYPE_CONSTRAINED_DELEGATION,
120                                           &deleg_data);
121                 krb5_data_free(&deleg_data);
122                 if (ret != 0) {
123                         return ret;
124                 }
125         }
126
127         return ret;
128 }
129
130 bool samba_princ_needs_pac(struct hdb_entry_ex *princ)
131 {
132
133         struct samba_kdc_entry *p = talloc_get_type(princ->ctx, struct samba_kdc_entry);
134         uint32_t userAccountControl;
135
136
137         /* The service account may be set not to want the PAC */
138         userAccountControl = ldb_msg_find_attr_as_uint(p->msg, "userAccountControl", 0);
139         if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) {
140                 return false;
141         }
142
143         return true;
144 }
145
146 /* Was the krbtgt an RODC (and we are not) */
147 bool samba_krbtgt_was_untrusted_rodc(struct hdb_entry_ex *princ)
148 {
149
150         struct samba_kdc_entry *p = talloc_get_type(princ->ctx, struct samba_kdc_entry);
151         int rodc_krbtgt_number;
152
153         /* Determine if this was printed by an RODC */
154         rodc_krbtgt_number = ldb_msg_find_attr_as_int(p->msg, "msDS-SecondaryKrbTgtNumber", -1);
155         if (rodc_krbtgt_number == -1) {
156                 return false;
157         } else if (rodc_krbtgt_number != p->kdc_db_ctx->my_krbtgt_number) {
158                 return true;
159         }
160
161         return false;
162 }
163
164 NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
165                                 struct hdb_entry_ex *client,
166                                 DATA_BLOB **_pac_blob)
167 {
168         struct samba_kdc_entry *p = talloc_get_type(client->ctx, struct samba_kdc_entry);
169         struct auth_user_info_dc *user_info_dc;
170         DATA_BLOB *pac_blob;
171         NTSTATUS nt_status;
172
173         /* The user account may be set not to want the PAC */
174         if ( ! samba_princ_needs_pac(client)) {
175                 *_pac_blob = NULL;
176                 return NT_STATUS_OK;
177         }
178
179         pac_blob = talloc_zero(mem_ctx, DATA_BLOB);
180         if (!pac_blob) {
181                 return NT_STATUS_NO_MEMORY;
182         }
183
184         nt_status = authsam_make_user_info_dc(mem_ctx, p->kdc_db_ctx->samdb,
185                                              lpcfg_netbios_name(p->kdc_db_ctx->lp_ctx),
186                                              lpcfg_sam_name(p->kdc_db_ctx->lp_ctx),
187                                              p->realm_dn,
188                                              p->msg,
189                                              data_blob(NULL, 0),
190                                              data_blob(NULL, 0),
191                                              &user_info_dc);
192         if (!NT_STATUS_IS_OK(nt_status)) {
193                 DEBUG(0, ("Getting user info for PAC failed: %s\n",
194                           nt_errstr(nt_status)));
195                 return nt_status;
196         }
197
198         nt_status = samba_get_logon_info_pac_blob(mem_ctx, user_info_dc, pac_blob);
199         if (!NT_STATUS_IS_OK(nt_status)) {
200                 DEBUG(0, ("Building PAC failed: %s\n",
201                           nt_errstr(nt_status)));
202                 return nt_status;
203         }
204
205         *_pac_blob = pac_blob;
206         return NT_STATUS_OK;
207 }
208
209 NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx,
210                                    krb5_context context,
211                                    const krb5_pac pac, DATA_BLOB *pac_blob)
212 {
213         struct auth_user_info_dc *user_info_dc;
214         krb5_error_code ret;
215         NTSTATUS nt_status;
216
217         ret = kerberos_pac_to_user_info_dc(mem_ctx, pac,
218                                            context, &user_info_dc, NULL, NULL);
219         if (ret) {
220                 return NT_STATUS_UNSUCCESSFUL;
221         }
222
223         nt_status = samba_get_logon_info_pac_blob(mem_ctx, 
224                                                   user_info_dc, pac_blob);
225
226         return nt_status;
227 }
228
229 NTSTATUS samba_kdc_update_delegation_info_blob(TALLOC_CTX *mem_ctx,
230                                 krb5_context context,
231                                 const krb5_pac pac,
232                                 const krb5_principal server_principal,
233                                 const krb5_principal proxy_principal,
234                                 DATA_BLOB *new_blob)
235 {
236         krb5_data old_data;
237         DATA_BLOB old_blob;
238         krb5_error_code ret;
239         NTSTATUS nt_status;
240         enum ndr_err_code ndr_err;
241         union PAC_INFO info;
242         struct PAC_CONSTRAINED_DELEGATION _d;
243         struct PAC_CONSTRAINED_DELEGATION *d = NULL;
244         char *server = NULL;
245         char *proxy = NULL;
246         uint32_t i;
247         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
248
249         if (tmp_ctx == NULL) {
250                 return NT_STATUS_NO_MEMORY;
251         }
252
253         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_CONSTRAINED_DELEGATION, &old_data);
254         if (ret == ENOENT) {
255                 ZERO_STRUCT(old_data);
256         } else if (ret) {
257                 talloc_free(tmp_ctx);
258                 return NT_STATUS_UNSUCCESSFUL;
259         }
260
261         old_blob.length = old_data.length;
262         old_blob.data = (uint8_t *)old_data.data;
263
264         ZERO_STRUCT(info);
265         if (old_blob.length > 0) {
266                 ndr_err = ndr_pull_union_blob(&old_blob, mem_ctx,
267                                 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
268                                 (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
269                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
270                         krb5_data_free(&old_data);
271                         nt_status = ndr_map_error2ntstatus(ndr_err);
272                         DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
273                         talloc_free(tmp_ctx);
274                         return nt_status;
275                 }
276         } else {
277                 ZERO_STRUCT(_d);
278                 info.constrained_delegation.info = &_d;
279         }
280         krb5_data_free(&old_data);
281
282         ret = krb5_unparse_name(context, server_principal, &server);
283         if (ret) {
284                 talloc_free(tmp_ctx);
285                 return NT_STATUS_INTERNAL_ERROR;
286         }
287
288         ret = krb5_unparse_name_flags(context, proxy_principal,
289                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &proxy);
290         if (ret) {
291                 SAFE_FREE(server);
292                 talloc_free(tmp_ctx);
293                 return NT_STATUS_INTERNAL_ERROR;
294         }
295
296         d = info.constrained_delegation.info;
297         i = d->num_transited_services;
298         d->proxy_target.string = server;
299         d->transited_services = talloc_realloc(mem_ctx, d->transited_services,
300                                                struct lsa_String, i + 1);
301         d->transited_services[i].string = proxy;
302         d->num_transited_services = i + 1;
303
304         ndr_err = ndr_push_union_blob(new_blob, mem_ctx,
305                                 &info, PAC_TYPE_CONSTRAINED_DELEGATION,
306                                 (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
307         SAFE_FREE(server);
308         SAFE_FREE(proxy);
309         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
310                 krb5_data_free(&old_data);
311                 nt_status = ndr_map_error2ntstatus(ndr_err);
312                 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
313                 talloc_free(tmp_ctx);
314                 return nt_status;
315         }
316
317         talloc_free(tmp_ctx);
318         return NT_STATUS_OK;
319 }
320
321 /* this function allocates 'data' using malloc.
322  * The caller is responsible for freeing it */
323 void samba_kdc_build_edata_reply(NTSTATUS nt_status, DATA_BLOB *e_data)
324 {
325         PA_DATA pa;
326         unsigned char *buf;
327         size_t len;
328         krb5_error_code ret = 0;
329
330         if (!e_data)
331                 return;
332
333         pa.padata_type          = KRB5_PADATA_PW_SALT;
334         pa.padata_value.length  = 12;
335         pa.padata_value.data    = malloc(pa.padata_value.length);
336         if (!pa.padata_value.data) {
337                 e_data->length = 0;
338                 e_data->data = NULL;
339                 return;
340         }
341
342         SIVAL(pa.padata_value.data, 0, NT_STATUS_V(nt_status));
343         SIVAL(pa.padata_value.data, 4, 0);
344         SIVAL(pa.padata_value.data, 8, 1);
345
346         ASN1_MALLOC_ENCODE(PA_DATA, buf, len, &pa, &len, ret);
347         free(pa.padata_value.data);
348
349         e_data->data   = buf;
350         e_data->length = len;
351
352         return;
353 }
354
355 /* function to map policy errors */
356 krb5_error_code samba_kdc_map_policy_err(NTSTATUS nt_status)
357 {
358         krb5_error_code ret;
359
360         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
361                 ret = KRB5KDC_ERR_KEY_EXPIRED;
362         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
363                 ret = KRB5KDC_ERR_KEY_EXPIRED;
364         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
365                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
366         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
367                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
368         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
369                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
370         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
371                 ret = KRB5KDC_ERR_CLIENT_REVOKED;
372         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
373                 ret = KRB5KDC_ERR_POLICY;
374         else
375                 ret = KRB5KDC_ERR_POLICY;
376
377         return ret;
378 }
379
380 /* Given a kdc entry, consult the account_ok routine in auth/auth_sam.c
381  * for consistency */
382 NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry,
383                                        const char *client_name,
384                                        const char *workstation,
385                                        bool password_change)
386 {
387         TALLOC_CTX *tmp_ctx;
388         NTSTATUS nt_status;
389
390         tmp_ctx = talloc_named(NULL, 0, "samba_kdc_check_client_access");
391         if (!tmp_ctx) {
392                 return NT_STATUS_NO_MEMORY;
393         }
394
395         /* we allow all kinds of trusts here */
396         nt_status = authsam_account_ok(tmp_ctx,
397                                        kdc_entry->kdc_db_ctx->samdb,
398                                        MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
399                                        MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
400                                        kdc_entry->realm_dn, kdc_entry->msg,
401                                        workstation, client_name,
402                                        true, password_change);
403
404         talloc_free(tmp_ctx);
405         return nt_status;
406 }
407