xref: /haiku/src/system/libroot/posix/pwd_query.c (revision f11b30463296494633476a81bdb9a64e8cb2e85d)
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 
49 static const char *B_GR_GID = "sys:group:gid";
50 static const char *B_GR_NAME = "sys:group:name";
51 //static const char *B_GR_PASSWD = "sys:group:passwd";
52 
53 static const char *B_PW_DIR = "sys:user:dir";
54 static const char *B_PW_GECOS = "sys:user:fullname";
55 static const char *B_PW_GID = "sys:user:gid";
56 static const char *B_PW_NAME = "sys:user:name";
57 static const char *B_PW_PASSWD = "sys:user:passwd";
58 static const char *B_PW_SHELL = "sys:user:shell";
59 static const char *B_PW_UID = "sys:user:uid";
60 
61 /* queries */
62 static const char *Q_GR_ALL = "sys:group:gid>-1";
63 static const char *QT_GR_GID = "sys:group:gid==%d";
64 static const char *QT_GR_NAM = "sys:group:name==\"%s\"";
65 static const char *Q_PW_ALL = "sys:user:uid>-1";
66 static const char *QT_PW_UID = "sys:user:uid==%d";
67 static const char *QT_PW_NAM = "sys:user:name==\"%s\"";
68 
69 extern void __init_pwd_stuff(void);
70 extern void __fini_pwd_stuff(void);
71 
72 static char *default_gr_members[] = { /*"baron",*/ NULL };
73 
74 static dev_t boot_device;
75 
76 /* TLS stuff */
77 
78 static int32 pw_tls_id;
79 
80 typedef 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 
97 struct 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() */
115 int 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 
131 int 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 
141 int 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 
166 void 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 
178 void 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 */
193 int 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;
203 PRINT(("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 
234 struct 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 */
258 struct 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 */
300 struct 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 
348 int 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 
362 int 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 
443 void 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 
455 void 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 */
470 int 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;
480 PRINT(("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 
511 struct 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 */
535 struct 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 */
578 struct 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 
625 void __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 /*
634 void __fini_pwd_backend(void)
635 {
636 
637 }
638 */
639 
640 #endif /* REAL_MULTIUSER */
641 
642