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