1/*
2   Unix SMB/CIFS implementation.
3
4   Windows NT Domain nsswitch module
5
6   Copyright (C) Tim Potter 2000
7   Copyright (C) James Peach 2006
8
9   This library is free software; you can redistribute it and/or
10   modify it under the terms of the GNU Library General Public
11   License as published by the Free Software Foundation; either
12   version 2 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 Library General Public
20   License along with this library; if not, write to the
21   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22   Boston, MA  02111-1307, USA.
23*/
24
25#include "winbind_client.h"
26
27#ifndef PRINTF_ATTRIBUTE
28#define PRINTF_ATTRIBUTE(m, n)
29#endif
30
31#ifndef HAVE_ASPRINTF_DECL
32/*PRINTFLIKE2 */
33int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
34#endif
35
36#ifdef HAVE_NS_API_H
37#undef VOLATILE
38#undef STATIC
39#undef DYNAMIC
40#include <ns_daemon.h>
41#endif
42
43/* Maximum number of users to pass back over the unix domain socket
44   per call. This is not a static limit on the total number of users
45   or groups returned in total. */
46
47#define MAX_GETPWENT_USERS 250
48#define MAX_GETGRENT_USERS 250
49
50/* Prototypes from wb_common.c */
51
52extern int winbindd_fd;
53
54#ifdef HAVE_NS_API_H
55
56/* IRIX version */
57
58static int send_next_request(nsd_file_t *, struct winbindd_request *);
59static int do_list(int state, nsd_file_t *rq);
60
61static nsd_file_t *current_rq = NULL;
62static int current_winbind_xid = 0;
63static int next_winbind_xid = 0;
64
65typedef struct winbind_xid {
66	int			xid;
67	nsd_file_t		*rq;
68	struct winbindd_request *request;
69	struct winbind_xid	*next;
70} winbind_xid_t;
71
72static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
73
74static int
75winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
76{
77	winbind_xid_t *new;
78
79	nsd_logprintf(NSD_LOG_LOW,
80		"entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
81		xid, rq, request);
82	new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
83	if (!new) {
84		nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
85		return NSD_ERROR;
86	}
87
88	new->xid = xid;
89	new->rq = rq;
90	new->request = request;
91	new->next = winbind_xids;
92	winbind_xids = new;
93
94	return NSD_CONTINUE;
95}
96
97/*
98** This routine will look down the xid list and return the request
99** associated with an xid.  We remove the record if it is found.
100*/
101nsd_file_t *
102winbind_xid_lookup(int xid, struct winbindd_request **requestp)
103{
104        winbind_xid_t **last, *dx;
105        nsd_file_t *result=0;
106
107        for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
108            last = &dx->next, dx = dx->next);
109        if (dx) {
110                *last = dx->next;
111                result = dx->rq;
112		*requestp = dx->request;
113                SAFE_FREE(dx);
114        }
115	nsd_logprintf(NSD_LOG_LOW,
116		"entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
117		xid, result, dx->request);
118
119        return result;
120}
121
122static int
123winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
124{
125	nsd_file_t *rq;
126	struct winbindd_request *request;
127
128	nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
129	rq = to->t_file;
130	*rqp = rq;
131	nsd_timeout_remove(rq);
132	request = to->t_clientdata;
133	return(send_next_request(rq, request));
134}
135
136static void
137dequeue_request(void)
138{
139	nsd_file_t *rq;
140	struct winbindd_request *request;
141
142	/*
143	 * Check for queued requests
144	 */
145	if (winbind_xids) {
146	    nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
147			current_winbind_xid);
148	    rq = winbind_xid_lookup(current_winbind_xid++, &request);
149	    /* cause a timeout on the queued request so we can send it */
150	    nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
151	}
152}
153
154static int
155do_request(nsd_file_t *rq, struct winbindd_request *request)
156{
157	if (winbind_xids == NULL) {
158		/*
159		 * No outstanding requests.
160		 * Send off the request to winbindd
161		 */
162		nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
163		return(send_next_request(rq, request));
164	} else {
165		/*
166		 * Just queue it up for now - previous callout or timout
167		 * will start it up
168		 */
169		nsd_logprintf(NSD_LOG_MIN,
170			"lookup (winbind): queue request xid = %d\n",
171			next_winbind_xid);
172		return(winbind_xid_new(next_winbind_xid++, rq, request));
173	}
174}
175
176static int
177winbind_callback(nsd_file_t **rqp, int fd)
178{
179	struct winbindd_response response;
180	nsd_file_t *rq;
181	NSS_STATUS status;
182	char * result = NULL;
183	size_t rlen;
184
185	dequeue_request();
186
187	nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
188
189	rq = current_rq;
190	*rqp = rq;
191
192	nsd_timeout_remove(rq);
193	nsd_callback_remove(fd);
194
195	ZERO_STRUCT(response);
196	status = winbindd_get_response(&response);
197
198	if (status != NSS_STATUS_SUCCESS) {
199		/* free any extra data area in response structure */
200		free_response(&response);
201		nsd_logprintf(NSD_LOG_MIN,
202			"callback (winbind) returning not found, status = %d\n",
203			status);
204
205		switch (status) {
206		    case NSS_STATUS_UNAVAIL:
207			rq->f_status = NS_UNAVAIL;
208			break;
209		    case NSS_STATUS_TRYAGAIN:
210			rq->f_status = NS_TRYAGAIN;
211			break;
212		    case NSS_STATUS_NOTFOUND:
213			/* FALLTHRU */
214		    default:
215			rq->f_status = NS_NOTFOUND;
216		}
217
218		return NSD_NEXT;
219	}
220
221	switch ((int)rq->f_cmd_data) {
222	    case WINBINDD_WINS_BYNAME:
223	    case WINBINDD_WINS_BYIP:
224		nsd_logprintf(NSD_LOG_MIN,
225			"callback (winbind) WINS_BYNAME | WINS_BYIP\n");
226
227		rlen = asprintf(&result, "%s\n", response.data.winsresp);
228		if (rlen == 0 || result == NULL) {
229			return NSD_ERROR;
230		}
231
232		free_response(&response);
233
234		nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
235		nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
236		return NSD_OK;
237
238	    case WINBINDD_GETPWUID:
239	    case WINBINDD_GETPWNAM:
240	    {
241	        struct winbindd_pw *pw = &response.data.pw;
242
243	        nsd_logprintf(NSD_LOG_MIN,
244			"callback (winbind) GETPWUID | GETPWUID\n");
245
246	        rlen = asprintf(&result,"%s:%s:%d:%d:%s:%s:%s\n",
247	                        pw->pw_name,
248	                        pw->pw_passwd,
249	                        pw->pw_uid,
250	                        pw->pw_gid,
251	                        pw->pw_gecos,
252	                        pw->pw_dir,
253	                        pw->pw_shell);
254	        if (rlen == 0 || result == NULL)
255	            return NSD_ERROR;
256
257	        free_response(&response);
258
259	        nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
260	        nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
261	        return NSD_OK;
262	    }
263
264	    case WINBINDD_GETGRNAM:
265	    case WINBINDD_GETGRGID:
266	    {
267	        const struct winbindd_gr *gr = &response.data.gr;
268	        const char * members;
269
270	        nsd_logprintf(NSD_LOG_MIN,
271			"callback (winbind) GETGRNAM | GETGRGID\n");
272
273	        if (gr->num_gr_mem && response.extra_data.data) {
274	                members = response.extra_data.data;
275	        } else {
276	                members = "";
277	        }
278
279	        rlen = asprintf(&result, "%s:%s:%d:%s\n",
280			    gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
281	        if (rlen == 0 || result == NULL)
282	            return NSD_ERROR;
283
284	        free_response(&response);
285
286	        nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
287	        nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
288	        return NSD_OK;
289	    }
290
291	    case WINBINDD_SETGRENT:
292	    case WINBINDD_SETPWENT:
293		nsd_logprintf(NSD_LOG_MIN,
294			"callback (winbind) SETGRENT | SETPWENT\n");
295		free_response(&response);
296		return(do_list(1,rq));
297
298	    case WINBINDD_GETGRENT:
299	    case WINBINDD_GETGRLST:
300	    {
301	        int entries;
302
303	        nsd_logprintf(NSD_LOG_MIN,
304		    "callback (winbind) GETGRENT | GETGRLIST %d responses\n",
305		    response.data.num_entries);
306
307	        if (response.data.num_entries) {
308	            const struct winbindd_gr *gr = &response.data.gr;
309	            const char * members;
310	            fstring grp_name;
311	            int     i;
312
313	            gr = (struct winbindd_gr *)response.extra_data.data;
314	            if (! gr ) {
315	                nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
316	                free_response(&response);
317	                return NSD_ERROR;
318	            }
319
320	            members = (char *)response.extra_data.data +
321			(response.data.num_entries * sizeof(struct winbindd_gr));
322
323	            for (i = 0; i < response.data.num_entries; i++) {
324	                snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:",
325	                            gr->gr_name, gr->gr_passwd, gr->gr_gid);
326
327	                nsd_append_element(rq, NS_SUCCESS, result, rlen);
328	                nsd_append_result(rq, NS_SUCCESS,
329				&members[gr->gr_mem_ofs],
330	                        strlen(&members[gr->gr_mem_ofs]));
331
332	                /* Don't log the whole list, because it might be
333	                 * _really_ long and we probably don't want to clobber
334	                 * the log with it.
335	                 */
336	                nsd_logprintf(NSD_LOG_MIN, "    %s (...)\n", grp_name);
337
338	                gr++;
339	            }
340	        }
341
342	        entries = response.data.num_entries;
343	        free_response(&response);
344	        if (entries < MAX_GETPWENT_USERS)
345	            return(do_list(2,rq));
346	        else
347	            return(do_list(1,rq));
348	    }
349
350	    case WINBINDD_GETPWENT:
351	    {
352		int entries;
353
354		nsd_logprintf(NSD_LOG_MIN,
355			"callback (winbind) GETPWENT  %d responses\n",
356			response.data.num_entries);
357
358		if (response.data.num_entries) {
359		    struct winbindd_pw *pw = &response.data.pw;
360		    int i;
361
362		    pw = (struct winbindd_pw *)response.extra_data.data;
363		    if (! pw ) {
364			nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
365			free_response(&response);
366			return NSD_ERROR;
367		    }
368		    for (i = 0; i < response.data.num_entries; i++) {
369			result = NULL;
370			rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s",
371					pw->pw_name,
372					pw->pw_passwd,
373					pw->pw_uid,
374					pw->pw_gid,
375					pw->pw_gecos,
376					pw->pw_dir,
377					pw->pw_shell);
378
379			if (rlen != 0 && result != NULL) {
380			    nsd_logprintf(NSD_LOG_MIN, "    %s\n",result);
381			    nsd_append_element(rq, NS_SUCCESS, result, rlen);
382			    free(result);
383			}
384
385			pw++;
386		    }
387		}
388
389		entries = response.data.num_entries;
390		free_response(&response);
391		if (entries < MAX_GETPWENT_USERS)
392		    return(do_list(2,rq));
393		else
394		    return(do_list(1,rq));
395	    }
396
397	    case WINBINDD_ENDGRENT:
398	    case WINBINDD_ENDPWENT:
399		nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n");
400		nsd_append_element(rq, NS_SUCCESS, "\n", 1);
401		free_response(&response);
402		return NSD_NEXT;
403
404	    default:
405		free_response(&response);
406		nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data);
407		return NSD_NEXT;
408	}
409}
410
411static int
412winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
413{
414	nsd_file_t *rq;
415
416	dequeue_request();
417
418	nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
419
420	rq = to->t_file;
421	*rqp = rq;
422
423	/* Remove the callback and timeout */
424	nsd_callback_remove(winbindd_fd);
425	nsd_timeout_remove(rq);
426
427	rq->f_status = NS_NOTFOUND;
428	return NSD_NEXT;
429}
430
431static int
432send_next_request(nsd_file_t *rq, struct winbindd_request *request)
433{
434	NSS_STATUS status;
435	long timeout;
436
437        switch (rq->f_index) {
438                case LOOKUP:
439                        timeout = nsd_attr_fetch_long(rq->f_attrs,
440                                        "lookup_timeout", 10, 10);
441                        break;
442                case LIST:
443                        timeout = nsd_attr_fetch_long(rq->f_attrs,
444                                        "list_timeout", 10, 10);
445                        break;
446                default:
447	                nsd_logprintf(NSD_LOG_OPER,
448                                "send_next_request (winbind) "
449                                "invalid request type %d\n", rq->f_index);
450                        rq->f_status = NS_BADREQ;
451                        return NSD_NEXT;
452        }
453
454	nsd_logprintf(NSD_LOG_MIN,
455		"send_next_request (winbind) %d, timeout = %d sec\n",
456			rq->f_cmd_data, timeout);
457	status = winbindd_send_request((int)rq->f_cmd_data,0,request);
458	SAFE_FREE(request);
459
460	if (status != NSS_STATUS_SUCCESS) {
461		nsd_logprintf(NSD_LOG_MIN,
462			"send_next_request (winbind) error status = %d\n",
463			status);
464		rq->f_status = status;
465		return NSD_NEXT;
466	}
467
468	current_rq = rq;
469
470	/*
471	 * Set up callback and timeouts
472	 */
473	nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",
474		winbindd_fd);
475
476	nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ);
477	nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL);
478	return NSD_CONTINUE;
479}
480
481int init(void)
482{
483	nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
484	return(NSD_OK);
485}
486
487int lookup(nsd_file_t *rq)
488{
489	char *map;
490	char *key;
491	struct winbindd_request *request;
492
493	nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
494	if (! rq)
495		return NSD_ERROR;
496
497	map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
498	key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
499	if (! map || ! key) {
500		nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
501		rq->f_status = NS_BADREQ;
502		return NSD_ERROR;
503	}
504
505	nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
506
507	request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
508	if (! request) {
509		nsd_logprintf(NSD_LOG_RESOURCE,
510			"lookup (winbind): failed malloc\n");
511		return NSD_ERROR;
512	}
513
514	if (strcasecmp(map,"passwd.byuid") == 0) {
515	    request->data.uid = atoi(key);
516	    rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
517	} else if (strcasecmp(map,"passwd.byname") == 0) {
518	    strncpy(request->data.username, key,
519		sizeof(request->data.username) - 1);
520	    request->data.username[sizeof(request->data.username) - 1] = '\0';
521	    rq->f_cmd_data = (void *)WINBINDD_GETPWNAM;
522	} else if (strcasecmp(map,"group.byname") == 0) {
523	    strncpy(request->data.groupname, key,
524		sizeof(request->data.groupname) - 1);
525	    request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
526	    rq->f_cmd_data = (void *)WINBINDD_GETGRNAM;
527	} else if (strcasecmp(map,"group.bygid") == 0) {
528	    request->data.gid = atoi(key);
529	    rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
530	} else if (strcasecmp(map,"hosts.byname") == 0) {
531	    strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
532	    request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
533	    rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
534	} else if (strcasecmp(map,"hosts.byaddr") == 0) {
535	    strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
536	    request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
537	    rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
538	} else {
539		/*
540		 * Don't understand this map - just return not found
541		 */
542		nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
543		SAFE_FREE(request);
544		rq->f_status = NS_NOTFOUND;
545		return NSD_NEXT;
546	}
547
548	return(do_request(rq, request));
549}
550
551int list(nsd_file_t *rq)
552{
553	char *map;
554
555	nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
556	if (! rq)
557		return NSD_ERROR;
558
559	map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
560	if (! map ) {
561		nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
562		rq->f_status = NS_BADREQ;
563		return NSD_ERROR;
564	}
565
566	nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
567
568	return (do_list(0,rq));
569}
570
571static int
572do_list(int state, nsd_file_t *rq)
573{
574	char *map;
575	struct winbindd_request *request;
576
577	nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
578
579	map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
580	request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
581	if (! request) {
582		nsd_logprintf(NSD_LOG_RESOURCE,
583			"do_list (winbind): failed malloc\n");
584		return NSD_ERROR;
585	}
586
587	if (strcasecmp(map,"passwd.byname") == 0) {
588	    switch (state) {
589		case 0:
590		    rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
591		    break;
592		case 1:
593		    request->data.num_entries = MAX_GETPWENT_USERS;
594		    rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
595		    break;
596		case 2:
597		    rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
598		    break;
599		default:
600		    nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
601		    SAFE_FREE(request);
602		    rq->f_status = NS_NOTFOUND;
603		    return NSD_NEXT;
604	    }
605	} else if (strcasecmp(map,"group.byname") == 0) {
606	    switch (state) {
607		case 0:
608		    rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
609		    break;
610		case 1:
611		    request->data.num_entries = MAX_GETGRENT_USERS;
612		    rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
613		    break;
614		case 2:
615		    rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
616		    break;
617		default:
618		    nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
619		    SAFE_FREE(request);
620		    rq->f_status = NS_NOTFOUND;
621		    return NSD_NEXT;
622	    }
623	} else {
624		/*
625		 * Don't understand this map - just return not found
626		 */
627		nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
628		SAFE_FREE(request);
629		rq->f_status = NS_NOTFOUND;
630		return NSD_NEXT;
631	}
632
633	return(do_request(rq, request));
634}
635
636#endif /* HAVE_NS_API_H */
637