5a9c96507b625b63547c3dd102faccfaad71609d
[jerry/samba.git] / source / nsswitch / libwbclient / wbclient.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 "nsswitch/winbind_nss_config.h"
26 #include "nsswitch/winbind_struct_protocol.h"
27 #include "wbclient.h"
28 #include <talloc.h>
29
30 #define BAIL_ON_WBC_ERROR(x)  do { if ((x) != WBC_ERR_SUCCESS) goto fail; } while(0);
31
32 /* From wb_common.c */
33
34 NSS_STATUS winbindd_request_response(int req_type,
35                                      struct winbindd_request *request,
36                                      struct winbindd_response *response);
37
38 /** @brief Free library allocated memory
39  *
40  * @param *p Pointer to free
41  *
42  * @return void
43  **/
44
45 void wbcFreeMemory(void *p)
46 {
47         talloc_free(p);
48 }
49
50
51 /** @brief Convert a binary SID to a character string
52  *
53  * @param sid           Binary Security Identifier
54  * @param **sid_string  Resulting character string
55  *
56  * @return #wbcErr
57  **/
58
59 wbcErr wbcSidToString(const struct wbcDomainSid *sid,
60                       char **sid_string)
61 {
62         wbcErr ret = WBC_ERR_UNKNOWN_FAILURE;   
63         uint32_t id_auth;
64         int i;
65         char *tmp = NULL;       
66         TALLOC_CTX *mem_ctx = NULL;
67
68         if (!sid)
69                 return WBC_ERR_INVALID_SID;
70
71         if ((mem_ctx=talloc_init("wbcSidToString")) == NULL )
72                 return WBC_ERR_NO_MEMORY;
73                 
74         id_auth = sid->id_auth[5] +
75                 (sid->id_auth[4] << 8) +
76                 (sid->id_auth[3] << 16) +
77                 (sid->id_auth[2] << 24);
78
79         tmp = talloc_asprintf(mem_ctx, "S-%d-%d", sid->sid_rev_num, id_auth );
80         if (!tmp) {
81                 ret = WBC_ERR_NO_MEMORY;
82                 BAIL_ON_WBC_ERROR(ret);
83         }
84
85         for (i=0; i<sid->num_auths; i++) {
86                 char *tmp2 = 
87                 tmp2 = talloc_asprintf_append(tmp, "-%u", sid->sub_auths[i]);
88                 if (!tmp2) {
89                         ret = WBC_ERR_NO_MEMORY;                        
90                         BAIL_ON_WBC_ERROR(ret);                 
91                 }
92                 tmp = tmp2;             
93         }
94
95         if ((*sid_string=talloc_strdup(NULL, tmp)) == NULL ) {
96                 ret = WBC_ERR_NO_MEMORY;
97                 BAIL_ON_WBC_ERROR(ret);
98         }
99
100         ret = WBC_ERR_SUCCESS;
101
102  fail:
103         talloc_free(mem_ctx);
104
105         return ret;     
106 }
107
108 /** @brief Convert a character string to a binary SID
109  *
110  * @param *str          Character string in the form of S-...
111  * @param sid           Resulting binary SID
112  *
113  * @return #wbcErr
114  **/
115
116 wbcErr wbcStringToSid(const char *str,
117                       struct wbcDomainSid *sid)
118 {
119         const char *p;
120         char *q;
121         uint32_t x;
122
123         if (!sid)
124                 return WBC_ERR_INVALID_PARAM;
125
126         /* Sanity check for either "S-" or "s-" */
127
128         if (!str
129             || (str[0]!='S' && str[0]!='s')
130             || (str[1]!='-')
131             || (strlen(str)<2))
132         {
133                 return WBC_ERR_INVALID_SID;
134         }
135
136         /* Get the SID revision number */
137
138         p = str+2;
139         x = (uint32_t)strtol(p, &q, 10);
140         if (x==0 || !q || *q!='-')
141                 return WBC_ERR_INVALID_SID;
142         sid->sid_rev_num = (uint8_t)x;
143
144         /* Next the Identifier Authority.  This is stored in big-endian
145            in a 6 byte array. */
146
147         p = q+1;
148         x = (uint32_t)strtol(p, &q, 10);
149         if (x==0 || !q || *q!='-')
150                 return WBC_ERR_INVALID_SID;
151         sid->id_auth[5] = (x & 0x000000ff);
152         sid->id_auth[4] = (x & 0x0000ff00) >> 8;
153         sid->id_auth[3] = (x & 0x00ff0000) >> 16;
154         sid->id_auth[2] = (x & 0xff000000) >> 24;
155         sid->id_auth[1] = 0;
156         sid->id_auth[0] = 0;
157
158         /* now read the the subauthorities */
159
160         p = q +1;
161         sid->num_auths = 0;
162         while (sid->num_auths < MAXSUBAUTHS) {
163                 if ((x=(uint32_t)strtoul(p, &q, 10)) == 0)
164                         break;
165                 sid->sub_auths[sid->num_auths++] = x;
166
167                 if (q && ((*q!='-') || (*q=='\0')))
168                         break;
169                 p = q + 1;
170         }
171
172         /* IF we ended early, then the SID could not be converted */
173
174         if (q && *q!='\0')
175                 return WBC_ERR_INVALID_SID;
176
177         return WBC_ERR_SUCCESS;
178 }
179
180
181
182 /** @brief Convert a domain and name to SID
183  *
184  * @param domain      Domain name (possibly "")
185  * @param name        User or group name
186  * @param *sid        Pointer to the resolved domain SID
187  * @param *name_type  Pointet to the SID type
188  *
189  * @return #wbcErr
190  *
191  **/
192
193 wbcErr wbcLookupName(const char *domain,
194                      const char *name,
195                      struct wbcDomainSid *sid,
196                      enum wbcSidType *name_type)
197 {
198         struct winbindd_request request;
199         struct winbindd_response response;
200         NSS_STATUS result;
201         wbcErr wbc_ret = WBC_ERR_UNKNOWN_FAILURE;
202
203         if (!sid || !name_type)
204                 return WBC_ERR_INVALID_PARAM;
205
206         /* Initialize request */
207
208         ZERO_STRUCT(request);
209         ZERO_STRUCT(response);
210
211         /* dst is already null terminated from the memset above */
212
213         strncpy(request.data.name.dom_name, domain,
214                 sizeof(request.data.name.dom_name)-1);
215         strncpy(request.data.name.name, name,
216                 sizeof(request.data.name.name)-1);
217
218         result = winbindd_request_response(WINBINDD_LOOKUPNAME,
219                                            &request,
220                                            &response);
221         if ( result != NSS_STATUS_SUCCESS) {
222                 goto fail;
223         }
224
225         wbc_ret = wbcStringToSid(response.data.sid.sid, sid);
226         BAIL_ON_WBC_ERROR(wbc_ret);
227
228         *name_type = (enum wbcSidType)response.data.sid.type;
229         wbc_ret = WBC_ERR_SUCCESS;
230
231  fail:
232         return wbc_ret;
233 }
234
235 /** @brief Convert a SID to a domain and name
236  *
237  * @param *sid        Pointer to the domain SID to be resolved
238  * @param domain      Resolved Domain name (possibly "")
239  * @param name        Resolved User or group name
240  * @param *name_type  Pointet to the resolved SID type
241  *
242  * @return #wbcErr
243  *
244  **/
245
246 wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
247                     char **domain,
248                     char **name,
249                     enum wbcSidType *name_type)
250 {
251         struct winbindd_request request;
252         struct winbindd_response response;
253         NSS_STATUS result;
254         wbcErr wbc_ret = WBC_ERR_UNKNOWN_FAILURE;
255         char *sid_string = NULL;
256
257         *domain = NULL;
258         *name = NULL;
259
260         /* Initialize request */
261
262         ZERO_STRUCT(request);
263         ZERO_STRUCT(response);
264
265         /* dst is already null terminated from the memset above */
266
267         wbc_ret = wbcSidToString(sid, &sid_string);
268         BAIL_ON_WBC_ERROR(wbc_ret);
269
270         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
271         free(sid_string);
272
273         /* Make request */
274
275         result = winbindd_request_response(WINBINDD_LOOKUPSID,
276                                            &request,
277                                            &response);
278
279         if (result != NSS_STATUS_SUCCESS) {
280                 goto fail;
281         }
282
283         /* Copy out result */
284
285         if (domain != NULL) {
286                 if ((*domain = strdup(response.data.name.dom_name)) == NULL) {
287                         wbc_ret = WBC_ERR_NO_MEMORY;
288                         BAIL_ON_WBC_ERROR(wbc_ret);
289                 }
290         }
291
292         if (name != NULL) {
293                 if ((*name = strdup(response.data.name.name)) == NULL) {
294                         wbc_ret = WBC_ERR_NO_MEMORY;
295                         BAIL_ON_WBC_ERROR(wbc_ret);
296                 }
297         }
298
299         if (name_type)
300                 *name_type = (enum wbcSidType)response.data.name.type;
301
302         wbc_ret = WBC_ERR_SUCCESS;
303
304  fail:
305         if (wbc_ret != WBC_ERR_SUCCESS) {
306                 if (*domain)
307                         free(*domain);
308                 if (*name)
309                         free(*name);
310         }
311
312         return wbc_ret;
313 }
314
315 /** @brief Convert a Windows SID to a Unix uid
316  *
317  * @param *sid        Pointer to the domain SID to be resolved
318  * @param *puid       Pointer to the resolved uid_t value
319  *
320  * @return #wbcErr
321  *
322  **/
323
324 wbcErr wbcSidToUid(const struct wbcDomainSid *sid, uid_t *puid)
325 {
326         struct winbindd_request request;
327         struct winbindd_response response;
328         NSS_STATUS result;
329         char *sid_string = NULL;
330         wbcErr wbc_ret = WBC_ERR_UNKNOWN_FAILURE;
331
332         if (!puid)
333                 return 0;
334
335         /* Initialize request */
336
337         ZERO_STRUCT(request);
338         ZERO_STRUCT(response);
339
340         wbc_ret = wbcSidToString(sid, &sid_string);
341         BAIL_ON_WBC_ERROR(wbc_ret);
342
343         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
344         free(sid_string);
345
346         /* Make request */
347
348         result = winbindd_request_response(WINBINDD_SID_TO_UID,
349                                            &request,
350                                            &response);
351
352         /* Copy out result */
353
354         if (result != NSS_STATUS_SUCCESS) {
355                 return WBC_ERR_UNKNOWN_FAILURE;
356         }
357
358         *puid = response.data.uid;
359         wbc_ret = WBC_ERR_SUCCESS;
360
361  fail:
362         return wbc_ret;
363 }
364
365 /** @brief Convert a Unix uid to a Windows SID
366  *
367  * @param uid         Unix uid to be resolved
368  * @param *sid        Pointer to the resolved domain SID
369  *
370  * @return #wbcErr
371  *
372  **/
373
374 wbcErr wbcUidToSid(uid_t uid, struct wbcDomainSid *sid)
375 {
376         struct winbindd_request request;
377         struct winbindd_response response;
378         NSS_STATUS result;
379
380         if (!sid)
381                 return false;
382
383         /* Initialize request */
384
385         ZERO_STRUCT(request);
386         ZERO_STRUCT(response);
387
388         request.data.uid = uid;
389
390         /* Make request */
391
392         result = winbindd_request_response(WINBINDD_UID_TO_SID,
393                                            &request,
394                                            &response);
395
396         /* Copy out result */
397
398         if (result != NSS_STATUS_SUCCESS) {
399                 return WBC_ERR_UNKNOWN_FAILURE;
400         }
401
402         return wbcStringToSid(response.data.sid.sid, sid);
403 }
404
405 /** @brief Convert a Windows SID to a Unix gid
406  *
407  * @param *sid        Pointer to the domain SID to be resolved
408  * @param *pgid       Pointer to the resolved gid_t value
409  *
410  * @return #wbcErr
411  *
412  **/
413
414 wbcErr wbcSidToGid(const struct wbcDomainSid *sid, gid_t *pgid)
415 {
416         struct winbindd_request request;
417         struct winbindd_response response;
418         NSS_STATUS result;
419         wbcErr wbc_ret = WBC_ERR_UNKNOWN_FAILURE;
420         char *sid_string = NULL;
421
422         if (!pgid)
423                 return false;
424
425         /* Initialize request */
426
427         ZERO_STRUCT(request);
428         ZERO_STRUCT(response);
429
430         wbc_ret = wbcSidToString(sid, &sid_string);
431         BAIL_ON_WBC_ERROR(wbc_ret);
432
433         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
434         free(sid_string);
435
436         /* Make request */
437
438         result = winbindd_request_response(WINBINDD_SID_TO_GID,
439                                            &request,
440                                            &response);
441
442         /* Copy out result */
443
444         if (result != NSS_STATUS_SUCCESS) {
445                 return WBC_ERR_UNKNOWN_FAILURE;
446         }
447
448         *pgid = response.data.gid;
449         wbc_ret = WBC_ERR_SUCCESS;
450
451  fail:
452         return wbc_ret;
453 }
454
455 /** @brief Convert a Unix uid to a Windows SID
456  *
457  * @param gid         Unix gid to be resolved
458  * @param *sid        Pointer to the resolved domain SID
459  *
460  * @return #wbcErr
461  *
462  **/
463
464 wbcErr wbcGidToSid(gid_t gid, struct wbcDomainSid *sid)
465 {
466         struct winbindd_request request;
467         struct winbindd_response response;
468         NSS_STATUS result;
469
470         if (!sid)
471                 return false;
472
473         /* Initialize request */
474
475         ZERO_STRUCT(request);
476         ZERO_STRUCT(response);
477
478         request.data.gid = gid;
479
480         /* Make request */
481
482         result = winbindd_request_response(WINBINDD_GID_TO_SID,
483                                            &request,
484                                            &response);
485
486
487         /* Copy out result */
488
489         if (result != NSS_STATUS_SUCCESS) {
490                 return WBC_ERR_UNKNOWN_FAILURE;
491         }
492
493         return wbcStringToSid(response.data.sid.sid, sid);
494 }
495
496 /** @brief Ping winbindd to see if the daemon is running
497  *
498  * @return #wbcErr
499  **/
500
501 wbcErr wbcPing(void)
502 {
503         NSS_STATUS result;
504
505         result = winbindd_request_response(WINBINDD_PING, NULL, NULL);
506
507         if (result != NSS_STATUS_SUCCESS)
508                 return WBC_ERR_UNKNOWN_FAILURE;
509         
510         return WBC_ERR_SUCCESS; 
511 }
512
513 /** @brief Lookup the current status of a trusted domain
514  *
515  * @param domain      Domain to query
516  * @param *info       Pointer to returned domain_info struct
517  *
518  * @return #wbcErr
519  *
520  * The char* members of the struct wbcDomainInfo* are malloc()'d
521  * and it the the responsibility of the caller to free the members
522  * before  discarding the struct.
523  *
524  **/
525
526 /**********************************************************************
527  result == NSS_STATUS_UNAVAIL: winbind not around
528  result == NSS_STATUS_NOTFOUND: winbind around, but domain missing
529
530  Due to a bad API NSS_STATUS_NOTFOUND is returned both when winbind_off 
531  and when winbind return WINBINDD_ERROR. So the semantics of this 
532  routine depends on winbind_on. Grepping for winbind_off I just 
533  found 3 places where winbind is turned off, and this does not conflict 
534  (as far as I have seen) with the callers of is_trusted_domains.   
535
536  --Volker
537 **********************************************************************/
538
539 wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo *info)
540 {
541         struct winbindd_request request;
542         struct winbindd_response response;
543         NSS_STATUS result;
544         wbcErr ret;
545         
546         if (!domain || !info)
547                 return WBC_ERR_INVALID_PARAM;
548
549         ZERO_STRUCT(*info);
550         
551         /* Initialize request */
552
553         ZERO_STRUCT(request);
554         ZERO_STRUCT(response);
555
556         strncpy(request.domain_name, domain, sizeof(request.domain_name)-1);    
557
558         result = winbindd_request_response(WINBINDD_DOMAIN_INFO,
559                                            &request,
560                                            &response);
561
562         /* Copy out result */
563
564         if (result != NSS_STATUS_SUCCESS) {
565                 switch(result){
566                 case NSS_STATUS_UNAVAIL:
567                         return WBC_ERR_WINBIND_NOT_AVAILABLE;
568                         break;
569                 case NSS_STATUS_NOTFOUND:
570                         return WBC_ERR_DOMAIN_NOT_FOUND;
571                         break;                  
572                 default:
573                         return WBC_ERR_UNKNOWN_FAILURE;
574                 }               
575         }
576
577         info->short_name = strdup(response.data.domain_info.name);
578         info->dns_name   = strdup(response.data.domain_info.alt_name);
579         if (!info->short_name || !info->dns_name) {             
580                 ret = WBC_ERR_NO_MEMORY;
581                 BAIL_ON_WBC_ERROR(ret);
582         }       
583         
584         ret = wbcStringToSid(response.data.domain_info.sid, &info->sid);
585         BAIL_ON_WBC_ERROR(ret);
586         
587         if (response.data.domain_info.native_mode)
588                 info->flags |= WBC_DOMINFO_NATIVE;
589         if (response.data.domain_info.active_directory)
590                 info->flags |= WBC_DOMINFO_AD;
591         if (response.data.domain_info.primary)
592                 info->flags |= WBC_DOMINFO_PRIMARY;
593
594         ret = WBC_ERR_SUCCESS;  
595
596  fail:
597         if (ret != WBC_ERR_SUCCESS) {
598                 if (info->short_name)
599                         free(info->short_name);
600                 if (info->dns_name)
601                         free(info->dns_name);
602         }
603         
604         return ret;     
605 }
606
607 /** @brief Translate a collection of RIDs within a domain to names
608  *
609  **/
610
611 wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid, 
612                      int num_rids, 
613                      uint32_t *rids,
614                      const char **domain_name,
615                      const char ***names, 
616                      enum wbcSidType **types)
617 {
618         size_t i, len, ridbuf_size;     
619         char *ridlist;
620         char *p;
621         struct winbindd_request request;
622         struct winbindd_response response;
623         NSS_STATUS result;
624         char *sid_string = NULL;        
625         wbcErr ret;
626
627         if (num_rids == 0) {
628                 return WBC_ERR_INVALID_PARAM;
629         }
630
631         /* Initialise request */
632
633         ZERO_STRUCT(request);
634         ZERO_STRUCT(response);
635
636         ret = wbcSidToString(dom_sid, &sid_string);
637         BAIL_ON_WBC_ERROR(ret);
638
639         strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
640
641         /* Even if all the Rids were of maximum 32bit values,
642            we would only have 11 bytes per rid in the final array
643            ("4294967296" + \n).  Add one more byte for the 
644            terminating '\0' */
645
646         ridbuf_size = (sizeof(char)*11) * num_rids + 1;
647         if ((ridlist = malloc(ridbuf_size)) == NULL)
648                 return WBC_ERR_NO_MEMORY;
649         memset(ridlist, 0x0, ridbuf_size);      
650
651         len = 0;
652         for (i=0; i<num_rids && (len-1)>0; i++) {
653                 char ridstr[12];
654                 
655                 len = strlen(ridlist);
656                 p = ridlist + len;
657                 
658                 snprintf( ridstr, sizeof(ridstr)-1, "%u\n", rids[i]);
659                 strncat(p, ridstr, ridbuf_size-len-1);
660         }
661
662         request.extra_data.data = ridlist;
663         request.extra_len = strlen(ridlist)+1;
664
665         result = winbindd_request_response(WINBINDD_LOOKUPRIDS,
666                                            &request, &response);
667
668         free(ridlist);  
669
670         if (result != NSS_STATUS_SUCCESS) {
671                 return WBC_ERR_UNKNOWN_FAILURE;
672         }
673
674         *domain_name = strdup(response.data.domain_name);
675
676         *names = (const char**)malloc(sizeof(char*) * num_rids);
677         *types = (enum wbcSidType*)malloc(sizeof(enum wbcSidType) * num_rids);
678
679         if (!*names || !*types) {
680                 ret = WBC_ERR_NO_MEMORY;
681                 BAIL_ON_WBC_ERROR(ret);
682         }
683         
684         p = (char *)response.extra_data.data;
685
686         for (i=0; i<num_rids; i++) {
687                 char *q;
688
689                 if (*p == '\0') {
690                         ret = WCB_INVALID_RESPONSE;
691                         BAIL_ON_WBC_ERROR(ret);
692                 }
693                         
694                 (*types)[i] = (enum wbcSidType)strtoul(p, &q, 10);
695
696                 if (*q != ' ') {
697                         ret = WCB_INVALID_RESPONSE;
698                         BAIL_ON_WBC_ERROR(ret);
699                 }
700
701                 p = q+1;
702
703                 if ((q = strchr(p, '\n')) == NULL) {
704                         ret = WCB_INVALID_RESPONSE;
705                         BAIL_ON_WBC_ERROR(ret);
706                 }
707
708                 *q = '\0';
709
710                 (*names)[i] = strdup(p);
711
712                 p = q+1;
713         }
714
715         if (*p != '\0') {
716                 ret = WCB_INVALID_RESPONSE;
717                 BAIL_ON_WBC_ERROR(ret);
718         }
719
720         free(response.extra_data.data);
721
722         ret = WBC_ERR_SUCCESS;
723
724  fail:
725         if (ret != WBC_ERR_SUCCESS) {
726                 if (*domain_name)
727                         free(*domain_name);
728                 if (*names)
729                         free(*names);
730                 if (*types)
731                         free(*types);
732         }
733
734         return ret;
735 }
736
737 /** @brief Obtain a new uid from Winbind
738  *
739  * @param *puid      *pointer to the allocated uid
740  *
741  * @return #wbcErr
742  **/
743
744 wbcErr wbcAllocateUid(uid_t *puid)
745 {
746         struct winbindd_request request;
747         struct winbindd_response response;
748         NSS_STATUS result;
749
750         if (!puid)
751                 return WBC_ERR_INVALID_PARAM;
752         
753         /* Initialise request */
754
755         ZERO_STRUCT(request);
756         ZERO_STRUCT(response);
757
758         /* Make request */
759
760         result = winbindd_request_response(WINBINDD_ALLOCATE_UID,
761                                            &request, &response);
762
763         if (result != NSS_STATUS_SUCCESS)
764                 return WBC_ERR_UNKNOWN_FAILURE;
765
766         /* Copy out result */
767         *puid = response.data.uid;
768
769         return WBC_ERR_SUCCESS;
770 }
771
772 /** @brief Obtain a new gid from Winbind
773  *
774  * @param *pgid      Pointer to the allocated gid
775  *
776  * @return #wbcErr
777  **/
778
779 wbcErr wbcAllocateGid(uid_t *pgid)
780 {
781         struct winbindd_request request;
782         struct winbindd_response response;
783         NSS_STATUS result;
784
785         if (!pgid)
786                 return WBC_ERR_INVALID_PARAM;
787         
788         /* Initialise request */
789
790         ZERO_STRUCT(request);
791         ZERO_STRUCT(response);
792
793         /* Make request */
794
795         result = winbindd_request_response(WINBINDD_ALLOCATE_GID,
796                                            &request, &response);
797
798         if (result != NSS_STATUS_SUCCESS)
799                 return WBC_ERR_UNKNOWN_FAILURE;
800
801         /* Copy out result */
802         *pgid = response.data.gid;
803
804         return WBC_ERR_SUCCESS;
805 }
806