1
2/*
3 * This is a reimplementation of the BeOS R5 query-based multi-user system.
4 * (c) 2005, Fran��ois Revol.
5 * provided under the MIT licence
6 */
7
8//#ifdef REAL_MULTIUSER
9#if 1
10
11#include <pwd.h>
12#include <grp.h>
13
14#include <errno.h>
15#include <fs_attr.h>
16#include <fs_info.h>
17#include <fs_query.h>
18#include <string.h>
19#include <TLS.h>
20#include <Debug.h>
21#include <TypeConstants.h>
22
23#include <errno_private.h>
24
25/*
26 * Some notes.
27 * Users are stored in an fs node (not necessarily a regular file,
28 * that could be a dir, even the user's home dir,
29 * though we should be careful as they are owned by users.
30 */
31
32
33#define GR_MAX_NAME 32
34
35#define PW_MAX_NAME 32
36#define PW_MAX_DIR B_PATH_NAME_LENGTH
37#define PW_MAX_GECOS 128
38#define PW_MAX_PASSWD 32
39#define PW_MAX_SHELL B_PATH_NAME_LENGTH
40
41/* should be more than enough :) */
42#define GRBUFFSZ (GR_MAX_NAME + 2)
43#define PWBUFFSZ (PW_MAX_NAME + PW_MAX_DIR + PW_MAX_GECOS + PW_MAX_PASSWD + PW_MAX_SHELL + 4)
44
45
46/* attribute names we use to store groups & users. */
47/* pets nm & strings */
48
49static const char *B_GR_GID = "sys:group:gid";
50static const char *B_GR_NAME = "sys:group:name";
51//static const char *B_GR_PASSWD = "sys:group:passwd";
52
53static const char *B_PW_DIR = "sys:user:dir";
54static const char *B_PW_GECOS = "sys:user:fullname";
55static const char *B_PW_GID = "sys:user:gid";
56static const char *B_PW_NAME = "sys:user:name";
57static const char *B_PW_PASSWD = "sys:user:passwd";
58static const char *B_PW_SHELL = "sys:user:shell";
59static const char *B_PW_UID = "sys:user:uid";
60
61/* queries */
62static const char *Q_GR_ALL = "sys:group:gid>-1";
63static const char *QT_GR_GID = "sys:group:gid==%d";
64static const char *QT_GR_NAM = "sys:group:name==\"%s\"";
65static const char *Q_PW_ALL = "sys:user:uid>-1";
66static const char *QT_PW_UID = "sys:user:uid==%d";
67static const char *QT_PW_NAM = "sys:user:name==\"%s\"";
68
69extern void __init_pwd_stuff(void);
70extern void __fini_pwd_stuff(void);
71
72static char *default_gr_members[] = { /*"baron",*/ NULL };
73
74static dev_t boot_device;
75
76/* TLS stuff */
77
78static int32 pw_tls_id;
79
80typedef struct pw_tls {
81	DIR *grent_query;
82	DIR *pwent_query;
83
84	int gridx;
85	int pwidx;
86
87	char grfile[B_PATH_NAME_LENGTH+1]; /* current group's cached file path */
88	char pwfile[B_PATH_NAME_LENGTH+1]; /* current user's cached file path */
89
90	struct group grent;
91	char grbuff[GRBUFFSZ]; /* XXX: merge with pwbuff ? */
92
93	struct passwd pwent;
94	char pwbuff[PWBUFFSZ];
95} pw_tls_t;
96
97struct pw_tls *get_pw_tls(void)
98{
99	pw_tls_t *p = (pw_tls_t *)tls_get(pw_tls_id);
100	PRINT(("%s()\n", __FUNCTION__));
101
102	if (!p) {
103		p = (pw_tls_t *)malloc(sizeof(pw_tls_t));
104		if (!p)
105			return NULL;
106		memset(p, 0, sizeof(pw_tls_t));
107		p->grent_query = NULL;
108		p->pwent_query = NULL;
109		tls_set(pw_tls_id, p);
110	}
111	return p;
112}
113
114/* fill the path from the dirent and open() */
115int dentopen(struct dirent *dent, char *path)
116{
117	int err;
118	PRINT(("%s({%ld, %lld, %ld, %lld, %s}, )\n", __FUNCTION__, dent->d_pdev, dent->d_pino, dent->d_dev, dent->d_ino, dent->d_name));
119	dent->d_dev = boot_device;
120	err = get_path_for_dirent(dent, path, B_PATH_NAME_LENGTH);
121	if ((err < 0) || (path[0] != '/')) {
122		__set_errno(err);
123		return -1;
124	}
125	PRINT(("%s: open(%s)\n", __FUNCTION__, path));
126	return open(path, O_RDONLY);
127}
128
129/* group stuff */
130
131int fill_grent_default(struct group *gbuf)
132{
133	PRINT(("%s()\n", __FUNCTION__));
134	gbuf->gr_gid = 1000;
135	gbuf->gr_name = gbuf->gr_gid?"users":"wheel";
136	gbuf->gr_passwd = "";
137	gbuf->gr_mem = default_gr_members;
138	return 0;
139}
140
141int fill_grent_from_fd(int fd, struct group *gbuf, char *buf, size_t buflen)
142{
143	size_t left;
144	ssize_t len;
145	left = buflen;
146	len = fs_read_attr(fd, B_GR_GID, B_INT32_TYPE, 0LL, &gbuf->gr_gid, sizeof(gid_t));
147	if (len < 0)
148		return fill_grent_default(gbuf);
149	PRINT(("%s: got gid\n", __FUNCTION__));
150	gbuf->gr_passwd = "";
151	gbuf->gr_mem = default_gr_members;
152
153	if (left < GR_MAX_NAME + 1)
154		return ERANGE;
155	len = fs_read_attr(fd, B_GR_NAME, B_STRING_TYPE, 0LL, buf, GR_MAX_NAME);
156	if (len < 0)
157		return fill_grent_default(gbuf);
158	gbuf->gr_name = buf;
159	buf[len] = '\0';
160	left -= len + 1;
161	buf += len + 1;
162	PRINT(("%s: got name\n", __FUNCTION__));
163	return 0;
164}
165
166void setgrent(void)
167{
168	pw_tls_t *p;
169	p = get_pw_tls();
170	PRINT(("%s()\n", __FUNCTION__));
171	if (p->grent_query) /* clumsy apps */
172		fs_close_query(p->grent_query);
173	p->grent_query = fs_open_query(boot_device, Q_GR_ALL, 0);
174	PRINT(("pwq: %p\n", p->grent_query));
175	p->gridx = 0;
176}
177
178void endgrent(void)
179{
180	pw_tls_t *p;
181	PRINT(("%s()\n", __FUNCTION__));
182	p = get_pw_tls();
183
184	if (p->grent_query)
185		fs_close_query(p->grent_query);
186	p->grent_query = NULL;
187	p->gridx = -1;
188}
189
190
191/* this conforms to the linux getgrent_r (there are several protos for that one... crap) */
192/* note the FILE * based version is not supported as it makes no sense here */
193int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp)
194{
195	pw_tls_t *p;
196	int err;
197	int fd;
198	struct dirent *dent;
199	PRINT(("%s()\n", __FUNCTION__));
200	p = get_pw_tls();
201	if (!p)
202		return ENOMEM;
203PRINT(("getgrent_r: grq = %p, idx = %d\n", p->grent_query, p->gridx));
204	if (!p->grent_query)
205		setgrent(); /* y0u clumsy app! */
206	if (!p->grent_query)
207		return EIO; /* something happened... */
208	__set_errno(0);
209	dent = fs_read_query(p->grent_query);
210	*gbufp = NULL;
211	if (!dent) {
212		/* found nothing on first iteration ? */
213		if (p->gridx == 0) {
214			if (fill_grent_default(gbuf) < 0)
215				return -1;
216			*gbufp = gbuf;
217			p->gridx++;
218		}
219		return 0;
220	}
221	fd = dentopen(dent, p->grfile);
222	if (fd < B_OK)
223		return errno?errno:-1;
224	err = fill_grent_from_fd(fd, gbuf, buf, buflen);
225	PRINT(("%s: fill_grent_from_fd = %d\n", __FUNCTION__, err));
226	close(fd);
227	if (err)
228		return err;
229	p->gridx++;
230	*gbufp = gbuf;
231	return 0;
232}
233
234struct group *getgrent(void)
235{
236	pw_tls_t *p;
237	struct group *ent;
238	int err;
239	PRINT(("%s()\n", __FUNCTION__));
240	p = get_pw_tls();
241	if (!p) {
242		/* we are really bork */
243		__set_errno(ENOMEM);
244		return NULL;
245	}
246	err = getgrent_r(&p->grent, p->grbuff, GRBUFFSZ, &ent);
247	if (err < 0) {
248		__set_errno(err);
249		return NULL;
250	}
251	if (!ent)
252		return NULL;
253	PRINT(("getgrent(); returning entry for %s\n", ent->gr_name));
254	return ent;
255}
256
257/* by gid */
258struct group *getgrgid(gid_t gid)
259{
260	struct dirent *dent;
261	pw_tls_t *p;
262	DIR *query;
263	int err;
264	int fd;
265
266	PRINT(("%s()\n", __FUNCTION__));
267	p = get_pw_tls();
268	if (!p) {
269		/* we are really bork */
270		__set_errno(ENOMEM);
271		return NULL;
272	}
273
274	/* reusing path */
275	sprintf(p->grfile, QT_GR_GID, gid);
276	query = fs_open_query(boot_device, p->grfile, 0);
277	PRINT(("q: %p\n", query));
278	if (!query)
279		return NULL;
280
281	dent = fs_read_query(query);
282	if (!dent) {
283		fs_close_query(query);
284		return NULL;
285	}
286	fd = dentopen(dent, p->grfile);
287	fs_close_query(query);
288	if (fd < B_OK)
289		return NULL;
290	err = fill_grent_from_fd(fd, &p->grent, p->grbuff, GRBUFFSZ);
291	PRINT(("%s: fill_grent_from_fd = %d\n", __FUNCTION__, err));
292	close(fd);
293	if (err)
294		return NULL;
295	return &p->grent;
296
297}
298
299/* by name */
300struct group *getgrnam(const char *name)
301{
302	struct dirent *dent;
303	pw_tls_t *p;
304	DIR *query;
305	int err;
306	int fd;
307
308	PRINT(("%s()\n", __FUNCTION__));
309	p = get_pw_tls();
310	if (!p) {
311		/* we are really bork */
312		__set_errno(ENOMEM);
313		return NULL;
314	}
315
316	if (!name || strlen(name) > GR_MAX_NAME) {
317		__set_errno(EINVAL);
318		return NULL;
319	}
320	/* reusing path */
321	sprintf(p->grfile, QT_GR_NAM, name);
322	query = fs_open_query(boot_device, p->grfile, 0);
323	PRINT(("q: %p\n", query));
324	if (!query)
325		return NULL;
326
327	dent = fs_read_query(query);
328	if (!dent) {
329		fs_close_query(query);
330		return NULL;
331	}
332	fd = dentopen(dent, p->grfile);
333	fs_close_query(query);
334	if (fd < B_OK)
335		return NULL;
336	err = fill_grent_from_fd(fd, &p->grent, p->grbuff, GRBUFFSZ);
337	PRINT(("%s: fill_grent_from_fd = %d\n", __FUNCTION__, err));
338	close(fd);
339	if (err)
340		return NULL;
341	return &p->grent;
342
343}
344
345
346/* user stuff */
347
348int fill_pwent_default(struct passwd *pwbuf)
349{
350	PRINT(("%s()\n", __FUNCTION__));
351	return ENOENT; /* hmm no we don't exist! */
352	pwbuf->pw_gid = 1000;
353	pwbuf->pw_uid = 1000;
354	pwbuf->pw_name = "baron";
355	pwbuf->pw_passwd = "*";
356	pwbuf->pw_dir = "/var/tmp";
357	pwbuf->pw_shell = "/bin/false";
358	pwbuf->pw_gecos = "Unknown User";
359	return 0;
360}
361
362int fill_pwent_from_fd(int fd, struct passwd *pwbuf, char *buf, size_t buflen)
363{
364	ssize_t left;
365	ssize_t len;
366	PRINT(("%s()\n", __FUNCTION__));
367	left = buflen;
368	if (left <= 0)
369		return ERANGE;
370
371	len = fs_read_attr(fd, B_PW_GID, B_INT32_TYPE, 0LL, &pwbuf->pw_gid, sizeof(gid_t));
372	if (len < 0)
373		return fill_pwent_default(pwbuf);
374	PRINT(("%s: got gid\n", __FUNCTION__));
375
376	len = fs_read_attr(fd, B_PW_UID, B_INT32_TYPE, 0LL, &pwbuf->pw_uid, sizeof(uid_t));
377	if (len < 0)
378		return fill_pwent_default(pwbuf);
379	PRINT(("%s: got uid\n", __FUNCTION__));
380
381	if (left < PW_MAX_NAME + 1)
382		return ERANGE;
383	len = fs_read_attr(fd, B_PW_NAME, B_STRING_TYPE, 0LL, buf, PW_MAX_NAME);
384	if (len < 0)
385		return fill_pwent_default(pwbuf);
386	pwbuf->pw_name = buf;
387	buf[len] = '\0';
388	left -= len + 1;
389	buf += len + 1;
390	PRINT(("%s: got name\n", __FUNCTION__));
391
392	if (left < PW_MAX_DIR + 1)
393		return ERANGE;
394	len = fs_read_attr(fd, B_PW_DIR, B_STRING_TYPE, 0LL, buf, PW_MAX_DIR);
395	if (len < 0)
396		return fill_pwent_default(pwbuf);
397	pwbuf->pw_dir = buf;
398	buf[len] = '\0';
399	left -= len + 1;
400	buf += len + 1;
401	PRINT(("%s: got dir\n", __FUNCTION__));
402
403	if (left < PW_MAX_SHELL + 1)
404		return ERANGE;
405	len = fs_read_attr(fd, B_PW_SHELL, B_STRING_TYPE, 0LL, buf, PW_MAX_SHELL);
406	if (len < 0)
407		return fill_pwent_default(pwbuf);
408	pwbuf->pw_shell = buf;
409	buf[len] = '\0';
410	left -= len + 1;
411	buf += len + 1;
412	PRINT(("%s: got shell\n", __FUNCTION__));
413
414	if (left < PW_MAX_GECOS + 1)
415		return ERANGE;
416	len = fs_read_attr(fd, B_PW_GECOS, B_STRING_TYPE, 0LL, buf, PW_MAX_GECOS);
417	if (len < 0)
418		return fill_pwent_default(pwbuf);
419	pwbuf->pw_gecos = buf;
420	buf[len] = '\0';
421	left -= len + 1;
422	buf += len + 1;
423	PRINT(("%s: got gecos\n", __FUNCTION__));
424
425	if (left < PW_MAX_PASSWD + 1)
426		return ERANGE;
427	len = fs_read_attr(fd, B_PW_PASSWD, B_STRING_TYPE, 0LL, buf, PW_MAX_PASSWD);
428	if (len < 0) {
429		buf[0] = '*'; /* no pass set */
430		len = 1;
431	}
432	pwbuf->pw_passwd = buf;
433	buf[len] = '\0';
434	left -= len + 1;
435	buf += len + 1;
436	PRINT(("%s: got passwd\n", __FUNCTION__));
437
438	return 0;
439}
440
441
442
443void setpwent(void)
444{
445	pw_tls_t *p;
446	p = get_pw_tls();
447	PRINT(("%s()\n", __FUNCTION__));
448	if (p->pwent_query) /* clumsy apps */
449		fs_close_query(p->pwent_query);
450	p->pwent_query = fs_open_query(boot_device, Q_PW_ALL, 0);
451	PRINT(("pwq: %p\n", p->pwent_query));
452	p->pwidx = 0;
453}
454
455void endpwent(void)
456{
457	pw_tls_t *p;
458	PRINT(("%s()\n", __FUNCTION__));
459	p = get_pw_tls();
460
461	if (p->pwent_query)
462		fs_close_query(p->pwent_query);
463	p->pwent_query = NULL;
464	p->pwidx = -1;
465}
466
467
468/* this conforms to the linux getpwent_r (there are several protos for that one... crap) */
469/* note the FILE * based version is not supported as it makes no sense here */
470int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp)
471{
472	pw_tls_t *p;
473	int err;
474	int fd;
475	struct dirent *dent;
476	PRINT(("%s()\n", __FUNCTION__));
477	p = get_pw_tls();
478	if (!p)
479		return ENOMEM;
480PRINT(("getpwent_r: pwq = %p, idx = %d\n", p->pwent_query, p->pwidx));
481	if (!p->pwent_query)
482		setpwent(); /* y0u clumsy app! */
483	if (!p->pwent_query)
484		return EIO; /* something happened... */
485	__set_errno(0);
486	dent = fs_read_query(p->pwent_query);
487	*pwbufp = NULL;
488	if (!dent) {
489		/* found nothing on first iteration ? */
490		if (p->pwidx == 0) {
491			if (fill_pwent_default(pwbuf) < 0)
492				return -1;
493			*pwbufp = pwbuf;
494			p->pwidx++;
495		}
496		return 0;
497	}
498	fd = dentopen(dent, p->pwfile);
499	if (fd < B_OK)
500		return errno?errno:-1;
501	err = fill_pwent_from_fd(fd, pwbuf, buf, buflen);
502	PRINT(("%s: fill_pwent_from_fd = %d\n", __FUNCTION__, err));
503	close(fd);
504	if (err)
505		return err;
506	p->pwidx++;
507	*pwbufp = pwbuf;
508	return 0;
509}
510
511struct passwd *getpwent(void)
512{
513	pw_tls_t *p;
514	struct passwd *ent;
515	int err;
516	PRINT(("%s()\n", __FUNCTION__));
517	p = get_pw_tls();
518	if (!p) {
519		/* we are really bork */
520		__set_errno(ENOMEM);
521		return NULL;
522	}
523	err = getpwent_r(&p->pwent, p->pwbuff, PWBUFFSZ, &ent);
524	if (err < 0) {
525		__set_errno(err);
526		return NULL;
527	}
528	if (!ent)
529		return NULL;
530	PRINT(("getpwent(); returning entry for %s\n", ent->pw_name));
531	return ent;
532}
533
534/* by gid */
535struct passwd *getpwuid(uid_t uid)
536{
537	struct dirent *dent;
538	pw_tls_t *p;
539	DIR *query;
540	int err;
541	int fd;
542
543	PRINT(("%s(%d)\n", __FUNCTION__, uid));
544	p = get_pw_tls();
545	if (!p) {
546		/* we are really bork */
547		__set_errno(ENOMEM);
548		return NULL;
549	}
550
551	/* reusing path */
552	sprintf(p->pwfile, QT_PW_UID, uid);
553	PRINT(("%s: query(%s)\n", __FUNCTION__, p->pwfile));
554	query = fs_open_query(boot_device, p->pwfile, 0);
555	PRINT(("q: %p\n", query));
556	if (!query)
557		return NULL;
558
559	dent = fs_read_query(query);
560	if (!dent) {
561		fs_close_query(query);
562		return NULL;
563	}
564	fd = dentopen(dent, p->pwfile);
565	fs_close_query(query);
566	if (fd < B_OK)
567		return NULL;
568	err = fill_pwent_from_fd(fd, &p->pwent, p->pwbuff, PWBUFFSZ);
569	PRINT(("%s: fill_pwent_from_fd = %d\n", __FUNCTION__, err));
570	close(fd);
571	if (err)
572		return NULL;
573	return &p->pwent;
574
575}
576
577/* by name */
578struct passwd *getpwnam(const char *name)
579{
580	struct dirent *dent;
581	pw_tls_t *p;
582	DIR *query;
583	int err;
584	int fd;
585
586	PRINT(("%s(%s)\n", __FUNCTION__, name));
587	p = get_pw_tls();
588	if (!p) {
589		/* we are really bork */
590		__set_errno(ENOMEM);
591		return NULL;
592	}
593
594	if (!name || strlen(name) > PW_MAX_NAME) {
595		__set_errno(EINVAL);
596		return NULL;
597	}
598	/* reusing path */
599	sprintf(p->pwfile, QT_PW_NAM, name);
600	PRINT(("%s: query(%s)\n", __FUNCTION__, p->pwfile));
601	query = fs_open_query(boot_device, p->pwfile, 0);
602	PRINT(("q: %p\n", query));
603	if (!query)
604		return NULL;
605
606	dent = fs_read_query(query);
607	if (!dent) {
608		fs_close_query(query);
609		return NULL;
610	}
611	PRINT(("%s: dentopen()\n", __FUNCTION__));
612	fd = dentopen(dent, p->pwfile);
613	fs_close_query(query);
614	if (fd < B_OK)
615		return NULL;
616	err = fill_pwent_from_fd(fd, &p->pwent, p->pwbuff, PWBUFFSZ);
617	PRINT(("%s: fill_pwent_from_fd = %d\n", __FUNCTION__, err));
618	close(fd);
619	if (err)
620		return NULL;
621	return &p->pwent;
622
623}
624
625void __init_pwd_backend(void)
626{
627	/* dev_t for the boot volume */
628	boot_device = dev_for_path("/boot");
629	/* get us an id for holding thread-specific data */
630	pw_tls_id = tls_allocate();
631}
632
633/*
634void __fini_pwd_backend(void)
635{
636
637}
638*/
639
640#endif /* REAL_MULTIUSER */
641
642