Line data Source code
1 : /*
2 : SSSD
3 :
4 : PAC Responder - utility finctions
5 :
6 : Copyright (C) Sumit Bose <sbose@redhat.com> 2012
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 : #include <sys/types.h>
22 : #include <stdbool.h>
23 : #include <util/data_blob.h>
24 : #include <gen_ndr/security.h>
25 :
26 : #include "util/util.h"
27 : #include "responder/pac/pacsrv.h"
28 :
29 0 : errno_t get_sids_from_pac(TALLOC_CTX *mem_ctx,
30 : struct pac_ctx *pac_ctx,
31 : struct PAC_LOGON_INFO *logon_info,
32 : char **_user_sid_str,
33 : char **_primary_group_sid_str,
34 : hash_table_t **_sid_table)
35 : {
36 : int ret;
37 : size_t s;
38 : struct netr_SamInfo3 *info3;
39 : struct sss_domain_info *user_dom;
40 : struct sss_domain_info *group_dom;
41 0 : char *sid_str = NULL;
42 0 : char *msid_str = NULL;
43 0 : char *user_dom_sid_str = NULL;
44 : size_t user_dom_sid_str_len;
45 : enum idmap_error_code err;
46 0 : hash_table_t *sid_table = NULL;
47 : hash_key_t key;
48 : hash_value_t value;
49 : char *rid_start;
50 0 : struct ldb_result *msg = NULL;
51 0 : char *user_sid_str = NULL;
52 0 : char *primary_group_sid_str = NULL;
53 :
54 0 : if (pac_ctx == NULL || logon_info == NULL || _sid_table == NULL) {
55 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing parameter.\n");
56 0 : return EINVAL;
57 : }
58 :
59 0 : info3 = &logon_info->info3;
60 :
61 0 : ret = sss_hash_create(mem_ctx,
62 0 : info3->sidcount + info3->base.groups.count + 2,
63 : &sid_table);
64 0 : if (ret != EOK) {
65 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
66 0 : goto done;
67 : }
68 :
69 0 : key.type = HASH_KEY_STRING;
70 0 : value.type = HASH_VALUE_ULONG;
71 :
72 0 : err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, info3->base.domain_sid,
73 : &user_dom_sid_str);
74 0 : if (err != IDMAP_SUCCESS) {
75 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
76 0 : ret = EFAULT;
77 0 : goto done;
78 : }
79 :
80 0 : ret = responder_get_domain_by_id(pac_ctx->rctx, user_dom_sid_str,
81 : &user_dom);
82 0 : if (ret != EOK) {
83 0 : DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
84 0 : ret = EINVAL;
85 0 : goto done;
86 : }
87 :
88 0 : user_dom_sid_str_len = strlen(user_dom_sid_str);
89 0 : sid_str = talloc_zero_size(mem_ctx, user_dom_sid_str_len + 12);
90 0 : if (sid_str == NULL) {
91 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
92 0 : ret = ENOMEM;
93 0 : goto done;
94 : }
95 0 : rid_start = sid_str + user_dom_sid_str_len;
96 :
97 0 : memcpy(sid_str, user_dom_sid_str, user_dom_sid_str_len);
98 :
99 0 : memset(rid_start, '\0', 12);
100 0 : ret = snprintf(rid_start, 12, "-%lu",
101 0 : (unsigned long) info3->base.rid);
102 0 : if (ret < 0 || ret > 12) {
103 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
104 0 : ret = EIO;
105 0 : goto done;
106 : }
107 :
108 0 : user_sid_str = talloc_strdup(mem_ctx, sid_str);
109 0 : if (user_sid_str == NULL) {
110 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
111 0 : ret = ENOMEM;
112 0 : goto done;
113 : }
114 :
115 0 : key.str = sid_str;
116 0 : value.ul = 0;
117 :
118 0 : ret = sysdb_search_object_by_sid(mem_ctx, user_dom, sid_str, NULL, &msg);
119 0 : if (ret == EOK && msg->count == 1) {
120 0 : value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_UIDNUM, 0);
121 : }
122 0 : talloc_zfree(msg);
123 :
124 0 : ret = hash_enter(sid_table, &key, &value);
125 0 : if (ret != HASH_SUCCESS) {
126 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
127 : ret, hash_error_string(ret));
128 0 : ret = EIO;
129 0 : goto done;
130 : }
131 :
132 :
133 0 : memset(rid_start, '\0', 12);
134 0 : ret = snprintf(rid_start, 12, "-%lu",
135 0 : (unsigned long) info3->base.primary_gid);
136 0 : if (ret < 0 || ret > 12) {
137 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
138 0 : ret = EIO;
139 0 : goto done;
140 : }
141 :
142 0 : primary_group_sid_str = talloc_strdup(mem_ctx, sid_str);
143 0 : if (primary_group_sid_str == NULL) {
144 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
145 0 : ret = ENOMEM;
146 0 : goto done;
147 : }
148 :
149 0 : key.str = sid_str;
150 0 : value.ul = 0;
151 :
152 0 : ret = sysdb_search_object_by_sid(mem_ctx, user_dom, sid_str, NULL, &msg);
153 0 : if (ret == EOK && msg->count == 1) {
154 0 : value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0], SYSDB_GIDNUM, 0);
155 : }
156 0 : talloc_zfree(msg);
157 :
158 0 : ret = hash_enter(sid_table, &key, &value);
159 0 : if (ret != HASH_SUCCESS) {
160 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
161 : ret, hash_error_string(ret));
162 0 : ret = EIO;
163 0 : goto done;
164 : }
165 :
166 :
167 0 : for (s = 0; s < info3->base.groups.count; s++) {
168 0 : memset(rid_start, '\0', 12);
169 0 : ret = snprintf(rid_start, 12, "-%lu",
170 0 : (unsigned long) info3->base.groups.rids[s].rid);
171 0 : if (ret < 0 || ret > 12) {
172 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed.\n");
173 0 : ret = EIO;
174 0 : goto done;
175 : }
176 :
177 0 : key.str = sid_str;
178 0 : value.ul = 0;
179 :
180 0 : ret = sysdb_search_object_by_sid(mem_ctx, user_dom, sid_str,
181 : NULL, &msg);
182 0 : if (ret == EOK && msg->count == 1) {
183 0 : value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0],
184 : SYSDB_GIDNUM, 0);
185 : }
186 0 : talloc_zfree(msg);
187 :
188 0 : ret = hash_enter(sid_table, &key, &value);
189 0 : if (ret != HASH_SUCCESS) {
190 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
191 : ret, hash_error_string(ret));
192 0 : ret = EIO;
193 0 : goto done;
194 : }
195 :
196 : }
197 :
198 0 : for(s = 0; s < info3->sidcount; s++) {
199 0 : err = sss_idmap_smb_sid_to_sid(pac_ctx->idmap_ctx, info3->sids[s].sid,
200 : &msid_str);
201 0 : if (err != IDMAP_SUCCESS) {
202 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
203 0 : ret = EFAULT;
204 0 : goto done;
205 : }
206 :
207 0 : key.str = msid_str;
208 0 : value.ul = 0;
209 :
210 0 : ret = responder_get_domain_by_id(pac_ctx->rctx, msid_str, &group_dom);
211 0 : if (ret == EOK) {
212 0 : ret = sysdb_search_object_by_sid(mem_ctx, group_dom, msid_str,
213 : NULL, &msg);
214 0 : if (ret == EOK && msg->count == 1 ) {
215 0 : value.ul = ldb_msg_find_attr_as_uint64(msg->msgs[0],
216 : SYSDB_GIDNUM, 0);
217 : }
218 0 : talloc_zfree(msg);
219 : }
220 :
221 0 : ret = hash_enter(sid_table, &key, &value);
222 0 : sss_idmap_free_sid(pac_ctx->idmap_ctx, msid_str);
223 0 : if (ret != HASH_SUCCESS) {
224 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
225 : ret, hash_error_string(ret));
226 0 : ret = EIO;
227 0 : goto done;
228 : }
229 : }
230 :
231 0 : ret = EOK;
232 :
233 : done:
234 0 : talloc_free(sid_str);
235 0 : sss_idmap_free_sid(pac_ctx->idmap_ctx, user_dom_sid_str);
236 :
237 0 : if (ret == EOK) {
238 0 : *_sid_table = sid_table;
239 0 : *_user_sid_str = user_sid_str;
240 0 : *_primary_group_sid_str = primary_group_sid_str;
241 : } else {
242 0 : hash_destroy(sid_table);
243 0 : talloc_free(user_sid_str);
244 0 : talloc_free(primary_group_sid_str);
245 : }
246 :
247 0 : return ret;
248 : }
249 :
250 : /**
251 : * Extract the PAC logon data from an NDR blob.
252 : */
253 0 : errno_t get_data_from_pac(TALLOC_CTX *mem_ctx,
254 : uint8_t *pac_blob, size_t pac_len,
255 : struct PAC_LOGON_INFO **_logon_info)
256 : {
257 : DATA_BLOB blob;
258 : struct ndr_pull *ndr_pull;
259 : struct PAC_DATA *pac_data;
260 : enum ndr_err_code ndr_err;
261 : size_t c;
262 : int ret;
263 :
264 0 : blob.data = pac_blob;
265 0 : blob.length = pac_len;
266 :
267 0 : ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
268 0 : if (ndr_pull == NULL) {
269 0 : DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob failed.\n");
270 0 : return ENOMEM;
271 : }
272 0 : ndr_pull->flags |= LIBNDR_FLAG_REF_ALLOC; /* FIXME: is this really needed ? */
273 :
274 0 : pac_data = talloc_zero(mem_ctx, struct PAC_DATA);
275 0 : if (pac_data == NULL) {
276 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
277 0 : return ENOMEM;
278 : }
279 :
280 0 : ndr_err = ndr_pull_PAC_DATA(ndr_pull, NDR_SCALARS|NDR_BUFFERS, pac_data);
281 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
282 0 : DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_PAC_DATA failed [%d]\n", ndr_err);
283 0 : return EBADMSG;
284 : }
285 :
286 0 : for(c = 0; c < pac_data->num_buffers; c++) {
287 0 : if (pac_data->buffers[c].type == PAC_TYPE_LOGON_INFO) {
288 0 : *_logon_info = pac_data->buffers[c].info->logon_info.info;
289 :
290 0 : return EOK;
291 : }
292 : }
293 :
294 0 : ret = EINVAL;
295 :
296 0 : talloc_free(pac_data);
297 0 : return ret;
298 : }
299 :
300 : /**
301 : * Fill up the passwd struct with data from the PAC logon info
302 : */
303 0 : errno_t get_pwd_from_pac(TALLOC_CTX *mem_ctx,
304 : struct sss_domain_info *dom,
305 : char *user_sid_str,
306 : char *primary_group_sid_str,
307 : hash_table_t *sid_table,
308 : struct PAC_LOGON_INFO *logon_info,
309 : struct passwd **_pwd,
310 : struct sysdb_attrs **_attrs)
311 : {
312 0 : struct passwd *pwd = NULL;
313 0 : struct sysdb_attrs *attrs = NULL;
314 : struct netr_SamBaseInfo *base_info;
315 : int ret;
316 : char *lname;
317 : char *uc_realm;
318 : char *upn;
319 : hash_key_t key;
320 : hash_value_t value;
321 : struct sss_nss_homedir_ctx homedir_ctx;
322 :
323 0 : pwd = talloc_zero(mem_ctx, struct passwd);
324 0 : if (pwd == NULL) {
325 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
326 0 : return ENOMEM;
327 : }
328 :
329 0 : base_info = &logon_info->info3.base;
330 :
331 0 : if (base_info->account_name.size == 0) {
332 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing account name in PAC.\n");
333 0 : ret = EINVAL;
334 0 : goto done;
335 : }
336 0 : if (base_info->rid == 0) {
337 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing user RID in PAC.\n");
338 0 : ret = EINVAL;
339 0 : goto done;
340 : }
341 :
342 : /* To be compatible with winbind based lookups we have to use lower
343 : * case names only, effectively making the domain case-insenvitive. */
344 0 : lname = sss_tc_utf8_str_tolower(pwd, base_info->account_name.string);
345 0 : if (lname == NULL) {
346 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_tc_utf8_str_tolower failed.\n");
347 0 : ret = ENOMEM;
348 0 : goto done;
349 : }
350 :
351 : /* Subdomain use fully qualified names */
352 0 : pwd->pw_name = sss_get_domain_name(pwd, lname, dom);
353 0 : if (!pwd->pw_name) {
354 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_sprintf failed.\n");
355 0 : ret = ENOMEM;
356 0 : goto done;
357 : }
358 :
359 0 : key.type = HASH_KEY_STRING;
360 0 : key.str = user_sid_str;
361 0 : ret = hash_lookup(sid_table, &key, &value);
362 0 : if (ret != HASH_SUCCESS) {
363 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed.\n");
364 0 : ret = EIO;
365 0 : goto done;
366 : }
367 0 : if (value.type != HASH_VALUE_ULONG) {
368 0 : DEBUG(SSSDBG_OP_FAILURE, "Wrong value type.\n");
369 0 : ret = EIO;
370 0 : goto done;
371 : }
372 0 : pwd->pw_uid = value.ul;
373 :
374 0 : if (IS_SUBDOMAIN(dom) || dom->mpg) {
375 0 : pwd->pw_gid = 0; /* We use MPGs for sub-domains */
376 : } else {
377 0 : key.type = HASH_KEY_STRING;
378 0 : key.str = primary_group_sid_str;
379 0 : ret = hash_lookup(sid_table, &key, &value);
380 0 : if (ret != HASH_SUCCESS) {
381 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_lookup failed.\n");
382 0 : ret = EIO;
383 0 : goto done;
384 : }
385 0 : if (value.type != HASH_VALUE_ULONG) {
386 0 : DEBUG(SSSDBG_OP_FAILURE, "Wrong value type.\n");
387 0 : ret = EIO;
388 0 : goto done;
389 : }
390 0 : pwd->pw_gid = value.ul;
391 : }
392 :
393 0 : if (base_info->full_name.size != 0) {
394 0 : pwd->pw_gecos = talloc_strdup(pwd, base_info->full_name.string);
395 0 : if (pwd->pw_gecos == NULL) {
396 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
397 0 : ret = ENOMEM;
398 0 : goto done;
399 : }
400 : } else {
401 0 : DEBUG(SSSDBG_OP_FAILURE,
402 : "Missing full name in PAC, gecos field will by empty.\n");
403 : }
404 :
405 : /* Check if there is a special homedir template for sub-domains. If not a
406 : * fallback will be added by the NSS responder. */
407 0 : if (IS_SUBDOMAIN(dom) && dom->subdomain_homedir) {
408 0 : ZERO_STRUCT(homedir_ctx);
409 :
410 0 : homedir_ctx.username = lname;
411 0 : homedir_ctx.uid = pwd->pw_uid;
412 0 : homedir_ctx.domain = dom->name;
413 0 : homedir_ctx.flatname = dom->flat_name;
414 0 : homedir_ctx.config_homedir_substr = dom->homedir_substr;
415 :
416 0 : pwd->pw_dir = expand_homedir_template(pwd, dom->subdomain_homedir,
417 : &homedir_ctx);
418 0 : if (pwd->pw_dir == NULL) {
419 0 : ret = ENOMEM;
420 0 : goto done;
421 : }
422 : }
423 :
424 0 : pwd->pw_shell = NULL; /* Using default */
425 :
426 0 : attrs = sysdb_new_attrs(mem_ctx);
427 0 : if (attrs == NULL) {
428 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
429 0 : ret = ENOMEM;
430 0 : goto done;
431 : }
432 :
433 0 : uc_realm = get_uppercase_realm(mem_ctx, dom->name);
434 0 : if (uc_realm == NULL) {
435 0 : DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
436 0 : ret = ENOMEM;
437 0 : goto done;
438 : }
439 :
440 0 : upn = talloc_asprintf(mem_ctx, "%s@%s", lname, uc_realm);
441 0 : talloc_free(uc_realm);
442 0 : if (upn == NULL) {
443 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
444 0 : ret = ENOMEM;
445 0 : goto done;
446 : }
447 :
448 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_UPN, upn);
449 0 : talloc_free(upn);
450 0 : if (ret != EOK) {
451 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
452 0 : goto done;
453 : }
454 :
455 0 : ret = sysdb_attrs_add_lc_name_alias(attrs, pwd->pw_name);
456 0 : if (ret != EOK) {
457 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
458 0 : goto done;
459 : }
460 :
461 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, user_sid_str);
462 0 : if (ret != EOK) {
463 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
464 0 : goto done;
465 : }
466 :
467 0 : *_pwd = pwd;
468 0 : *_attrs = attrs;
469 :
470 0 : ret = EOK;
471 :
472 : done:
473 0 : if (ret != EOK) {
474 0 : talloc_free(pwd);
475 : }
476 :
477 0 : return ret;
478 : }
|