1 /*
2    Unix SMB/CIFS implementation.
3 
4    Winbind client API
5 
6    Copyright (C) Gerald (Jerry) Carter 2007
7    Copyright (C) Volker Lendecke 2010
8 
9 
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14 
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19 
20    You should have received a copy of the GNU Lesser General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23 
24 /* Required Headers */
25 
26 #include "replace.h"
27 #include "libwbclient.h"
28 #include "../winbind_client.h"
29 #include "lib/util/util.h"
30 
31 /* Convert a sid to a string into a buffer. Return the string
32  * length. If buflen is too small, return the string length that would
33  * result if it was long enough. */
wbcSidToStringBuf(const struct wbcDomainSid * sid,char * buf,int buflen)34 int wbcSidToStringBuf(const struct wbcDomainSid *sid, char *buf, int buflen)
35 {
36 	uint64_t id_auth;
37 	int i, ofs;
38 
39 	if (!sid) {
40 		strlcpy(buf, "(NULL SID)", buflen);
41 		return 10;	/* strlen("(NULL SID)") */
42 	}
43 
44 	id_auth = (uint64_t)sid->id_auth[5] +
45 		((uint64_t)sid->id_auth[4] << 8) +
46 		((uint64_t)sid->id_auth[3] << 16) +
47 		((uint64_t)sid->id_auth[2] << 24) +
48 		((uint64_t)sid->id_auth[1] << 32) +
49 		((uint64_t)sid->id_auth[0] << 40);
50 
51 	ofs = snprintf(buf, buflen, "S-%hhu-", (unsigned char)sid->sid_rev_num);
52 	if (id_auth >= UINT32_MAX) {
53 		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "0x%llx",
54 				(unsigned long long)id_auth);
55 	} else {
56 		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "%llu",
57 				(unsigned long long)id_auth);
58 	}
59 
60 	for (i = 0; i < sid->num_auths; i++) {
61 		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "-%u",
62 				(unsigned int)sid->sub_auths[i]);
63 	}
64 	return ofs;
65 }
66 
67 /* Convert a binary SID to a character string */
wbcSidToString(const struct wbcDomainSid * sid,char ** sid_string)68 wbcErr wbcSidToString(const struct wbcDomainSid *sid,
69 		      char **sid_string)
70 {
71 	char buf[WBC_SID_STRING_BUFLEN];
72 	char *result;
73 	int len;
74 
75 	if (!sid) {
76 		return WBC_ERR_INVALID_SID;
77 	}
78 
79 	len = wbcSidToStringBuf(sid, buf, sizeof(buf));
80 
81 	if (len+1 > sizeof(buf)) {
82 		return WBC_ERR_INVALID_SID;
83 	}
84 
85 	result = (char *)wbcAllocateMemory(len+1, 1, NULL);
86 	if (result == NULL) {
87 		return WBC_ERR_NO_MEMORY;
88 	}
89 	memcpy(result, buf, len+1);
90 
91 	*sid_string = result;
92 	return WBC_ERR_SUCCESS;
93 }
94 
95 #define AUTHORITY_MASK	(~(0xffffffffffffULL))
96 
97 /* Convert a character string to a binary SID */
wbcStringToSid(const char * str,struct wbcDomainSid * sid)98 wbcErr wbcStringToSid(const char *str,
99 		      struct wbcDomainSid *sid)
100 {
101 	const char *p;
102 	char *q;
103 	int error = 0;
104 	uint64_t x;
105 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
106 
107 	if (!sid) {
108 		wbc_status = WBC_ERR_INVALID_PARAM;
109 		BAIL_ON_WBC_ERROR(wbc_status);
110 	}
111 
112 	/* Sanity check for either "S-" or "s-" */
113 
114 	if (!str
115 	    || (str[0]!='S' && str[0]!='s')
116 	    || (str[1]!='-'))
117 	{
118 		wbc_status = WBC_ERR_INVALID_PARAM;
119 		BAIL_ON_WBC_ERROR(wbc_status);
120 	}
121 
122 	/* Get the SID revision number */
123 
124 	p = str+2;
125 	x = (uint64_t)smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
126 	if (x == 0 || x > UINT8_MAX || !q || *q != '-' || error != 0) {
127 		wbc_status = WBC_ERR_INVALID_SID;
128 		BAIL_ON_WBC_ERROR(wbc_status);
129 	}
130 	sid->sid_rev_num = (uint8_t)x;
131 
132 	/*
133 	 * Next the Identifier Authority.  This is stored big-endian in a
134 	 * 6 byte array. If the authority value is >= UINT_MAX, then it should
135 	 * be expressed as a hex value, according to MS-DTYP.
136 	 */
137 	p = q+1;
138 	x = smb_strtoull(p, &q, 0, &error, SMB_STR_STANDARD);
139 	if (!q || *q != '-' || (x & AUTHORITY_MASK) || error != 0) {
140 		wbc_status = WBC_ERR_INVALID_SID;
141 		BAIL_ON_WBC_ERROR(wbc_status);
142 	}
143 	sid->id_auth[5] = (x & 0x0000000000ffULL);
144 	sid->id_auth[4] = (x & 0x00000000ff00ULL) >> 8;
145 	sid->id_auth[3] = (x & 0x000000ff0000ULL) >> 16;
146 	sid->id_auth[2] = (x & 0x0000ff000000ULL) >> 24;
147 	sid->id_auth[1] = (x & 0x00ff00000000ULL) >> 32;
148 	sid->id_auth[0] = (x & 0xff0000000000ULL) >> 40;
149 
150 	/* now read the the subauthorities */
151 	p = q +1;
152 	sid->num_auths = 0;
153 	while (sid->num_auths < WBC_MAXSUBAUTHS) {
154 		x = smb_strtoull(p, &q, 10, &error, SMB_STR_ALLOW_NO_CONVERSION);
155 		if (p == q)
156 			break;
157 		if (x > UINT32_MAX || error != 0) {
158 			wbc_status = WBC_ERR_INVALID_SID;
159 			BAIL_ON_WBC_ERROR(wbc_status);
160 		}
161 		sid->sub_auths[sid->num_auths++] = x;
162 
163 		if (*q != '-') {
164 			break;
165 		}
166 		p = q + 1;
167 	}
168 
169 	/* IF we ended early, then the SID could not be converted */
170 
171 	if (q && *q!='\0') {
172 		wbc_status = WBC_ERR_INVALID_SID;
173 		BAIL_ON_WBC_ERROR(wbc_status);
174 	}
175 
176 	wbc_status = WBC_ERR_SUCCESS;
177 
178 done:
179 	return wbc_status;
180 
181 }
182 
183 
184 /* Convert a domain and name to SID */
wbcCtxLookupName(struct wbcContext * ctx,const char * domain,const char * name,struct wbcDomainSid * sid,enum wbcSidType * name_type)185 wbcErr wbcCtxLookupName(struct wbcContext *ctx,
186 			const char *domain,
187 			const char *name,
188 			struct wbcDomainSid *sid,
189 			enum wbcSidType *name_type)
190 {
191 	struct winbindd_request request;
192 	struct winbindd_response response;
193 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
194 
195 	if (!sid || !name_type) {
196 		wbc_status = WBC_ERR_INVALID_PARAM;
197 		BAIL_ON_WBC_ERROR(wbc_status);
198 	}
199 
200 	/* Initialize request */
201 
202 	ZERO_STRUCT(request);
203 	ZERO_STRUCT(response);
204 
205 	/* dst is already null terminated from the memset above */
206 
207 	strncpy(request.data.name.dom_name, domain,
208 		sizeof(request.data.name.dom_name)-1);
209 	strncpy(request.data.name.name, name,
210 		sizeof(request.data.name.name)-1);
211 
212 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPNAME,
213 					&request,
214 					&response);
215 	BAIL_ON_WBC_ERROR(wbc_status);
216 
217 	wbc_status = wbcStringToSid(response.data.sid.sid, sid);
218 	BAIL_ON_WBC_ERROR(wbc_status);
219 
220 	*name_type = (enum wbcSidType)response.data.sid.type;
221 
222 	wbc_status = WBC_ERR_SUCCESS;
223 
224  done:
225 	return wbc_status;
226 }
227 
wbcLookupName(const char * domain,const char * name,struct wbcDomainSid * sid,enum wbcSidType * name_type)228 wbcErr wbcLookupName(const char *domain,
229 		     const char *name,
230 		     struct wbcDomainSid *sid,
231 		     enum wbcSidType *name_type)
232 {
233 	return wbcCtxLookupName(NULL, domain, name, sid, name_type);
234 }
235 
236 
237 /* Convert a SID to a domain and name */
wbcCtxLookupSid(struct wbcContext * ctx,const struct wbcDomainSid * sid,char ** pdomain,char ** pname,enum wbcSidType * pname_type)238 wbcErr wbcCtxLookupSid(struct wbcContext *ctx,
239 		       const struct wbcDomainSid *sid,
240 		       char **pdomain,
241 		       char **pname,
242 		       enum wbcSidType *pname_type)
243 {
244 	struct winbindd_request request;
245 	struct winbindd_response response;
246 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
247 	char *domain, *name;
248 
249 	if (!sid) {
250 		return WBC_ERR_INVALID_PARAM;
251 	}
252 
253 	/* Initialize request */
254 
255 	ZERO_STRUCT(request);
256 	ZERO_STRUCT(response);
257 
258 	wbcSidToStringBuf(sid, request.data.sid, sizeof(request.data.sid));
259 
260 	/* Make request */
261 
262 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPSID,
263 					&request,
264 					&response);
265 	if (!WBC_ERROR_IS_OK(wbc_status)) {
266 		return wbc_status;
267 	}
268 
269 	/* Copy out result */
270 
271 	wbc_status = WBC_ERR_NO_MEMORY;
272 	domain = NULL;
273 	name = NULL;
274 
275 	domain = wbcStrDup(response.data.name.dom_name);
276 	if (domain == NULL) {
277 		goto done;
278 	}
279 	name = wbcStrDup(response.data.name.name);
280 	if (name == NULL) {
281 		goto done;
282 	}
283 	if (pdomain != NULL) {
284 		*pdomain = domain;
285 		domain = NULL;
286 	}
287 	if (pname != NULL) {
288 		*pname = name;
289 		name = NULL;
290 	}
291 	if (pname_type != NULL) {
292 		*pname_type = (enum wbcSidType)response.data.name.type;
293 	}
294 	wbc_status = WBC_ERR_SUCCESS;
295 done:
296 	wbcFreeMemory(name);
297 	wbcFreeMemory(domain);
298 	return wbc_status;
299 }
300 
wbcLookupSid(const struct wbcDomainSid * sid,char ** pdomain,char ** pname,enum wbcSidType * pname_type)301 wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
302 		    char **pdomain,
303 		    char **pname,
304 		    enum wbcSidType *pname_type)
305 {
306 	return wbcCtxLookupSid(NULL, sid, pdomain, pname, pname_type);
307 }
308 
wbcDomainInfosDestructor(void * ptr)309 static void wbcDomainInfosDestructor(void *ptr)
310 {
311 	struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
312 
313 	while (i->short_name != NULL) {
314 		wbcFreeMemory(i->short_name);
315 		wbcFreeMemory(i->dns_name);
316 		i += 1;
317 	}
318 }
319 
wbcTranslatedNamesDestructor(void * ptr)320 static void wbcTranslatedNamesDestructor(void *ptr)
321 {
322 	struct wbcTranslatedName *n = (struct wbcTranslatedName *)ptr;
323 
324 	while (n->name != NULL) {
325 		wbcFreeMemory(n->name);
326 		n += 1;
327 	}
328 }
329 
wbcCtxLookupSids(struct wbcContext * ctx,const struct wbcDomainSid * sids,int num_sids,struct wbcDomainInfo ** pdomains,int * pnum_domains,struct wbcTranslatedName ** pnames)330 wbcErr wbcCtxLookupSids(struct wbcContext *ctx,
331 			const struct wbcDomainSid *sids, int num_sids,
332 			struct wbcDomainInfo **pdomains, int *pnum_domains,
333 			struct wbcTranslatedName **pnames)
334 {
335 	struct winbindd_request request;
336 	struct winbindd_response response;
337 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
338 	int buflen, i, extra_len, num_domains, num_names;
339 	char *sidlist, *p, *q, *extra_data;
340 	struct wbcDomainInfo *domains = NULL;
341 	struct wbcTranslatedName *names = NULL;
342 	int error = 0;
343 
344 	buflen = num_sids * (WBC_SID_STRING_BUFLEN + 1) + 1;
345 
346 	sidlist = (char *)malloc(buflen);
347 	if (sidlist == NULL) {
348 		return WBC_ERR_NO_MEMORY;
349 	}
350 
351 	p = sidlist;
352 
353 	for (i=0; i<num_sids; i++) {
354 		int remaining;
355 		int len;
356 
357 		remaining = buflen - (p - sidlist);
358 
359 		len = wbcSidToStringBuf(&sids[i], p, remaining);
360 		if (len > remaining) {
361 			free(sidlist);
362 			return WBC_ERR_UNKNOWN_FAILURE;
363 		}
364 
365 		p += len;
366 		*p++ = '\n';
367 	}
368 	*p++ = '\0';
369 
370 	ZERO_STRUCT(request);
371 	ZERO_STRUCT(response);
372 
373 	request.extra_data.data = sidlist;
374 	request.extra_len = p - sidlist;
375 
376 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPSIDS,
377 					&request, &response);
378 	free(sidlist);
379 	if (!WBC_ERROR_IS_OK(wbc_status)) {
380 		return wbc_status;
381 	}
382 
383 	extra_len = response.length - sizeof(struct winbindd_response);
384 	extra_data = (char *)response.extra_data.data;
385 
386 	if ((extra_len <= 0) || (extra_data[extra_len-1] != '\0')) {
387 		goto wbc_err_invalid;
388 	}
389 
390 	p = extra_data;
391 
392 	num_domains = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
393 	if (*q != '\n' || error != 0) {
394 		goto wbc_err_invalid;
395 	}
396 	p = q+1;
397 
398 	domains = (struct wbcDomainInfo *)wbcAllocateMemory(
399 		num_domains+1, sizeof(struct wbcDomainInfo),
400 		wbcDomainInfosDestructor);
401 	if (domains == NULL) {
402 		wbc_status = WBC_ERR_NO_MEMORY;
403 		goto fail;
404 	}
405 
406 	for (i=0; i<num_domains; i++) {
407 
408 		q = strchr(p, ' ');
409 		if (q == NULL) {
410 			goto wbc_err_invalid;
411 		}
412 		*q = '\0';
413 		wbc_status = wbcStringToSid(p, &domains[i].sid);
414 		if (!WBC_ERROR_IS_OK(wbc_status)) {
415 			goto fail;
416 		}
417 		p = q+1;
418 
419 		q = strchr(p, '\n');
420 		if (q == NULL) {
421 			goto wbc_err_invalid;
422 		}
423 		*q = '\0';
424 		domains[i].short_name = wbcStrDup(p);
425 		if (domains[i].short_name == NULL) {
426 			wbc_status = WBC_ERR_NO_MEMORY;
427 			goto fail;
428 		}
429 		p = q+1;
430 	}
431 
432 	num_names = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
433 	if (*q != '\n' || error != 0) {
434 		goto wbc_err_invalid;
435 	}
436 	p = q+1;
437 
438 	if (num_names != num_sids) {
439 		goto wbc_err_invalid;
440 	}
441 
442 	names = (struct wbcTranslatedName *)wbcAllocateMemory(
443 		num_names+1, sizeof(struct wbcTranslatedName),
444 		wbcTranslatedNamesDestructor);
445 	if (names == NULL) {
446 		wbc_status = WBC_ERR_NO_MEMORY;
447 		goto fail;
448 	}
449 
450 	for (i=0; i<num_names; i++) {
451 
452 		names[i].domain_index = smb_strtoul(p,
453 						    &q,
454 						    10,
455 						    &error,
456 						    SMB_STR_STANDARD);
457 		if (names[i].domain_index < 0 || error != 0) {
458 			goto wbc_err_invalid;
459 		}
460 		if (names[i].domain_index >= num_domains) {
461 			goto wbc_err_invalid;
462 		}
463 
464 		if (*q != ' ') {
465 			goto wbc_err_invalid;
466 		}
467 		p = q+1;
468 
469 		names[i].type = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
470 		if (*q != ' ' || error != 0) {
471 			goto wbc_err_invalid;
472 		}
473 		p = q+1;
474 
475 		q = strchr(p, '\n');
476 		if (q == NULL) {
477 			goto wbc_err_invalid;
478 		}
479 		*q = '\0';
480 		names[i].name = wbcStrDup(p);
481 		if (names[i].name == NULL) {
482 			wbc_status = WBC_ERR_NO_MEMORY;
483 			goto fail;
484 		}
485 		p = q+1;
486 	}
487 	if (*p != '\0') {
488 		goto wbc_err_invalid;
489 	}
490 
491 	*pdomains = domains;
492 	*pnames = names;
493 	winbindd_free_response(&response);
494 	return WBC_ERR_SUCCESS;
495 
496 wbc_err_invalid:
497 	wbc_status = WBC_ERR_INVALID_RESPONSE;
498 fail:
499 	winbindd_free_response(&response);
500 	wbcFreeMemory(domains);
501 	wbcFreeMemory(names);
502 	return wbc_status;
503 }
504 
wbcLookupSids(const struct wbcDomainSid * sids,int num_sids,struct wbcDomainInfo ** pdomains,int * pnum_domains,struct wbcTranslatedName ** pnames)505 wbcErr wbcLookupSids(const struct wbcDomainSid *sids, int num_sids,
506 		     struct wbcDomainInfo **pdomains, int *pnum_domains,
507 		     struct wbcTranslatedName **pnames)
508 {
509 	return wbcCtxLookupSids(NULL, sids, num_sids, pdomains,
510 				pnum_domains, pnames);
511 }
512 
513 /* Translate a collection of RIDs within a domain to names */
514 
wbcCtxLookupRids(struct wbcContext * ctx,struct wbcDomainSid * dom_sid,int num_rids,uint32_t * rids,const char ** pp_domain_name,const char *** pnames,enum wbcSidType ** ptypes)515 wbcErr wbcCtxLookupRids(struct wbcContext *ctx, struct wbcDomainSid *dom_sid,
516 		     int num_rids,
517 		     uint32_t *rids,
518 		     const char **pp_domain_name,
519 		     const char ***pnames,
520 		     enum wbcSidType **ptypes)
521 {
522 	size_t i, len, ridbuf_size;
523 	char *ridlist;
524 	char *p;
525 	int error = 0;
526 	struct winbindd_request request;
527 	struct winbindd_response response;
528 	char *domain_name = NULL;
529 	const char **names = NULL;
530 	enum wbcSidType *types = NULL;
531 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
532 
533 	/* Initialise request */
534 
535 	ZERO_STRUCT(request);
536 	ZERO_STRUCT(response);
537 
538 	if (!dom_sid || (num_rids == 0)) {
539 		wbc_status = WBC_ERR_INVALID_PARAM;
540 		BAIL_ON_WBC_ERROR(wbc_status);
541 	}
542 
543 	wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
544 
545 	/* Even if all the Rids were of maximum 32bit values,
546 	   we would only have 11 bytes per rid in the final array
547 	   ("4294967296" + \n).  Add one more byte for the
548 	   terminating '\0' */
549 
550 	ridbuf_size = (sizeof(char)*11) * num_rids + 1;
551 
552 	ridlist = (char *)malloc(ridbuf_size);
553 	BAIL_ON_PTR_ERROR(ridlist, wbc_status);
554 
555 	len = 0;
556 	for (i=0; i<num_rids; i++) {
557 		len += snprintf(ridlist + len, ridbuf_size - len, "%u\n",
558 				rids[i]);
559 	}
560 	ridlist[len] = '\0';
561 	len += 1;
562 
563 	request.extra_data.data = ridlist;
564 	request.extra_len = len;
565 
566 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPRIDS,
567 					&request,
568 					&response);
569 	free(ridlist);
570 	BAIL_ON_WBC_ERROR(wbc_status);
571 
572 	domain_name = wbcStrDup(response.data.domain_name);
573 	BAIL_ON_PTR_ERROR(domain_name, wbc_status);
574 
575 	names = wbcAllocateStringArray(num_rids);
576 	BAIL_ON_PTR_ERROR(names, wbc_status);
577 
578 	types = (enum wbcSidType *)wbcAllocateMemory(
579 		num_rids, sizeof(enum wbcSidType), NULL);
580 	BAIL_ON_PTR_ERROR(types, wbc_status);
581 
582 	p = (char *)response.extra_data.data;
583 
584 	for (i=0; i<num_rids; i++) {
585 		char *q;
586 
587 		if (*p == '\0') {
588 			wbc_status = WBC_ERR_INVALID_RESPONSE;
589 			goto done;
590 		}
591 
592 		types[i] = (enum wbcSidType)smb_strtoul(p,
593 							&q,
594 							10,
595 							&error,
596 							SMB_STR_STANDARD);
597 
598 		if (*q != ' ' || error != 0) {
599 			wbc_status = WBC_ERR_INVALID_RESPONSE;
600 			goto done;
601 		}
602 
603 		p = q+1;
604 
605 		if ((q = strchr(p, '\n')) == NULL) {
606 			wbc_status = WBC_ERR_INVALID_RESPONSE;
607 			goto done;
608 		}
609 
610 		*q = '\0';
611 
612 		names[i] = strdup(p);
613 		BAIL_ON_PTR_ERROR(names[i], wbc_status);
614 
615 		p = q+1;
616 	}
617 
618 	if (*p != '\0') {
619 		wbc_status = WBC_ERR_INVALID_RESPONSE;
620 		goto done;
621 	}
622 
623 	wbc_status = WBC_ERR_SUCCESS;
624 
625  done:
626 	winbindd_free_response(&response);
627 
628 	if (WBC_ERROR_IS_OK(wbc_status)) {
629 		*pp_domain_name = domain_name;
630 		*pnames = names;
631 		*ptypes = types;
632 	}
633 	else {
634 		wbcFreeMemory(domain_name);
635 		wbcFreeMemory(names);
636 		wbcFreeMemory(types);
637 	}
638 
639 	return wbc_status;
640 }
641 
wbcLookupRids(struct wbcDomainSid * dom_sid,int num_rids,uint32_t * rids,const char ** pp_domain_name,const char *** pnames,enum wbcSidType ** ptypes)642 wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
643 		     int num_rids,
644 		     uint32_t *rids,
645 		     const char **pp_domain_name,
646 		     const char ***pnames,
647 		     enum wbcSidType **ptypes)
648 {
649 	return wbcCtxLookupRids(NULL, dom_sid, num_rids, rids,
650 				pp_domain_name, pnames, ptypes);
651 }
652 
653 /* Get the groups a user belongs to */
wbcCtxLookupUserSids(struct wbcContext * ctx,const struct wbcDomainSid * user_sid,bool domain_groups_only,uint32_t * num_sids,struct wbcDomainSid ** _sids)654 wbcErr wbcCtxLookupUserSids(struct wbcContext *ctx,
655 			    const struct wbcDomainSid *user_sid,
656 			    bool domain_groups_only,
657 			    uint32_t *num_sids,
658 			    struct wbcDomainSid **_sids)
659 {
660 	uint32_t i;
661 	const char *s;
662 	struct winbindd_request request;
663 	struct winbindd_response response;
664 	struct wbcDomainSid *sids = NULL;
665 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
666 	int cmd;
667 
668 	/* Initialise request */
669 
670 	ZERO_STRUCT(request);
671 	ZERO_STRUCT(response);
672 
673 	if (!user_sid) {
674 		wbc_status = WBC_ERR_INVALID_PARAM;
675 		BAIL_ON_WBC_ERROR(wbc_status);
676 	}
677 
678 	wbcSidToStringBuf(user_sid, request.data.sid, sizeof(request.data.sid));
679 
680 	if (domain_groups_only) {
681 		cmd = WINBINDD_GETUSERDOMGROUPS;
682 	} else {
683 		cmd = WINBINDD_GETUSERSIDS;
684 	}
685 
686 	wbc_status = wbcRequestResponse(ctx, cmd,
687 					&request,
688 					&response);
689 	BAIL_ON_WBC_ERROR(wbc_status);
690 
691 	if (response.data.num_entries &&
692 	    !response.extra_data.data) {
693 		wbc_status = WBC_ERR_INVALID_RESPONSE;
694 		BAIL_ON_WBC_ERROR(wbc_status);
695 	}
696 
697 	sids = (struct wbcDomainSid *)wbcAllocateMemory(
698 		response.data.num_entries, sizeof(struct wbcDomainSid),
699 		NULL);
700 	BAIL_ON_PTR_ERROR(sids, wbc_status);
701 
702 	s = (const char *)response.extra_data.data;
703 	for (i = 0; i < response.data.num_entries; i++) {
704 		char *n = strchr(s, '\n');
705 		if (n) {
706 			*n = '\0';
707 		}
708 		wbc_status = wbcStringToSid(s, &sids[i]);
709 		BAIL_ON_WBC_ERROR(wbc_status);
710 		s += strlen(s) + 1;
711 	}
712 
713 	*num_sids = response.data.num_entries;
714 	*_sids = sids;
715 	sids = NULL;
716 	wbc_status = WBC_ERR_SUCCESS;
717 
718  done:
719 	winbindd_free_response(&response);
720 	if (sids) {
721 		wbcFreeMemory(sids);
722 	}
723 
724 	return wbc_status;
725 }
726 
wbcLookupUserSids(const struct wbcDomainSid * user_sid,bool domain_groups_only,uint32_t * num_sids,struct wbcDomainSid ** _sids)727 wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
728 			 bool domain_groups_only,
729 			 uint32_t *num_sids,
730 			 struct wbcDomainSid **_sids)
731 {
732 	return wbcCtxLookupUserSids(NULL, user_sid, domain_groups_only,
733 				    num_sids, _sids);
734 }
735 
736 static inline
_sid_to_rid(struct wbcDomainSid * sid,uint32_t * rid)737 wbcErr _sid_to_rid(struct wbcDomainSid *sid, uint32_t *rid)
738 {
739 	if (sid->num_auths < 1) {
740 		return WBC_ERR_INVALID_RESPONSE;
741 	}
742 	*rid = sid->sub_auths[sid->num_auths - 1];
743 
744 	return WBC_ERR_SUCCESS;
745 }
746 
747 /* Get alias membership for sids */
wbcCtxGetSidAliases(struct wbcContext * ctx,const struct wbcDomainSid * dom_sid,struct wbcDomainSid * sids,uint32_t num_sids,uint32_t ** alias_rids,uint32_t * num_alias_rids)748 wbcErr wbcCtxGetSidAliases(struct wbcContext *ctx,
749 			   const struct wbcDomainSid *dom_sid,
750 			   struct wbcDomainSid *sids,
751 			   uint32_t num_sids,
752 			   uint32_t **alias_rids,
753 			   uint32_t *num_alias_rids)
754 {
755 	uint32_t i;
756 	const char *s;
757 	struct winbindd_request request;
758 	struct winbindd_response response;
759 	ssize_t extra_data_len = 0;
760 	char * extra_data = NULL;
761 	ssize_t buflen = 0;
762 	struct wbcDomainSid sid;
763 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
764 	uint32_t * rids = NULL;
765 
766 	/* Initialise request */
767 
768 	ZERO_STRUCT(request);
769 	ZERO_STRUCT(response);
770 
771 	if (!dom_sid) {
772 		wbc_status = WBC_ERR_INVALID_PARAM;
773 		goto done;
774 	}
775 
776 	wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
777 
778 	/* Lets assume each sid is around 57 characters
779 	 * S-1-5-21-AAAAAAAAAAA-BBBBBBBBBBB-CCCCCCCCCCC-DDDDDDDDDDD\n */
780 	buflen = 57 * num_sids;
781 	extra_data = (char *)malloc(buflen);
782 	if (!extra_data) {
783 		wbc_status = WBC_ERR_NO_MEMORY;
784 		goto done;
785 	}
786 
787 	/* Build the sid list */
788 	for (i=0; i<num_sids; i++) {
789 		char sid_str[WBC_SID_STRING_BUFLEN];
790 		size_t sid_len;
791 
792 		sid_len = wbcSidToStringBuf(&sids[i], sid_str, sizeof(sid_str));
793 
794 		if (buflen < extra_data_len + sid_len + 2) {
795 			char * tmp_data = NULL;
796 			buflen *= 2;
797 			tmp_data = (char *)realloc(extra_data, buflen);
798 			if (!tmp_data) {
799 				wbc_status = WBC_ERR_NO_MEMORY;
800 				BAIL_ON_WBC_ERROR(wbc_status);
801 			}
802 			extra_data = tmp_data;
803 		}
804 
805 		strncpy(&extra_data[extra_data_len], sid_str,
806 			buflen - extra_data_len);
807 		extra_data_len += sid_len;
808 		extra_data[extra_data_len++] = '\n';
809 		extra_data[extra_data_len] = '\0';
810 	}
811 	extra_data_len += 1;
812 
813 	request.extra_data.data = extra_data;
814 	request.extra_len = extra_data_len;
815 
816 	wbc_status = wbcRequestResponse(ctx, WINBINDD_GETSIDALIASES,
817 					&request,
818 					&response);
819 	BAIL_ON_WBC_ERROR(wbc_status);
820 
821 	if (response.data.num_entries &&
822 	    !response.extra_data.data) {
823 		wbc_status = WBC_ERR_INVALID_RESPONSE;
824 		goto done;
825 	}
826 
827 	rids = (uint32_t *)wbcAllocateMemory(response.data.num_entries,
828 					     sizeof(uint32_t), NULL);
829 	BAIL_ON_PTR_ERROR(rids, wbc_status);
830 
831 	s = (const char *)response.extra_data.data;
832 	for (i = 0; i < response.data.num_entries; i++) {
833 		char *n = strchr(s, '\n');
834 		if (n) {
835 			*n = '\0';
836 		}
837 		wbc_status = wbcStringToSid(s, &sid);
838 		BAIL_ON_WBC_ERROR(wbc_status);
839 		wbc_status = _sid_to_rid(&sid, &rids[i]);
840 		BAIL_ON_WBC_ERROR(wbc_status);
841 		s += strlen(s) + 1;
842 	}
843 
844 	*num_alias_rids = response.data.num_entries;
845 	*alias_rids = rids;
846 	rids = NULL;
847 	wbc_status = WBC_ERR_SUCCESS;
848 
849  done:
850 	free(extra_data);
851 	winbindd_free_response(&response);
852 	wbcFreeMemory(rids);
853 	return wbc_status;
854 }
855 
wbcGetSidAliases(const struct wbcDomainSid * dom_sid,struct wbcDomainSid * sids,uint32_t num_sids,uint32_t ** alias_rids,uint32_t * num_alias_rids)856 wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
857 			struct wbcDomainSid *sids,
858 			uint32_t num_sids,
859 			uint32_t **alias_rids,
860 			uint32_t *num_alias_rids)
861 {
862 	return wbcCtxGetSidAliases(NULL, dom_sid, sids, num_sids,
863 				   alias_rids, num_alias_rids);
864 }
865 
866 
867 /* Lists Users */
wbcCtxListUsers(struct wbcContext * ctx,const char * domain_name,uint32_t * _num_users,const char *** _users)868 wbcErr wbcCtxListUsers(struct wbcContext *ctx,
869 		       const char *domain_name,
870 		       uint32_t *_num_users,
871 		       const char ***_users)
872 {
873 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
874 	struct winbindd_request request;
875 	struct winbindd_response response;
876 	uint32_t num_users = 0;
877 	const char **users = NULL;
878 	const char *next;
879 
880 	/* Initialise request */
881 
882 	ZERO_STRUCT(request);
883 	ZERO_STRUCT(response);
884 
885 	if (domain_name) {
886 		strncpy(request.domain_name, domain_name,
887 			sizeof(request.domain_name)-1);
888 	}
889 
890 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_USERS,
891 					&request,
892 					&response);
893 	BAIL_ON_WBC_ERROR(wbc_status);
894 
895 	users = wbcAllocateStringArray(response.data.num_entries);
896 	if (users == NULL) {
897 		return WBC_ERR_NO_MEMORY;
898 	}
899 
900 	/* Look through extra data */
901 
902 	next = (const char *)response.extra_data.data;
903 	while (next) {
904 		const char *current;
905 		char *k;
906 
907 		if (num_users >= response.data.num_entries) {
908 			wbc_status = WBC_ERR_INVALID_RESPONSE;
909 			goto done;
910 		}
911 
912 		current = next;
913 		k = strchr(next, ',');
914 
915 		if (k) {
916 			k[0] = '\0';
917 			next = k+1;
918 		} else {
919 			next = NULL;
920 		}
921 
922 		users[num_users] = strdup(current);
923 		BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
924 		num_users += 1;
925 	}
926 	if (num_users != response.data.num_entries) {
927 		wbc_status = WBC_ERR_INVALID_RESPONSE;
928 		goto done;
929 	}
930 
931 	*_num_users = response.data.num_entries;
932 	*_users = users;
933 	users = NULL;
934 	wbc_status = WBC_ERR_SUCCESS;
935 
936  done:
937 	winbindd_free_response(&response);
938 	wbcFreeMemory(users);
939 	return wbc_status;
940 }
941 
wbcListUsers(const char * domain_name,uint32_t * _num_users,const char *** _users)942 wbcErr wbcListUsers(const char *domain_name,
943 		    uint32_t *_num_users,
944 		    const char ***_users)
945 {
946 	return wbcCtxListUsers(NULL, domain_name, _num_users, _users);
947 }
948 
949 /* Lists Groups */
wbcCtxListGroups(struct wbcContext * ctx,const char * domain_name,uint32_t * _num_groups,const char *** _groups)950 wbcErr wbcCtxListGroups(struct wbcContext *ctx,
951 			const char *domain_name,
952 			uint32_t *_num_groups,
953 			const char ***_groups)
954 {
955 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
956 	struct winbindd_request request;
957 	struct winbindd_response response;
958 	uint32_t num_groups = 0;
959 	const char **groups = NULL;
960 	const char *next;
961 
962 	/* Initialise request */
963 
964 	ZERO_STRUCT(request);
965 	ZERO_STRUCT(response);
966 
967 	if (domain_name) {
968 		strncpy(request.domain_name, domain_name,
969 			sizeof(request.domain_name)-1);
970 	}
971 
972 	wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_GROUPS,
973 					&request,
974 					&response);
975 	BAIL_ON_WBC_ERROR(wbc_status);
976 
977 	groups = wbcAllocateStringArray(response.data.num_entries);
978 	if (groups == NULL) {
979 		return WBC_ERR_NO_MEMORY;
980 	}
981 
982 	/* Look through extra data */
983 
984 	next = (const char *)response.extra_data.data;
985 	while (next) {
986 		const char *current;
987 		char *k;
988 
989 		if (num_groups >= response.data.num_entries) {
990 			wbc_status = WBC_ERR_INVALID_RESPONSE;
991 			goto done;
992 		}
993 
994 		current = next;
995 		k = strchr(next, ',');
996 
997 		if (k) {
998 			k[0] = '\0';
999 			next = k+1;
1000 		} else {
1001 			next = NULL;
1002 		}
1003 
1004 		groups[num_groups] = strdup(current);
1005 		BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
1006 		num_groups += 1;
1007 	}
1008 	if (num_groups != response.data.num_entries) {
1009 		wbc_status = WBC_ERR_INVALID_RESPONSE;
1010 		goto done;
1011 	}
1012 
1013 	*_num_groups = response.data.num_entries;
1014 	*_groups = groups;
1015 	groups = NULL;
1016 	wbc_status = WBC_ERR_SUCCESS;
1017 
1018  done:
1019 	winbindd_free_response(&response);
1020 	wbcFreeMemory(groups);
1021 	return wbc_status;
1022 }
1023 
wbcListGroups(const char * domain_name,uint32_t * _num_groups,const char *** _groups)1024 wbcErr wbcListGroups(const char *domain_name,
1025 		     uint32_t *_num_groups,
1026 		     const char ***_groups)
1027 {
1028 	return wbcCtxListGroups(NULL, domain_name, _num_groups, _groups);
1029 }
1030 
wbcCtxGetDisplayName(struct wbcContext * ctx,const struct wbcDomainSid * sid,char ** pdomain,char ** pfullname,enum wbcSidType * pname_type)1031 wbcErr wbcCtxGetDisplayName(struct wbcContext *ctx,
1032 			    const struct wbcDomainSid *sid,
1033 			    char **pdomain,
1034 			    char **pfullname,
1035 			    enum wbcSidType *pname_type)
1036 {
1037 	wbcErr wbc_status;
1038 	char *domain = NULL;
1039 	char *name = NULL;
1040 	enum wbcSidType name_type;
1041 
1042 	wbc_status = wbcCtxLookupSid(ctx, sid, &domain, &name, &name_type);
1043 	BAIL_ON_WBC_ERROR(wbc_status);
1044 
1045 	if (name_type == WBC_SID_NAME_USER) {
1046 		uid_t uid;
1047 		struct passwd *pwd;
1048 
1049 		wbc_status = wbcCtxSidToUid(ctx, sid, &uid);
1050 		BAIL_ON_WBC_ERROR(wbc_status);
1051 
1052 		wbc_status = wbcCtxGetpwuid(ctx, uid, &pwd);
1053 		BAIL_ON_WBC_ERROR(wbc_status);
1054 
1055 		wbcFreeMemory(name);
1056 
1057 		name = wbcStrDup(pwd->pw_gecos);
1058 		wbcFreeMemory(pwd);
1059 		BAIL_ON_PTR_ERROR(name, wbc_status);
1060 	}
1061 
1062 	wbc_status = WBC_ERR_SUCCESS;
1063 
1064  done:
1065 	if (WBC_ERROR_IS_OK(wbc_status)) {
1066 		*pdomain = domain;
1067 		*pfullname = name;
1068 		*pname_type = name_type;
1069 	} else {
1070 		wbcFreeMemory(domain);
1071 		wbcFreeMemory(name);
1072 	}
1073 
1074 	return wbc_status;
1075 }
1076 
wbcGetDisplayName(const struct wbcDomainSid * sid,char ** pdomain,char ** pfullname,enum wbcSidType * pname_type)1077 wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
1078 			 char **pdomain,
1079 			 char **pfullname,
1080 			 enum wbcSidType *pname_type)
1081 {
1082 	return wbcCtxGetDisplayName(NULL, sid, pdomain, pfullname, pname_type);
1083 }
1084 
wbcSidTypeString(enum wbcSidType type)1085 const char* wbcSidTypeString(enum wbcSidType type)
1086 {
1087 	switch (type) {
1088 	case WBC_SID_NAME_USE_NONE: return "SID_NONE";
1089 	case WBC_SID_NAME_USER:     return "SID_USER";
1090 	case WBC_SID_NAME_DOM_GRP:  return "SID_DOM_GROUP";
1091 	case WBC_SID_NAME_DOMAIN:   return "SID_DOMAIN";
1092 	case WBC_SID_NAME_ALIAS:    return "SID_ALIAS";
1093 	case WBC_SID_NAME_WKN_GRP:  return "SID_WKN_GROUP";
1094 	case WBC_SID_NAME_DELETED:  return "SID_DELETED";
1095 	case WBC_SID_NAME_INVALID:  return "SID_INVALID";
1096 	case WBC_SID_NAME_UNKNOWN:  return "SID_UNKNOWN";
1097 	case WBC_SID_NAME_COMPUTER: return "SID_COMPUTER";
1098 	case WBC_SID_NAME_LABEL:    return "SID_LABEL";
1099 	default:                    return "Unknown type";
1100 	}
1101 }
1102