514a7e8d7380186944310b478c2d5ca190a1a71f
[sfrench/samba-autobuild/.git] / nsswitch / libwbclient / wbc_sid.c
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. */
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 */
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 */
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)strtoul_err(p, &q, 10, &error);
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 = strtoull_err(p, &q, 0, &error);
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 = strtoull_err(p, &q, 10, &error);
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 */
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
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 */
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
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
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
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
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 = strtoul_err(p, &q, 10, &error);
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 = strtoul_err(p, &q, 10, &error);
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 = strtoul_err(p, &q, 10, &error);
453                 if (names[i].domain_index < 0 || error != 0) {
454                         goto wbc_err_invalid;
455                 }
456                 if (names[i].domain_index >= num_domains) {
457                         goto wbc_err_invalid;
458                 }
459
460                 if (*q != ' ') {
461                         goto wbc_err_invalid;
462                 }
463                 p = q+1;
464
465                 names[i].type = strtoul_err(p, &q, 10, &error);
466                 if (*q != ' ' || error != 0) {
467                         goto wbc_err_invalid;
468                 }
469                 p = q+1;
470
471                 q = strchr(p, '\n');
472                 if (q == NULL) {
473                         goto wbc_err_invalid;
474                 }
475                 *q = '\0';
476                 names[i].name = wbcStrDup(p);
477                 if (names[i].name == NULL) {
478                         wbc_status = WBC_ERR_NO_MEMORY;
479                         goto fail;
480                 }
481                 p = q+1;
482         }
483         if (*p != '\0') {
484                 goto wbc_err_invalid;
485         }
486
487         *pdomains = domains;
488         *pnames = names;
489         winbindd_free_response(&response);
490         return WBC_ERR_SUCCESS;
491
492 wbc_err_invalid:
493         wbc_status = WBC_ERR_INVALID_RESPONSE;
494 fail:
495         winbindd_free_response(&response);
496         wbcFreeMemory(domains);
497         wbcFreeMemory(names);
498         return wbc_status;
499 }
500
501 wbcErr wbcLookupSids(const struct wbcDomainSid *sids, int num_sids,
502                      struct wbcDomainInfo **pdomains, int *pnum_domains,
503                      struct wbcTranslatedName **pnames)
504 {
505         return wbcCtxLookupSids(NULL, sids, num_sids, pdomains,
506                                 pnum_domains, pnames);
507 }
508
509 /* Translate a collection of RIDs within a domain to names */
510
511 wbcErr wbcCtxLookupRids(struct wbcContext *ctx, struct wbcDomainSid *dom_sid,
512                      int num_rids,
513                      uint32_t *rids,
514                      const char **pp_domain_name,
515                      const char ***pnames,
516                      enum wbcSidType **ptypes)
517 {
518         size_t i, len, ridbuf_size;
519         char *ridlist;
520         char *p;
521         int error = 0;
522         struct winbindd_request request;
523         struct winbindd_response response;
524         char *domain_name = NULL;
525         const char **names = NULL;
526         enum wbcSidType *types = NULL;
527         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
528
529         /* Initialise request */
530
531         ZERO_STRUCT(request);
532         ZERO_STRUCT(response);
533
534         if (!dom_sid || (num_rids == 0)) {
535                 wbc_status = WBC_ERR_INVALID_PARAM;
536                 BAIL_ON_WBC_ERROR(wbc_status);
537         }
538
539         wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
540
541         /* Even if all the Rids were of maximum 32bit values,
542            we would only have 11 bytes per rid in the final array
543            ("4294967296" + \n).  Add one more byte for the
544            terminating '\0' */
545
546         ridbuf_size = (sizeof(char)*11) * num_rids + 1;
547
548         ridlist = (char *)malloc(ridbuf_size);
549         BAIL_ON_PTR_ERROR(ridlist, wbc_status);
550
551         len = 0;
552         for (i=0; i<num_rids; i++) {
553                 len += snprintf(ridlist + len, ridbuf_size - len, "%u\n",
554                                 rids[i]);
555         }
556         ridlist[len] = '\0';
557         len += 1;
558
559         request.extra_data.data = ridlist;
560         request.extra_len = len;
561
562         wbc_status = wbcRequestResponse(ctx, WINBINDD_LOOKUPRIDS,
563                                         &request,
564                                         &response);
565         free(ridlist);
566         BAIL_ON_WBC_ERROR(wbc_status);
567
568         domain_name = wbcStrDup(response.data.domain_name);
569         BAIL_ON_PTR_ERROR(domain_name, wbc_status);
570
571         names = wbcAllocateStringArray(num_rids);
572         BAIL_ON_PTR_ERROR(names, wbc_status);
573
574         types = (enum wbcSidType *)wbcAllocateMemory(
575                 num_rids, sizeof(enum wbcSidType), NULL);
576         BAIL_ON_PTR_ERROR(types, wbc_status);
577
578         p = (char *)response.extra_data.data;
579
580         for (i=0; i<num_rids; i++) {
581                 char *q;
582
583                 if (*p == '\0') {
584                         wbc_status = WBC_ERR_INVALID_RESPONSE;
585                         goto done;
586                 }
587
588                 types[i] = (enum wbcSidType)strtoul_err(p, &q, 10, &error);
589
590                 if (*q != ' ' || error != 0) {
591                         wbc_status = WBC_ERR_INVALID_RESPONSE;
592                         goto done;
593                 }
594
595                 p = q+1;
596
597                 if ((q = strchr(p, '\n')) == NULL) {
598                         wbc_status = WBC_ERR_INVALID_RESPONSE;
599                         goto done;
600                 }
601
602                 *q = '\0';
603
604                 names[i] = strdup(p);
605                 BAIL_ON_PTR_ERROR(names[i], wbc_status);
606
607                 p = q+1;
608         }
609
610         if (*p != '\0') {
611                 wbc_status = WBC_ERR_INVALID_RESPONSE;
612                 goto done;
613         }
614
615         wbc_status = WBC_ERR_SUCCESS;
616
617  done:
618         winbindd_free_response(&response);
619
620         if (WBC_ERROR_IS_OK(wbc_status)) {
621                 *pp_domain_name = domain_name;
622                 *pnames = names;
623                 *ptypes = types;
624         }
625         else {
626                 wbcFreeMemory(domain_name);
627                 wbcFreeMemory(names);
628                 wbcFreeMemory(types);
629         }
630
631         return wbc_status;
632 }
633
634 wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
635                      int num_rids,
636                      uint32_t *rids,
637                      const char **pp_domain_name,
638                      const char ***pnames,
639                      enum wbcSidType **ptypes)
640 {
641         return wbcCtxLookupRids(NULL, dom_sid, num_rids, rids,
642                                 pp_domain_name, pnames, ptypes);
643 }
644
645 /* Get the groups a user belongs to */
646 wbcErr wbcCtxLookupUserSids(struct wbcContext *ctx,
647                             const struct wbcDomainSid *user_sid,
648                             bool domain_groups_only,
649                             uint32_t *num_sids,
650                             struct wbcDomainSid **_sids)
651 {
652         uint32_t i;
653         const char *s;
654         struct winbindd_request request;
655         struct winbindd_response response;
656         struct wbcDomainSid *sids = NULL;
657         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
658         int cmd;
659
660         /* Initialise request */
661
662         ZERO_STRUCT(request);
663         ZERO_STRUCT(response);
664
665         if (!user_sid) {
666                 wbc_status = WBC_ERR_INVALID_PARAM;
667                 BAIL_ON_WBC_ERROR(wbc_status);
668         }
669
670         wbcSidToStringBuf(user_sid, request.data.sid, sizeof(request.data.sid));
671
672         if (domain_groups_only) {
673                 cmd = WINBINDD_GETUSERDOMGROUPS;
674         } else {
675                 cmd = WINBINDD_GETUSERSIDS;
676         }
677
678         wbc_status = wbcRequestResponse(ctx, cmd,
679                                         &request,
680                                         &response);
681         BAIL_ON_WBC_ERROR(wbc_status);
682
683         if (response.data.num_entries &&
684             !response.extra_data.data) {
685                 wbc_status = WBC_ERR_INVALID_RESPONSE;
686                 BAIL_ON_WBC_ERROR(wbc_status);
687         }
688
689         sids = (struct wbcDomainSid *)wbcAllocateMemory(
690                 response.data.num_entries, sizeof(struct wbcDomainSid),
691                 NULL);
692         BAIL_ON_PTR_ERROR(sids, wbc_status);
693
694         s = (const char *)response.extra_data.data;
695         for (i = 0; i < response.data.num_entries; i++) {
696                 char *n = strchr(s, '\n');
697                 if (n) {
698                         *n = '\0';
699                 }
700                 wbc_status = wbcStringToSid(s, &sids[i]);
701                 BAIL_ON_WBC_ERROR(wbc_status);
702                 s += strlen(s) + 1;
703         }
704
705         *num_sids = response.data.num_entries;
706         *_sids = sids;
707         sids = NULL;
708         wbc_status = WBC_ERR_SUCCESS;
709
710  done:
711         winbindd_free_response(&response);
712         if (sids) {
713                 wbcFreeMemory(sids);
714         }
715
716         return wbc_status;
717 }
718
719 wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
720                          bool domain_groups_only,
721                          uint32_t *num_sids,
722                          struct wbcDomainSid **_sids)
723 {
724         return wbcCtxLookupUserSids(NULL, user_sid, domain_groups_only,
725                                     num_sids, _sids);
726 }
727
728 static inline
729 wbcErr _sid_to_rid(struct wbcDomainSid *sid, uint32_t *rid)
730 {
731         if (sid->num_auths < 1) {
732                 return WBC_ERR_INVALID_RESPONSE;
733         }
734         *rid = sid->sub_auths[sid->num_auths - 1];
735
736         return WBC_ERR_SUCCESS;
737 }
738
739 /* Get alias membership for sids */
740 wbcErr wbcCtxGetSidAliases(struct wbcContext *ctx,
741                            const struct wbcDomainSid *dom_sid,
742                            struct wbcDomainSid *sids,
743                            uint32_t num_sids,
744                            uint32_t **alias_rids,
745                            uint32_t *num_alias_rids)
746 {
747         uint32_t i;
748         const char *s;
749         struct winbindd_request request;
750         struct winbindd_response response;
751         ssize_t extra_data_len = 0;
752         char * extra_data = NULL;
753         ssize_t buflen = 0;
754         struct wbcDomainSid sid;
755         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
756         uint32_t * rids = NULL;
757
758         /* Initialise request */
759
760         ZERO_STRUCT(request);
761         ZERO_STRUCT(response);
762
763         if (!dom_sid) {
764                 wbc_status = WBC_ERR_INVALID_PARAM;
765                 goto done;
766         }
767
768         wbcSidToStringBuf(dom_sid, request.data.sid, sizeof(request.data.sid));
769
770         /* Lets assume each sid is around 57 characters
771          * S-1-5-21-AAAAAAAAAAA-BBBBBBBBBBB-CCCCCCCCCCC-DDDDDDDDDDD\n */
772         buflen = 57 * num_sids;
773         extra_data = (char *)malloc(buflen);
774         if (!extra_data) {
775                 wbc_status = WBC_ERR_NO_MEMORY;
776                 goto done;
777         }
778
779         /* Build the sid list */
780         for (i=0; i<num_sids; i++) {
781                 char sid_str[WBC_SID_STRING_BUFLEN];
782                 size_t sid_len;
783
784                 sid_len = wbcSidToStringBuf(&sids[i], sid_str, sizeof(sid_str));
785
786                 if (buflen < extra_data_len + sid_len + 2) {
787                         buflen *= 2;
788                         extra_data = (char *)realloc(extra_data, buflen);
789                         if (!extra_data) {
790                                 wbc_status = WBC_ERR_NO_MEMORY;
791                                 BAIL_ON_WBC_ERROR(wbc_status);
792                         }
793                 }
794
795                 strncpy(&extra_data[extra_data_len], sid_str,
796                         buflen - extra_data_len);
797                 extra_data_len += sid_len;
798                 extra_data[extra_data_len++] = '\n';
799                 extra_data[extra_data_len] = '\0';
800         }
801         extra_data_len += 1;
802
803         request.extra_data.data = extra_data;
804         request.extra_len = extra_data_len;
805
806         wbc_status = wbcRequestResponse(ctx, WINBINDD_GETSIDALIASES,
807                                         &request,
808                                         &response);
809         BAIL_ON_WBC_ERROR(wbc_status);
810
811         if (response.data.num_entries &&
812             !response.extra_data.data) {
813                 wbc_status = WBC_ERR_INVALID_RESPONSE;
814                 goto done;
815         }
816
817         rids = (uint32_t *)wbcAllocateMemory(response.data.num_entries,
818                                              sizeof(uint32_t), NULL);
819         BAIL_ON_PTR_ERROR(rids, wbc_status);
820
821         s = (const char *)response.extra_data.data;
822         for (i = 0; i < response.data.num_entries; i++) {
823                 char *n = strchr(s, '\n');
824                 if (n) {
825                         *n = '\0';
826                 }
827                 wbc_status = wbcStringToSid(s, &sid);
828                 BAIL_ON_WBC_ERROR(wbc_status);
829                 wbc_status = _sid_to_rid(&sid, &rids[i]);
830                 BAIL_ON_WBC_ERROR(wbc_status);
831                 s += strlen(s) + 1;
832         }
833
834         *num_alias_rids = response.data.num_entries;
835         *alias_rids = rids;
836         rids = NULL;
837         wbc_status = WBC_ERR_SUCCESS;
838
839  done:
840         free(extra_data);
841         winbindd_free_response(&response);
842         wbcFreeMemory(rids);
843         return wbc_status;
844 }
845
846 wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
847                         struct wbcDomainSid *sids,
848                         uint32_t num_sids,
849                         uint32_t **alias_rids,
850                         uint32_t *num_alias_rids)
851 {
852         return wbcCtxGetSidAliases(NULL, dom_sid, sids, num_sids,
853                                    alias_rids, num_alias_rids);
854 }
855
856
857 /* Lists Users */
858 wbcErr wbcCtxListUsers(struct wbcContext *ctx,
859                        const char *domain_name,
860                        uint32_t *_num_users,
861                        const char ***_users)
862 {
863         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
864         struct winbindd_request request;
865         struct winbindd_response response;
866         uint32_t num_users = 0;
867         const char **users = NULL;
868         const char *next;
869
870         /* Initialise request */
871
872         ZERO_STRUCT(request);
873         ZERO_STRUCT(response);
874
875         if (domain_name) {
876                 strncpy(request.domain_name, domain_name,
877                         sizeof(request.domain_name)-1);
878         }
879
880         wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_USERS,
881                                         &request,
882                                         &response);
883         BAIL_ON_WBC_ERROR(wbc_status);
884
885         users = wbcAllocateStringArray(response.data.num_entries);
886         if (users == NULL) {
887                 return WBC_ERR_NO_MEMORY;
888         }
889
890         /* Look through extra data */
891
892         next = (const char *)response.extra_data.data;
893         while (next) {
894                 const char *current;
895                 char *k;
896
897                 if (num_users >= response.data.num_entries) {
898                         wbc_status = WBC_ERR_INVALID_RESPONSE;
899                         goto done;
900                 }
901
902                 current = next;
903                 k = strchr(next, ',');
904
905                 if (k) {
906                         k[0] = '\0';
907                         next = k+1;
908                 } else {
909                         next = NULL;
910                 }
911
912                 users[num_users] = strdup(current);
913                 BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
914                 num_users += 1;
915         }
916         if (num_users != response.data.num_entries) {
917                 wbc_status = WBC_ERR_INVALID_RESPONSE;
918                 goto done;
919         }
920
921         *_num_users = response.data.num_entries;
922         *_users = users;
923         users = NULL;
924         wbc_status = WBC_ERR_SUCCESS;
925
926  done:
927         winbindd_free_response(&response);
928         wbcFreeMemory(users);
929         return wbc_status;
930 }
931
932 wbcErr wbcListUsers(const char *domain_name,
933                     uint32_t *_num_users,
934                     const char ***_users)
935 {
936         return wbcCtxListUsers(NULL, domain_name, _num_users, _users);
937 }
938
939 /* Lists Groups */
940 wbcErr wbcCtxListGroups(struct wbcContext *ctx,
941                         const char *domain_name,
942                         uint32_t *_num_groups,
943                         const char ***_groups)
944 {
945         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
946         struct winbindd_request request;
947         struct winbindd_response response;
948         uint32_t num_groups = 0;
949         const char **groups = NULL;
950         const char *next;
951
952         /* Initialise request */
953
954         ZERO_STRUCT(request);
955         ZERO_STRUCT(response);
956
957         if (domain_name) {
958                 strncpy(request.domain_name, domain_name,
959                         sizeof(request.domain_name)-1);
960         }
961
962         wbc_status = wbcRequestResponse(ctx, WINBINDD_LIST_GROUPS,
963                                         &request,
964                                         &response);
965         BAIL_ON_WBC_ERROR(wbc_status);
966
967         groups = wbcAllocateStringArray(response.data.num_entries);
968         if (groups == NULL) {
969                 return WBC_ERR_NO_MEMORY;
970         }
971
972         /* Look through extra data */
973
974         next = (const char *)response.extra_data.data;
975         while (next) {
976                 const char *current;
977                 char *k;
978
979                 if (num_groups >= response.data.num_entries) {
980                         wbc_status = WBC_ERR_INVALID_RESPONSE;
981                         goto done;
982                 }
983
984                 current = next;
985                 k = strchr(next, ',');
986
987                 if (k) {
988                         k[0] = '\0';
989                         next = k+1;
990                 } else {
991                         next = NULL;
992                 }
993
994                 groups[num_groups] = strdup(current);
995                 BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
996                 num_groups += 1;
997         }
998         if (num_groups != response.data.num_entries) {
999                 wbc_status = WBC_ERR_INVALID_RESPONSE;
1000                 goto done;
1001         }
1002
1003         *_num_groups = response.data.num_entries;
1004         *_groups = groups;
1005         groups = NULL;
1006         wbc_status = WBC_ERR_SUCCESS;
1007
1008  done:
1009         winbindd_free_response(&response);
1010         wbcFreeMemory(groups);
1011         return wbc_status;
1012 }
1013
1014 wbcErr wbcListGroups(const char *domain_name,
1015                      uint32_t *_num_groups,
1016                      const char ***_groups)
1017 {
1018         return wbcCtxListGroups(NULL, domain_name, _num_groups, _groups);
1019 }
1020
1021 wbcErr wbcCtxGetDisplayName(struct wbcContext *ctx,
1022                             const struct wbcDomainSid *sid,
1023                             char **pdomain,
1024                             char **pfullname,
1025                             enum wbcSidType *pname_type)
1026 {
1027         wbcErr wbc_status;
1028         char *domain = NULL;
1029         char *name = NULL;
1030         enum wbcSidType name_type;
1031
1032         wbc_status = wbcCtxLookupSid(ctx, sid, &domain, &name, &name_type);
1033         BAIL_ON_WBC_ERROR(wbc_status);
1034
1035         if (name_type == WBC_SID_NAME_USER) {
1036                 uid_t uid;
1037                 struct passwd *pwd;
1038
1039                 wbc_status = wbcCtxSidToUid(ctx, sid, &uid);
1040                 BAIL_ON_WBC_ERROR(wbc_status);
1041
1042                 wbc_status = wbcCtxGetpwuid(ctx, uid, &pwd);
1043                 BAIL_ON_WBC_ERROR(wbc_status);
1044
1045                 wbcFreeMemory(name);
1046
1047                 name = wbcStrDup(pwd->pw_gecos);
1048                 wbcFreeMemory(pwd);
1049                 BAIL_ON_PTR_ERROR(name, wbc_status);
1050         }
1051
1052         wbc_status = WBC_ERR_SUCCESS;
1053
1054  done:
1055         if (WBC_ERROR_IS_OK(wbc_status)) {
1056                 *pdomain = domain;
1057                 *pfullname = name;
1058                 *pname_type = name_type;
1059         } else {
1060                 wbcFreeMemory(domain);
1061                 wbcFreeMemory(name);
1062         }
1063
1064         return wbc_status;
1065 }
1066
1067 wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
1068                          char **pdomain,
1069                          char **pfullname,
1070                          enum wbcSidType *pname_type)
1071 {
1072         return wbcCtxGetDisplayName(NULL, sid, pdomain, pfullname, pname_type);
1073 }
1074
1075 const char* wbcSidTypeString(enum wbcSidType type)
1076 {
1077         switch (type) {
1078         case WBC_SID_NAME_USE_NONE: return "SID_NONE";
1079         case WBC_SID_NAME_USER:     return "SID_USER";
1080         case WBC_SID_NAME_DOM_GRP:  return "SID_DOM_GROUP";
1081         case WBC_SID_NAME_DOMAIN:   return "SID_DOMAIN";
1082         case WBC_SID_NAME_ALIAS:    return "SID_ALIAS";
1083         case WBC_SID_NAME_WKN_GRP:  return "SID_WKN_GROUP";
1084         case WBC_SID_NAME_DELETED:  return "SID_DELETED";
1085         case WBC_SID_NAME_INVALID:  return "SID_INVALID";
1086         case WBC_SID_NAME_UNKNOWN:  return "SID_UNKNOWN";
1087         case WBC_SID_NAME_COMPUTER: return "SID_COMPUTER";
1088         case WBC_SID_NAME_LABEL:    return "SID_LABEL";
1089         default:                    return "Unknown type";
1090         }
1091 }