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#define _GNU_SOURCE
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <nss.h>
16#include <dlfcn.h>
17#include <errno.h>
18#include <string.h>
19#include <sys/types.h>
20
21#include "nss_winbind.h"
22
23/*
24  find a function in the nss library
25*/
26static void *find_fn(struct nss_state *nss, const char *name)
27{
28	void *res;
29	char *s = NULL;
30
31	asprintf(&s, "_nss_%s_%s", nss->nss_name, name);
32	if (!s) {
33		errno = ENOMEM;
34		return NULL;
35	}
36	res = dlsym(nss->dl_handle, s);
37	free(s);
38	if (!res) {
39		errno = ENOENT;
40		return NULL;
41	}
42	return res;
43}
44
45/*
46  establish a link to the nss library
47  Return 0 on success and -1 on error
48*/
49int nss_open(struct nss_state *nss, const char *nss_path)
50{
51	char *p;
52	p = strrchr(nss_path, '_');
53	if (!p) {
54		errno = EINVAL;
55		return -1;
56	}
57
58	nss->nss_name = strdup(p+1);
59	p = strchr(nss->nss_name, '.');
60	if (p) *p = 0;
61
62	nss->dl_handle = dlopen(nss_path, RTLD_LAZY);
63	if (!nss->dl_handle) {
64		free(nss->nss_name);
65		return -1;
66	}
67
68	return 0;
69}
70
71/*
72  close and cleanup a nss state
73*/
74void nss_close(struct nss_state *nss)
75{
76	free(nss->nss_name);
77	dlclose(nss->dl_handle);
78}
79
80/*
81  make a getpwnam call.
82  Return 0 on success and -1 on error
83*/
84int nss_getpwent(struct nss_state *nss, struct passwd *pwd)
85{
86	enum nss_status (*_nss_getpwent_r)(struct passwd *, char *,
87					   size_t , int *);
88	enum nss_status status;
89	int nss_errno = 0;
90
91	_nss_getpwent_r = find_fn(nss, "getpwent_r");
92
93	if (!_nss_getpwent_r) {
94		return -1;
95	}
96
97	status = _nss_getpwent_r(pwd, nss->pwnam_buf, sizeof(nss->pwnam_buf),
98				 &nss_errno);
99	if (status == NSS_STATUS_NOTFOUND) {
100		errno = ENOENT;
101		return -1;
102	}
103	if (status != NSS_STATUS_SUCCESS) {
104		errno = nss_errno;
105		return -1;
106	}
107
108	return 0;
109}
110
111/*
112  make a setpwent call.
113  Return 0 on success and -1 on error
114*/
115int nss_setpwent(struct nss_state *nss)
116{
117	enum nss_status (*_nss_setpwent)(void) = find_fn(nss, "setpwent");
118	enum nss_status status;
119	if (!_nss_setpwent) {
120		return -1;
121	}
122	status = _nss_setpwent();
123	if (status != NSS_STATUS_SUCCESS) {
124		errno = EINVAL;
125		return -1;
126	}
127	return 0;
128}
129
130/*
131  make a endpwent call.
132  Return 0 on success and -1 on error
133*/
134int nss_endpwent(struct nss_state *nss)
135{
136	enum nss_status (*_nss_endpwent)(void) = find_fn(nss, "endpwent");
137	enum nss_status status;
138	if (!_nss_endpwent) {
139		return -1;
140	}
141	status = _nss_endpwent();
142	if (status != NSS_STATUS_SUCCESS) {
143		errno = EINVAL;
144		return -1;
145	}
146	return 0;
147}
148
149
150/*
151  convert a name to a SID
152  caller frees
153  Return 0 on success and -1 on error
154*/
155int nss_nametosid(struct nss_state *nss, const char *name, char **sid)
156{
157	enum nss_status (*_nss_nametosid)(const char *, char **, char *,
158					  size_t, int *);
159	enum nss_status status;
160	int nss_errno = 0;
161	char buf[200];
162
163	_nss_nametosid = find_fn(nss, "nametosid");
164
165	if (!_nss_nametosid) {
166		return -1;
167	}
168
169	status = _nss_nametosid(name, sid, buf, sizeof(buf), &nss_errno);
170	if (status == NSS_STATUS_NOTFOUND) {
171		errno = ENOENT;
172		return -1;
173	}
174	if (status != NSS_STATUS_SUCCESS) {
175		errno = nss_errno;
176		return -1;
177	}
178
179	*sid = strdup(*sid);
180
181	return 0;
182}
183
184/*
185  convert a SID to a name
186  caller frees
187  Return 0 on success and -1 on error
188*/
189int nss_sidtoname(struct nss_state *nss, const char *sid, char **name)
190{
191	enum nss_status (*_nss_sidtoname)(const char *, char **, char *,
192					  size_t, int *);
193	enum nss_status status;
194	int nss_errno = 0;
195	char buf[200];
196
197	_nss_sidtoname = find_fn(nss, "sidtoname");
198
199	if (!_nss_sidtoname) {
200		return -1;
201	}
202
203	status = _nss_sidtoname(sid, name, buf, sizeof(buf), &nss_errno);
204	if (status == NSS_STATUS_NOTFOUND) {
205		errno = ENOENT;
206		return -1;
207	}
208	if (status != NSS_STATUS_SUCCESS) {
209		errno = nss_errno;
210		return -1;
211	}
212
213	*name = strdup(*name);
214
215	return 0;
216}
217
218/*
219  return a list of group SIDs for a user SID
220  the returned list is NULL terminated
221  Return 0 on success and -1 on error
222*/
223int nss_getusersids(struct nss_state *nss, const char *user_sid, char ***sids)
224{
225	enum nss_status (*_nss_getusersids)(const char *, char **, int *,
226					    char *, size_t, int *);
227	enum nss_status status;
228	int nss_errno = 0;
229	char *s;
230	int i, num_groups = 0;
231	unsigned bufsize = 10;
232	char *buf;
233
234	_nss_getusersids = find_fn(nss, "getusersids");
235
236	if (!_nss_getusersids) {
237		return -1;
238	}
239
240again:
241	buf = malloc(bufsize);
242	if (!buf) {
243		errno = ENOMEM;
244		return -1;
245	}
246
247	status = _nss_getusersids(user_sid, &s, &num_groups, buf, bufsize,
248				  &nss_errno);
249
250	if (status == NSS_STATUS_NOTFOUND) {
251		errno = ENOENT;
252		free(buf);
253		return -1;
254	}
255
256	if (status == NSS_STATUS_TRYAGAIN) {
257		bufsize *= 2;
258		free(buf);
259		goto again;
260	}
261
262	if (status != NSS_STATUS_SUCCESS) {
263		free(buf);
264		errno = nss_errno;
265		return -1;
266	}
267
268	if (num_groups == 0) {
269		free(buf);
270		return 0;
271	}
272
273	*sids = (char **)malloc(sizeof(char *) * (num_groups+1));
274	if (! *sids) {
275		errno = ENOMEM;
276		free(buf);
277		return -1;
278	}
279
280	for (i=0;i<num_groups;i++) {
281		(*sids)[i] = strdup(s);
282		s += strlen(s) + 1;
283	}
284	(*sids)[i] = NULL;
285
286	free(buf);
287
288	return 0;
289}
290
291/*
292  convert a sid to a uid
293  Return 0 on success and -1 on error
294*/
295int nss_sidtouid(struct nss_state *nss, const char *sid, uid_t *uid)
296{
297	enum nss_status (*_nss_sidtouid)(const char*, uid_t *, int*);
298
299	enum nss_status status;
300	int nss_errno = 0;
301
302	_nss_sidtouid = find_fn(nss, "sidtouid");
303
304	if (!_nss_sidtouid) {
305		return -1;
306	}
307
308	status = _nss_sidtouid(sid, uid, &nss_errno);
309
310	if (status == NSS_STATUS_NOTFOUND) {
311		errno = ENOENT;
312		return -1;
313	}
314
315	if (status != NSS_STATUS_SUCCESS) {
316		errno = nss_errno;
317		return -1;
318	}
319
320	return 0;
321}
322
323/*
324  convert a sid to a gid
325  Return 0 on success and -1 on error
326*/
327int nss_sidtogid(struct nss_state *nss, const char *sid, gid_t *gid)
328{
329	enum nss_status (*_nss_sidtogid)(const char*, gid_t *, int*);
330
331	enum nss_status status;
332	int nss_errno = 0;
333
334	_nss_sidtogid = find_fn(nss, "sidtogid");
335
336	if (!_nss_sidtogid) {
337		return -1;
338	}
339
340	status = _nss_sidtogid(sid, gid, &nss_errno);
341
342	if (status == NSS_STATUS_NOTFOUND) {
343		errno = ENOENT;
344		return -1;
345	}
346
347	if (status != NSS_STATUS_SUCCESS) {
348		errno = nss_errno;
349		return -1;
350	}
351
352	return 0;
353}
354
355/*
356  convert a uid to a sid
357  caller frees
358  Return 0 on success and -1 on error
359*/
360int nss_uidtosid(struct nss_state *nss, uid_t uid, char **sid)
361{
362	enum nss_status (*_nss_uidtosid)(uid_t, char **, char *,
363					 size_t, int *);
364	enum nss_status status;
365	int nss_errno = 0;
366	char buf[200];
367
368	_nss_uidtosid = find_fn(nss, "uidtosid");
369
370	if (!_nss_uidtosid) {
371		return -1;
372	}
373
374	status = _nss_uidtosid(uid, sid, buf, sizeof(buf), &nss_errno);
375	if (status == NSS_STATUS_NOTFOUND) {
376		errno = ENOENT;
377		return -1;
378	}
379	if (status != NSS_STATUS_SUCCESS) {
380		errno = nss_errno;
381		return -1;
382	}
383
384	*sid = strdup(*sid);
385
386	return 0;
387}
388
389/*
390  convert a gid to a sid
391  caller frees
392  Return 0 on success and -1 on error
393*/
394int nss_gidtosid(struct nss_state *nss, gid_t gid, char **sid)
395{
396	enum nss_status (*_nss_gidtosid)(gid_t, char **, char *,
397					 size_t, int *);
398	enum nss_status status;
399	int nss_errno = 0;
400	char buf[200];
401
402	_nss_gidtosid = find_fn(nss, "gidtosid");
403
404	if (!_nss_gidtosid) {
405		return -1;
406	}
407
408	status = _nss_gidtosid(gid, sid, buf, sizeof(buf), &nss_errno);
409	if (status == NSS_STATUS_NOTFOUND) {
410		errno = ENOENT;
411		return -1;
412	}
413	if (status != NSS_STATUS_SUCCESS) {
414		errno = nss_errno;
415		return -1;
416	}
417
418	*sid = strdup(*sid);
419
420	return 0;
421}
422
423