Make warning a bit more user-friendly.
[ira/wip.git] / source3 / 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
8
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 3 of the License, or (at your option) any later version.
13
14    This library 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 GNU
17    Library General Public License for more details.
18
19    You should have received a copy of the GNU Lesser General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* Required Headers */
24
25 #include "libwbclient.h"
26
27
28 /** @brief Convert a binary SID to a character string
29  *
30  * @param sid           Binary Security Identifier
31  * @param **sid_string  Resulting character string
32  *
33  * @return #wbcErr
34  **/
35
36 wbcErr wbcSidToString(const struct wbcDomainSid *sid,
37                       char **sid_string)
38 {
39         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
40         uint32_t id_auth;
41         int i;
42         char *tmp = NULL;
43         TALLOC_CTX *ctx = NULL;
44
45         if (!sid) {
46                 wbc_status = WBC_ERR_INVALID_SID;
47                 BAIL_ON_WBC_ERROR(wbc_status);
48         }
49
50         ctx = talloc_init("wbcSidToString");
51         BAIL_ON_PTR_ERROR(ctx, wbc_status);
52
53         id_auth = sid->id_auth[5] +
54                 (sid->id_auth[4] << 8) +
55                 (sid->id_auth[3] << 16) +
56                 (sid->id_auth[2] << 24);
57
58         tmp = talloc_asprintf(ctx, "S-%d-%d", sid->sid_rev_num, id_auth);
59         BAIL_ON_PTR_ERROR(tmp, wbc_status);
60
61         for (i=0; i<sid->num_auths; i++) {
62                 char *tmp2;
63                 tmp2 = talloc_asprintf_append(tmp, "-%u", sid->sub_auths[i]);
64                 BAIL_ON_PTR_ERROR(tmp2, wbc_status);
65
66                 tmp = tmp2;
67         }
68
69         *sid_string=talloc_strdup(NULL, tmp);
70         BAIL_ON_PTR_ERROR((*sid_string), wbc_status);
71
72         wbc_status = WBC_ERR_SUCCESS;
73
74 done:
75         talloc_free(ctx);
76
77         return wbc_status;
78 }
79
80 /** @brief Convert a character string to a binary SID
81  *
82  * @param *str          Character string in the form of S-...
83  * @param sid           Resulting binary SID
84  *
85  * @return #wbcErr
86  **/
87
88 wbcErr wbcStringToSid(const char *str,
89                       struct wbcDomainSid *sid)
90 {
91         const char *p;
92         char *q;
93         uint32_t x;
94         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
95
96         if (!sid) {
97                 wbc_status = WBC_ERR_INVALID_PARAM;
98                 BAIL_ON_WBC_ERROR(wbc_status);
99         }
100
101         /* Sanity check for either "S-" or "s-" */
102
103         if (!str
104             || (str[0]!='S' && str[0]!='s')
105             || (str[1]!='-'))
106         {
107                 wbc_status = WBC_ERR_INVALID_PARAM;
108                 BAIL_ON_WBC_ERROR(wbc_status);
109         }
110
111         /* Get the SID revision number */
112
113         p = str+2;
114         x = (uint32_t)strtol(p, &q, 10);
115         if (x==0 || !q || *q!='-') {
116                 wbc_status = WBC_ERR_INVALID_SID;
117                 BAIL_ON_WBC_ERROR(wbc_status);
118         }
119         sid->sid_rev_num = (uint8_t)x;
120
121         /* Next the Identifier Authority.  This is stored in big-endian
122            in a 6 byte array. */
123
124         p = q+1;
125         x = (uint32_t)strtol(p, &q, 10);
126         if (!q || *q!='-') {
127                 wbc_status = WBC_ERR_INVALID_SID;
128                 BAIL_ON_WBC_ERROR(wbc_status);
129         }
130         sid->id_auth[5] = (x & 0x000000ff);
131         sid->id_auth[4] = (x & 0x0000ff00) >> 8;
132         sid->id_auth[3] = (x & 0x00ff0000) >> 16;
133         sid->id_auth[2] = (x & 0xff000000) >> 24;
134         sid->id_auth[1] = 0;
135         sid->id_auth[0] = 0;
136
137         /* now read the the subauthorities */
138
139         p = q +1;
140         sid->num_auths = 0;
141         while (sid->num_auths < WBC_MAXSUBAUTHS) {
142                 x=(uint32_t)strtoul(p, &q, 10);
143                 if (p == q)
144                         break;
145                 if (q == NULL) {
146                         wbc_status = WBC_ERR_INVALID_SID;
147                         BAIL_ON_WBC_ERROR(wbc_status);
148                 }
149                 sid->sub_auths[sid->num_auths++] = x;
150
151                 if ((*q!='-') || (*q=='\0'))
152                         break;
153                 p = q + 1;
154         }
155
156         /* IF we ended early, then the SID could not be converted */
157
158         if (q && *q!='\0') {
159                 wbc_status = WBC_ERR_INVALID_SID;
160                 BAIL_ON_WBC_ERROR(wbc_status);
161         }
162
163         wbc_status = WBC_ERR_SUCCESS;
164
165 done:
166         return wbc_status;
167
168 }
169
170 /** @brief Convert a domain and name to SID
171  *
172  * @param domain      Domain name (possibly "")
173  * @param name        User or group name
174  * @param *sid        Pointer to the resolved domain SID
175  * @param *name_type  Pointet to the SID type
176  *
177  * @return #wbcErr
178  *
179  **/
180
181 wbcErr wbcLookupName(const char *domain,
182                      const char *name,
183                      struct wbcDomainSid *sid,
184                      enum wbcSidType *name_type)
185 {
186         struct winbindd_request request;
187         struct winbindd_response response;
188         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
189
190         if (!sid || !name_type) {
191                 wbc_status = WBC_ERR_INVALID_PARAM;
192                 BAIL_ON_WBC_ERROR(wbc_status);
193         }
194
195         /* Initialize request */
196
197         ZERO_STRUCT(request);
198         ZERO_STRUCT(response);
199
200         /* dst is already null terminated from the memset above */
201
202         strncpy(request.data.name.dom_name, domain,
203                 sizeof(request.data.name.dom_name)-1);
204         strncpy(request.data.name.name, name,
205                 sizeof(request.data.name.name)-1);
206
207         wbc_status = wbcRequestResponse(WINBINDD_LOOKUPNAME,
208                                         &request,
209                                         &response);
210         BAIL_ON_WBC_ERROR(wbc_status);
211
212         wbc_status = wbcStringToSid(response.data.sid.sid, sid);
213         BAIL_ON_WBC_ERROR(wbc_status);
214
215         *name_type = (enum wbcSidType)response.data.sid.type;
216
217         wbc_status = WBC_ERR_SUCCESS;
218
219  done:
220         return wbc_status;
221 }
222
223 /** @brief Convert a SID to a domain and name
224  *
225  * @param *sid        Pointer to the domain SID to be resolved
226  * @param pdomain     Resolved Domain name (possibly "")
227  * @param pname       Resolved User or group name
228  * @param *pname_type Pointet to the resolved SID type
229  *
230  * @return #wbcErr
231  *
232  **/
233
234 wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
235                     char **pdomain,
236                     char **pname,
237                     enum wbcSidType *pname_type)
238 {
239         struct winbindd_request request;
240         struct winbindd_response response;
241         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
242         char *sid_string = NULL;
243         char *domain = NULL;
244         char *name = NULL;
245         enum wbcSidType name_type = WBC_SID_NAME_USE_NONE;
246
247         if (!sid) {
248                 wbc_status = WBC_ERR_INVALID_PARAM;
249                 BAIL_ON_WBC_ERROR(wbc_status);
250         }
251
252         /* Initialize request */
253
254         ZERO_STRUCT(request);
255         ZERO_STRUCT(response);
256
257         /* dst is already null terminated from the memset above */
258
259         wbc_status = wbcSidToString(sid, &sid_string);
260         BAIL_ON_WBC_ERROR(wbc_status);
261
262         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
263         wbcFreeMemory(sid_string);
264
265         /* Make request */
266
267         wbc_status = wbcRequestResponse(WINBINDD_LOOKUPSID,
268                                            &request,
269                                            &response);
270         BAIL_ON_WBC_ERROR(wbc_status);
271
272         /* Copy out result */
273
274         domain = talloc_strdup(NULL, response.data.name.dom_name);
275         BAIL_ON_PTR_ERROR(domain, wbc_status);
276
277         name = talloc_strdup(NULL, response.data.name.name);
278         BAIL_ON_PTR_ERROR(name, wbc_status);
279
280         name_type = (enum wbcSidType)response.data.name.type;
281
282         wbc_status = WBC_ERR_SUCCESS;
283
284  done:
285         if (WBC_ERROR_IS_OK(wbc_status)) {
286                 if (pdomain != NULL) {
287                         *pdomain = domain;
288                 }
289                 if (pname != NULL) {
290                         *pname = name;
291                 }
292                 if (pname_type != NULL) {
293                         *pname_type = name_type;
294                 }
295         }
296         else {
297 #if 0
298                 /*
299                  * Found by Coverity: In this particular routine we can't end
300                  * up here with a non-NULL name. Further up there are just two
301                  * exit paths that lead here, neither of which leave an
302                  * allocated name. If you add more paths up there, re-activate
303                  * this.
304                  */
305                 if (name != NULL) {
306                         talloc_free(name);
307                 }
308 #endif
309                 if (domain != NULL) {
310                         talloc_free(domain);
311                 }
312         }
313
314         return wbc_status;
315 }
316
317 /** @brief Translate a collection of RIDs within a domain to names
318  *
319  **/
320
321 wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
322                      int num_rids,
323                      uint32_t *rids,
324                      const char **pp_domain_name,
325                      const char ***pnames,
326                      enum wbcSidType **ptypes)
327 {
328         size_t i, len, ridbuf_size;
329         char *ridlist;
330         char *p;
331         struct winbindd_request request;
332         struct winbindd_response response;
333         char *sid_string = NULL;
334         char *domain_name = NULL;
335         const char **names = NULL;
336         enum wbcSidType *types = NULL;
337         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
338
339         /* Initialise request */
340
341         ZERO_STRUCT(request);
342         ZERO_STRUCT(response);
343
344         if (!dom_sid || (num_rids == 0)) {
345                 wbc_status = WBC_ERR_INVALID_PARAM;
346                 BAIL_ON_WBC_ERROR(wbc_status);
347         }
348
349         wbc_status = wbcSidToString(dom_sid, &sid_string);
350         BAIL_ON_WBC_ERROR(wbc_status);
351
352         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
353         wbcFreeMemory(sid_string);
354
355         /* Even if all the Rids were of maximum 32bit values,
356            we would only have 11 bytes per rid in the final array
357            ("4294967296" + \n).  Add one more byte for the
358            terminating '\0' */
359
360         ridbuf_size = (sizeof(char)*11) * num_rids + 1;
361
362         ridlist = talloc_zero_array(NULL, char, ridbuf_size);
363         BAIL_ON_PTR_ERROR(ridlist, wbc_status);
364
365         len = 0;
366         for (i=0; i<num_rids && (len-1)>0; i++) {
367                 char ridstr[12];
368
369                 len = strlen(ridlist);
370                 p = ridlist + len;
371
372                 snprintf( ridstr, sizeof(ridstr)-1, "%u\n", rids[i]);
373                 strncat(p, ridstr, ridbuf_size-len-1);
374         }
375
376         request.extra_data.data = ridlist;
377         request.extra_len = strlen(ridlist)+1;
378
379         wbc_status = wbcRequestResponse(WINBINDD_LOOKUPRIDS,
380                                         &request,
381                                         &response);
382         talloc_free(ridlist);
383         BAIL_ON_WBC_ERROR(wbc_status);
384
385         domain_name = talloc_strdup(NULL, response.data.domain_name);
386         BAIL_ON_PTR_ERROR(domain_name, wbc_status);
387
388         names = talloc_array(NULL, const char*, num_rids);
389         BAIL_ON_PTR_ERROR(names, wbc_status);
390
391         types = talloc_array(NULL, enum wbcSidType, num_rids);
392         BAIL_ON_PTR_ERROR(types, wbc_status);
393
394         p = (char *)response.extra_data.data;
395
396         for (i=0; i<num_rids; i++) {
397                 char *q;
398
399                 if (*p == '\0') {
400                         wbc_status = WBC_ERR_INVALID_RESPONSE;
401                         BAIL_ON_WBC_ERROR(wbc_status);
402                 }
403
404                 types[i] = (enum wbcSidType)strtoul(p, &q, 10);
405
406                 if (*q != ' ') {
407                         wbc_status = WBC_ERR_INVALID_RESPONSE;
408                         BAIL_ON_WBC_ERROR(wbc_status);
409                 }
410
411                 p = q+1;
412
413                 if ((q = strchr(p, '\n')) == NULL) {
414                         wbc_status = WBC_ERR_INVALID_RESPONSE;
415                         BAIL_ON_WBC_ERROR(wbc_status);
416                 }
417
418                 *q = '\0';
419
420                 names[i] = talloc_strdup(names, p);
421                 BAIL_ON_PTR_ERROR(names[i], wbc_status);
422
423                 p = q+1;
424         }
425
426         if (*p != '\0') {
427                 wbc_status = WBC_ERR_INVALID_RESPONSE;
428                 BAIL_ON_WBC_ERROR(wbc_status);
429         }
430
431         wbc_status = WBC_ERR_SUCCESS;
432
433  done:
434         if (response.extra_data.data) {
435                 free(response.extra_data.data);
436         }
437
438         if (WBC_ERROR_IS_OK(wbc_status)) {
439                 *pp_domain_name = domain_name;
440                 *pnames = names;
441                 *ptypes = types;
442         }
443         else {
444                 if (domain_name)
445                         talloc_free(domain_name);
446                 if (names)
447                         talloc_free(names);
448                 if (types)
449                         talloc_free(types);
450         }
451
452         return wbc_status;
453 }
454
455 /** @brief Get the groups a user belongs to
456  *
457  **/
458
459 wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
460                          bool domain_groups_only,
461                          uint32_t *num_sids,
462                          struct wbcDomainSid **_sids)
463 {
464         uint32_t i;
465         const char *s;
466         struct winbindd_request request;
467         struct winbindd_response response;
468         char *sid_string = NULL;
469         struct wbcDomainSid *sids = NULL;
470         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
471         int cmd;
472
473         /* Initialise request */
474
475         ZERO_STRUCT(request);
476         ZERO_STRUCT(response);
477
478         if (!user_sid) {
479                 wbc_status = WBC_ERR_INVALID_PARAM;
480                 BAIL_ON_WBC_ERROR(wbc_status);
481         }
482
483         wbc_status = wbcSidToString(user_sid, &sid_string);
484         BAIL_ON_WBC_ERROR(wbc_status);
485
486         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
487         wbcFreeMemory(sid_string);
488
489         if (domain_groups_only) {
490                 cmd = WINBINDD_GETUSERDOMGROUPS;
491         } else {
492                 cmd = WINBINDD_GETUSERSIDS;
493         }
494
495         wbc_status = wbcRequestResponse(cmd,
496                                         &request,
497                                         &response);
498         BAIL_ON_WBC_ERROR(wbc_status);
499
500         if (response.data.num_entries &&
501             !response.extra_data.data) {
502                 wbc_status = WBC_ERR_INVALID_RESPONSE;
503                 BAIL_ON_WBC_ERROR(wbc_status);
504         }
505
506         sids = talloc_array(NULL, struct wbcDomainSid,
507                             response.data.num_entries);
508         BAIL_ON_PTR_ERROR(sids, wbc_status);
509
510         s = (const char *)response.extra_data.data;
511         for (i = 0; i < response.data.num_entries; i++) {
512                 char *n = strchr(s, '\n');
513                 if (n) {
514                         *n = '\0';
515                 }
516                 wbc_status = wbcStringToSid(s, &sids[i]);
517                 BAIL_ON_WBC_ERROR(wbc_status);
518                 s += strlen(s) + 1;
519         }
520
521         *num_sids = response.data.num_entries;
522         *_sids = sids;
523         sids = NULL;
524         wbc_status = WBC_ERR_SUCCESS;
525
526  done:
527         if (response.extra_data.data) {
528                 free(response.extra_data.data);
529         }
530         if (sids) {
531                 talloc_free(sids);
532         }
533
534         return wbc_status;
535 }
536
537 /** @brief Lists Users
538  *
539  **/
540
541 wbcErr wbcListUsers(const char *domain_name,
542                     uint32_t *_num_users,
543                     const char ***_users)
544 {
545         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
546         struct winbindd_request request;
547         struct winbindd_response response;
548         uint32_t num_users = 0;
549         const char **users = NULL;
550         const char *next;
551
552         /* Initialise request */
553
554         ZERO_STRUCT(request);
555         ZERO_STRUCT(response);
556
557         if (domain_name) {
558                 strncpy(request.domain_name, domain_name,
559                         sizeof(request.domain_name)-1);
560         }
561
562         wbc_status = wbcRequestResponse(WINBINDD_LIST_USERS,
563                                         &request,
564                                         &response);
565         BAIL_ON_WBC_ERROR(wbc_status);
566
567         /* Look through extra data */
568
569         next = (const char *)response.extra_data.data;
570         while (next) {
571                 const char **tmp;
572                 const char *current = next;
573                 char *k = strchr(next, ',');
574                 if (k) {
575                         k[0] = '\0';
576                         next = k+1;
577                 } else {
578                         next = NULL;
579                 }
580
581                 tmp = talloc_realloc(NULL, users,
582                                      const char *,
583                                      num_users+1);
584                 BAIL_ON_PTR_ERROR(tmp, wbc_status);
585                 users = tmp;
586
587                 users[num_users] = talloc_strdup(users, current);
588                 BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
589
590                 num_users++;
591         }
592
593         *_num_users = num_users;
594         *_users = users;
595         users = NULL;
596         wbc_status = WBC_ERR_SUCCESS;
597
598  done:
599         if (response.extra_data.data) {
600                 free(response.extra_data.data);
601         }
602         if (users) {
603                 talloc_free(users);
604         }
605         return wbc_status;
606 }
607
608 /** @brief Lists Groups
609  *
610  **/
611
612 wbcErr wbcListGroups(const char *domain_name,
613                      uint32_t *_num_groups,
614                      const char ***_groups)
615 {
616         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
617         struct winbindd_request request;
618         struct winbindd_response response;
619         uint32_t num_groups = 0;
620         const char **groups = NULL;
621         const char *next;
622
623         /* Initialise request */
624
625         ZERO_STRUCT(request);
626         ZERO_STRUCT(response);
627
628         if (domain_name) {
629                 strncpy(request.domain_name, domain_name,
630                         sizeof(request.domain_name)-1);
631         }
632
633         wbc_status = wbcRequestResponse(WINBINDD_LIST_GROUPS,
634                                         &request,
635                                         &response);
636         BAIL_ON_WBC_ERROR(wbc_status);
637
638         /* Look through extra data */
639
640         next = (const char *)response.extra_data.data;
641         while (next) {
642                 const char **tmp;
643                 const char *current = next;
644                 char *k = strchr(next, ',');
645                 if (k) {
646                         k[0] = '\0';
647                         next = k+1;
648                 } else {
649                         next = NULL;
650                 }
651
652                 tmp = talloc_realloc(NULL, groups,
653                                      const char *,
654                                      num_groups+1);
655                 BAIL_ON_PTR_ERROR(tmp, wbc_status);
656                 groups = tmp;
657
658                 groups[num_groups] = talloc_strdup(groups, current);
659                 BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
660
661                 num_groups++;
662         }
663
664         *_num_groups = num_groups;
665         *_groups = groups;
666         groups = NULL;
667         wbc_status = WBC_ERR_SUCCESS;
668
669  done:
670         if (response.extra_data.data) {
671                 free(response.extra_data.data);
672         }
673         if (groups) {
674                 talloc_free(groups);
675         }
676         return wbc_status;
677 }