added a wbtest program that shows how to access winbindd extended nss
[bbaumbach/samba-autobuild/.git] / examples / nss / wbtest.c
1 /* 
2    nss sample code for extended winbindd functionality
3
4    Copyright (C) Andrew Tridgell (tridge@samba.org)   
5
6    you are free to use this code in any way you see fit, including
7    without restriction, using this code in your own products. You do
8    not need to give any attribution.
9 */
10
11 /*
12    compile like this:
13
14       cc -o wbtest wbtest.c -ldl
15
16    and run like this:
17
18       ./wbtest /lib/libnss_winbind.so
19 */
20
21 #define _GNU_SOURCE
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <nss.h>
26 #include <dlfcn.h>
27 #include <pwd.h>
28 #include <grp.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <sys/types.h>
32
33 typedef enum nss_status NSS_STATUS;
34
35 struct nss_state {
36         void *dl_handle;
37         char *nss_name;
38         char pwnam_buf[512];
39 };
40
41 /*
42   find a function in the nss library
43 */
44 static void *find_fn(struct nss_state *nss, const char *name)
45 {
46         void *res;
47         char *s = NULL;
48
49         asprintf(&s, "_nss_%s_%s", nss->nss_name, name);
50         if (!s) {
51                 errno = ENOMEM;
52                 return NULL;
53         }
54         res = dlsym(nss->dl_handle, s);
55         free(s);
56         if (!res) {
57                 errno = ENOENT;
58                 return NULL;
59         }
60         return res;
61 }
62
63 /*
64   establish a link to the nss library
65   Return 0 on success and -1 on error
66 */
67 int nss_open(struct nss_state *nss, const char *nss_path)
68 {
69         char *p;
70         p = strrchr(nss_path, '_');
71         if (!p) {
72                 errno = EINVAL;
73                 return -1;
74         }
75
76         nss->nss_name = strdup(p+1);
77         p = strchr(nss->nss_name, '.');
78         if (p) *p = 0;
79
80         nss->dl_handle = dlopen(nss_path, RTLD_LAZY);
81         if (!nss->dl_handle) {
82                 free(nss->nss_name);
83                 return -1;
84         }
85
86         return 0;
87 }
88
89 /*
90   close and cleanup a nss state
91 */
92 void nss_close(struct nss_state *nss)
93 {
94         free(nss->nss_name);
95         dlclose(nss->dl_handle);
96 }
97
98 /*
99   make a getpwnam call. 
100   Return 0 on success and -1 on error
101 */
102 int nss_getpwent(struct nss_state *nss, struct passwd *pwd)
103 {
104         NSS_STATUS (*_nss_getpwent_r)(struct passwd *, char *, 
105                                       size_t , int *) = find_fn(nss, "getpwent_r");
106         NSS_STATUS status;
107         int nss_errno = 0;
108
109         if (!_nss_getpwent_r) {
110                 return -1;
111         }
112
113         status = _nss_getpwent_r(pwd, nss->pwnam_buf, sizeof(nss->pwnam_buf), &nss_errno);
114         if (status == NSS_STATUS_NOTFOUND) {
115                 errno = ENOENT;
116                 return -1;
117         }
118         if (status != NSS_STATUS_SUCCESS) {
119                 errno = nss_errno;
120                 return -1;
121         }
122
123         return 0;
124 }
125
126 /*
127   make a setpwent call. 
128   Return 0 on success and -1 on error
129 */
130 int nss_setpwent(struct nss_state *nss)
131 {
132         NSS_STATUS (*_nss_setpwent)(void) = find_fn(nss, "setpwent");
133         NSS_STATUS status;
134         if (!_nss_setpwent) {
135                 return -1;
136         }
137         status = _nss_setpwent();
138         if (status != NSS_STATUS_SUCCESS) {
139                 errno = EINVAL;
140                 return -1;
141         }
142         return 0;
143 }
144
145 /*
146   make a endpwent call. 
147   Return 0 on success and -1 on error
148 */
149 int nss_endpwent(struct nss_state *nss)
150 {
151         NSS_STATUS (*_nss_endpwent)(void) = find_fn(nss, "endpwent");
152         NSS_STATUS status;
153         if (!_nss_endpwent) {
154                 return -1;
155         }
156         status = _nss_endpwent();
157         if (status != NSS_STATUS_SUCCESS) {
158                 errno = EINVAL;
159                 return -1;
160         }
161         return 0;
162 }
163
164
165 /*
166   convert a name to a SID
167   caller frees
168   Return 0 on success and -1 on error
169 */
170 int nss_nametosid(struct nss_state *nss, const char *name, char **sid)
171 {
172         NSS_STATUS (*_nss_nametosid)(const char *, char **, char *, size_t, int *) = 
173                 find_fn(nss, "nametosid");
174         NSS_STATUS status;
175         int nss_errno = 0;
176         char buf[200];
177
178         if (!_nss_nametosid) {
179                 return -1;
180         }
181
182         status = _nss_nametosid(name, sid, buf, sizeof(buf), &nss_errno);
183         if (status == NSS_STATUS_NOTFOUND) {
184                 errno = ENOENT;
185                 return -1;
186         }
187         if (status != NSS_STATUS_SUCCESS) {
188                 errno = nss_errno;
189                 return -1;
190         }
191
192         *sid = strdup(*sid);
193
194         return 0;
195 }
196
197 /*
198   convert a SID to a name
199   caller frees
200   Return 0 on success and -1 on error
201 */
202 int nss_sidtoname(struct nss_state *nss, char *sid, char **name)
203 {
204         NSS_STATUS (*_nss_sidtoname)(const char *, char **, char *, size_t, int *) = 
205                 find_fn(nss, "sidtoname");
206         NSS_STATUS status;
207         int nss_errno = 0;
208         char buf[200];
209
210         if (!_nss_sidtoname) {
211                 return -1;
212         }
213
214         status = _nss_sidtoname(sid, name, buf, sizeof(buf), &nss_errno);
215         if (status == NSS_STATUS_NOTFOUND) {
216                 errno = ENOENT;
217                 return -1;
218         }
219         if (status != NSS_STATUS_SUCCESS) {
220                 errno = nss_errno;
221                 return -1;
222         }
223
224         *name = strdup(*name);
225
226         return 0;
227 }
228
229 /*
230   return a list of group SIDs for a user SID
231   the returned list is NULL terminated
232   Return 0 on success and -1 on error
233 */
234 int nss_getusersids(struct nss_state *nss, const char *user_sid, char ***sids)
235 {
236         NSS_STATUS (*_nss_getusersids)(const char *, char **, int *, char *, size_t, int *) = 
237                 find_fn(nss, "getusersids");
238         NSS_STATUS status;
239         int nss_errno = 0;
240         char *s;
241         int i, num_groups = 0;
242         unsigned bufsize = 10;
243         char *buf;
244
245         if (!_nss_getusersids) {
246                 return -1;
247         }
248
249 again:
250         buf = malloc(bufsize);
251         if (!buf) {
252                 errno = ENOMEM;
253                 return -1;
254         }
255
256         status = _nss_getusersids(user_sid, &s, &num_groups, buf, bufsize, &nss_errno);
257         if (status == NSS_STATUS_NOTFOUND) {
258                 errno = ENOENT;
259                 free(buf);
260                 return -1;
261         }
262         
263         if (status == NSS_STATUS_TRYAGAIN) {
264                 bufsize *= 2;
265                 free(buf);
266                 goto again;
267         }
268
269         if (status != NSS_STATUS_SUCCESS) {
270                 free(buf);
271                 errno = nss_errno;
272                 return -1;
273         }
274
275         if (num_groups == 0) {
276                 free(buf);
277                 return 0;
278         }
279
280         *sids = (char **)malloc(sizeof(char *) * (num_groups+1));
281         if (! *sids) {
282                 errno = ENOMEM;
283                 free(buf);
284                 return -1;
285         }
286
287         for (i=0;i<num_groups;i++) {
288                 (*sids)[i] = strdup(s);
289                 s += strlen(s) + 1;
290         }
291         (*sids)[i] = NULL;
292
293         free(buf);
294
295         return 0;
296 }
297
298
299 static int nss_test_users(struct nss_state *nss)
300 {
301         struct passwd pwd;
302
303         if (nss_setpwent(nss) != 0) {
304                 perror("setpwent");
305                 return -1;
306         }
307
308         /* loop over all users */
309         while ((nss_getpwent(nss, &pwd) == 0)) {
310                 char *sid, **group_sids, *name2;
311                 int i;
312
313                 printf("User %s\n", pwd.pw_name);
314                 if (nss_nametosid(nss, pwd.pw_name, &sid) != 0) {
315                         perror("nametosid");
316                         return -1;
317                 }
318                 printf("\tSID %s\n", sid);
319
320                 if (nss_sidtoname(nss, sid, &name2) != 0) {
321                         perror("sidtoname");
322                         return -1;
323                 }
324                 printf("\tSID->name %s\n", name2);
325
326                 if (nss_getusersids(nss, sid, &group_sids) != 0) {
327                         perror("getusersids");
328                         return -1;
329                 }
330
331                 printf("\tGroups:\n");
332                 for (i=0; group_sids[i]; i++) {
333                         printf("\t\t%s\n", group_sids[i]);
334                         free(group_sids[i]);
335                 }
336
337                 free(sid);
338                 free(name2);
339                 free(group_sids);
340         }
341
342
343         if (nss_endpwent(nss) != 0) {
344                 perror("endpwent");
345                 return -1;
346         }
347
348         return 0;
349 }
350
351
352 /*
353   main program. It lists all users, listing user SIDs for each user
354  */
355 int main(int argc, char *argv[])
356 {       
357         struct nss_state nss;
358         const char *so_path = "/lib/libnss_winbind.so";
359         int ret;
360
361         if (argc > 1) {
362                 so_path = argv[1];
363         }
364
365         if (nss_open(&nss, so_path) != 0) {
366                 perror("nss_open");
367                 exit(1);
368         }
369
370         ret = nss_test_users(&nss);
371
372         nss_close(&nss);
373
374         return ret;
375 }