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