Improvements to fogbugz oo stuff to make it more schedulator-compatible.
[wvapps.git] / nss / libnss_uniconf.cc
blob9cda764cda2ab61a286c8d3c1abe5e991a60b796
1 #include "uniconfroot.h"
2 #include "wvlog.h"
3 #include "wvlogfile.h"
4 #include "wvtclstring.h"
6 #include <stdlib.h>
7 #include <nss.h>
8 #include <pwd.h>
9 #include <grp.h>
10 #include <shadow.h>
11 #include <utmp.h>
12 #include <time.h>
13 #include <sys/types.h>
14 #include <pthread.h>
15 #include <errno.h>
17 #define MY_RETURN(x) do \
18 { \
19 pthread_mutex_unlock(&nssunilock); \
20 return x; \
21 } while(0)
22 #define MY_INIT do \
23 { \
24 pthread_mutex_lock(&nssunilock); \
25 init(); \
26 } while (0)
27 #define LOCK pthread_mutex_lock(&nssunilock)
28 #define UNLOCK pthread_mutex_unlock(&nssunilock)
30 pthread_mutex_t nssunilock = PTHREAD_MUTEX_INITIALIZER;
32 #ifdef DOLOG
33 #define LOG(info...) log(info)
34 static WvLog log("nss-uniconf", WvLog::Debug4);
35 static WvLogFile logger("/tmp/nss-uniconf", WvLog::Debug5);
36 #else
37 #define LOG(info...) ;
38 static WvLogFileBase logger("/dev/null", WvLog::Debug5);
39 #endif
41 static UniConfRoot *cfgroot = NULL;
42 static UniConf cfg, fnames, uids, groups;
43 static UniConf::Iter *uiter = NULL;
44 static UniConf::Iter *giter = NULL;
45 static UniConf::Iter *siter = NULL;
47 static void init()
49 // FIXME: when do we plan to _free_ these?
50 if (!cfgroot)
52 // connect to the local UniConf daemon
53 cfgroot = new UniConfRoot();
54 cfg = UniConf(*cfgroot);
56 // cache in the daemon, not here
57 cfg.mount("auto:ca/nit/nssuniconf");
59 fnames = cfg["Full Names"];
60 uids = cfg["UIDs"];
61 groups = cfg["Groups"];
63 uiter = new UniConf::Iter(uids);
64 giter = new UniConf::Iter(uids);
65 siter = new UniConf::Iter(uids);
66 uiter->rewind();
67 giter->rewind();
68 siter->rewind();
72 static bool putgroup(
73 gid_t newgrp, gid_t **groupsp,
74 long int *start, long int *size, long int limit)
76 if (*start == *size && limit <= 0)
78 LOG("Not enough room for group entries. Reallocating.\n");
79 gid_t *newgroups =
80 (gid_t *)realloc((*groupsp), (2 * (*size) + 1) * sizeof(gid_t));
82 if (!newgroups)
84 LOG("Failed to reallocate.\n");
85 return false;
88 *groupsp = newgroups;
89 *size = 2 * (*size) + 1;
92 if (*start < *size)
94 (*groupsp)[*start] = newgrp;
95 *start += 1;
98 LOG("Successfully added group %s to the list", newgrp);
99 return true;
102 static char *putmatch(WvBuf &out, WvStringParm s)
104 WvConstStringBuffer buf(s);
106 int startofs = out.used();
107 while (buf.used())
109 out.putstr(wvtcl_getword(buf));
110 if (buf.used())
111 out.putstr(" ");
113 out.putch(0);
115 return (char *)out.mutablepeek(startofs, out.used() - (unsigned)startofs);
118 static char **putmatches(WvBuf &out, WvStringParm s)
120 WvStringList users;
121 users.split(s, " ");
123 if (0 == users.count())
124 return NULL;
126 int startofl = out.used();
127 int currptr = startofl;
129 // an extra ptr for NULL at the end
130 size_t ptrsize = (users.count() + 1)*sizeof(char *);
131 char *ptrs = new char[ptrsize];
133 memset(ptrs, 0, ptrsize);
134 out.put(ptrs, ptrsize);
136 deletev ptrs;
138 // dump the strings into the buffer
139 WvStringList::Iter i(users);
140 for (i.rewind(); i.next(); )
142 char *bufstr = putmatch(out, i());
143 out.poke(&bufstr, currptr, sizeof(char *));
144 currptr += sizeof(char *);
147 return (char **)out.mutablepeek(startofl, out.used() - (unsigned)startofl);
151 static enum nss_status pwmatch(struct passwd *pw, int *errnop,
152 char *buf, size_t buflen,
153 WvStringParm username)
155 WvInPlaceBuf out(buf, 0, buflen);
157 WvString homedir("/export/home/%s", username);
158 WvString shell("/bin/bash");
159 WvString gecos(fnames[username].getme());
160 if (!gecos)
161 gecos = username;
163 pw->pw_name = putmatch(out, username);
164 pw->pw_passwd = putmatch(out, "x");
165 pw->pw_uid = uids[username].getmeint(65534);
166 pw->pw_gid = uids[username].getmeint(65534);
167 pw->pw_gecos = putmatch(out, gecos);
168 pw->pw_dir = putmatch(out, homedir);
169 pw->pw_shell = putmatch(out, shell);
171 if (out.free() == 0)
173 *errnop = errno = ERANGE;
174 return NSS_STATUS_TRYAGAIN;
176 else
178 *errnop = errno = 0;
179 return NSS_STATUS_SUCCESS;
184 static enum nss_status grmatch(struct group *gr, int *errnop,
185 char *buf, size_t buflen,
186 WvStringParm username)
188 WvInPlaceBuf out(buf, 0, buflen);
189 WvString members = groups[username].getme();
190 if (!members)
191 members = username;
193 gr->gr_name = putmatch(out, username);
194 gr->gr_passwd = putmatch(out, "x");
195 gr->gr_gid = uids[username].getmeint(65534);
196 gr->gr_mem = putmatches(out, members);
198 if (out.free() == 0)
200 *errnop = errno = ERANGE;
201 return NSS_STATUS_TRYAGAIN;
203 else
205 *errnop = errno = 0;
206 return NSS_STATUS_SUCCESS;
211 static enum nss_status spmatch(struct spwd *sp, int *errnop,
212 char *buf, size_t buflen,
213 WvStringParm username)
215 WvInPlaceBuf out(buf, 0, buflen);
216 time_t now = time(NULL);
217 long lastchg = (now / (60*60*24)) - 1;
219 sp->sp_namp = putmatch(out, username);
220 sp->sp_pwdp = NULL;
221 sp->sp_lstchg = lastchg;
222 sp->sp_min = 0;
223 sp->sp_max = LONG_MAX;
224 sp->sp_warn = 10;
225 sp->sp_inact = LONG_MAX;
226 sp->sp_expire = LONG_MAX;
228 if (out.free() == 0)
230 *errnop = errno = ERANGE;
231 return NSS_STATUS_TRYAGAIN;
233 else
235 *errnop = errno = 0;
236 return NSS_STATUS_SUCCESS;
241 extern "C" enum nss_status _nss_uniconf_getpwnam_r(
242 const char *user, struct passwd *pw,
243 char *buf, size_t buflen, int *errnop)
245 MY_INIT;
246 LOG("%s(%s) called.\n", __func__, user);
248 enum nss_status ret = NSS_STATUS_NOTFOUND;
249 *errnop = errno = ENOENT;
251 if (uids[user].exists())
252 ret = pwmatch(pw, errnop, buf, buflen, user);
254 MY_RETURN(ret);
258 extern "C" enum nss_status _nss_uniconf_getpwuid_r(
259 uid_t uid, struct passwd *pw,
260 char *buf, size_t buflen, int *errnop)
262 MY_INIT;
263 LOG("%s(%s) called.\n", __func__, uid);
265 WvString username;
266 enum nss_status ret = NSS_STATUS_NOTFOUND;
267 *errnop = errno = ENOENT;
269 UniConf::Iter i(uids);
270 for (i.rewind(); i.next(); )
272 if (uid == (unsigned)i._value().num())
274 username = i().key().printable();
275 ret = pwmatch(pw, errnop, buf, buflen, username);
276 break;
280 MY_RETURN(ret);
285 extern "C" enum nss_status _nss_uniconf_getgrnam_r(
286 const char *user, struct group *gr,
287 char *buf, size_t buflen, int *errnop)
289 MY_INIT;
290 LOG("%s(%s) called.\n", __func__, user);
292 enum nss_status ret = NSS_STATUS_NOTFOUND;
293 *errnop = errno = ENOENT;
295 if (uids[user].exists())
296 ret = grmatch(gr, errnop, buf, buflen, user);
298 MY_RETURN(ret);
302 extern "C" enum nss_status _nss_uniconf_getgrgid_r(
303 gid_t gid, struct group *gr,
304 char *buf, size_t buflen, int *errnop)
306 MY_INIT;
307 LOG("%s(%s) called.\n", __func__, gid);
309 WvString username;
310 enum nss_status ret = NSS_STATUS_NOTFOUND;
311 *errnop = errno = ENOENT;
313 UniConf::Iter i(uids);
314 for (i.rewind(); i.next(); )
316 if (gid == (unsigned)i._value().num())
318 username = i().key().printable();
319 ret = grmatch(gr, errnop, buf, buflen, username);
320 break;
324 MY_RETURN(ret);
327 extern "C" enum nss_status _nss_uniconf_getspnam_r(
328 const char *user, struct spwd *sp,
329 char *buf, size_t buflen, int *errnop)
331 MY_INIT;
332 LOG("%s(%s) called.\n", __func__, user);
334 enum nss_status ret = NSS_STATUS_NOTFOUND;
335 *errnop = errno = ENOENT;
337 if (uids[user].exists())
338 ret = spmatch(sp, errnop, buf, buflen, user);
340 MY_RETURN(ret);
344 extern "C" enum nss_status _nss_uniconf_getspuid_r(
345 uid_t uid, struct spwd *sp,
346 char *buf, size_t buflen, int *errnop)
348 MY_INIT;
349 LOG("%s(%s) called.\n", __func__, uid);
351 WvString username;
352 enum nss_status ret = NSS_STATUS_NOTFOUND;
353 *errnop = errno = ENOENT;
355 UniConf::Iter i(uids);
356 for (i.rewind(); i.next(); )
358 if (uid == (unsigned)i._value().num())
360 username = i().key().printable();
361 ret = spmatch(sp, errnop, buf, buflen, username);
362 break;
366 MY_RETURN(ret);
369 extern "C" enum nss_status _nss_uniconf_setpwent()
371 MY_INIT;
372 LOG("%s() called.\n", __func__);
374 MY_RETURN(NSS_STATUS_SUCCESS);
377 extern "C" enum nss_status _nss_uniconf_setgrent()
379 MY_INIT;
380 LOG("%s() called.\n", __func__);
382 MY_RETURN(NSS_STATUS_SUCCESS);
385 extern "C" enum nss_status _nss_uniconf_endpwent()
387 MY_INIT;
388 LOG("%s() called.\n", __func__);
390 MY_RETURN(NSS_STATUS_SUCCESS);
393 extern "C" enum nss_status _nss_uniconf_endgrent()
395 MY_INIT;
396 LOG("%s() called.\n", __func__);
398 MY_RETURN(NSS_STATUS_SUCCESS);
401 extern "C" enum nss_status _nss_uniconf_setspent()
403 MY_INIT;
404 LOG("%s() called.\n", __func__);
406 MY_RETURN(NSS_STATUS_SUCCESS);
409 extern "C" enum nss_status _nss_uniconf_endspent()
411 MY_INIT;
412 LOG("%s() called.\n", __func__);
414 MY_RETURN(NSS_STATUS_SUCCESS);
417 extern "C" enum nss_status _nss_uniconf_getpwent_r(
418 struct passwd *pw, char *buf,
419 int buflen, int *errnop)
421 MY_INIT;
422 LOG("%s() called.\n", __func__);
424 UniConf::Iter &i = *uiter;
426 if (i.next())
428 WvString username = i().key().printable();
429 MY_RETURN(_nss_uniconf_getpwnam_r(username, pw,
430 buf, buflen, errnop));
432 else
433 i.rewind();
435 errno = *errnop = ENOENT;
436 MY_RETURN(NSS_STATUS_NOTFOUND);
439 extern "C" enum nss_status _nss_uniconf_getgrent_r(
440 struct group *gr, char *buf,
441 int buflen, int *errnop)
443 MY_INIT;
444 LOG("%s() called.\n", __func__);
446 UniConf::Iter &i = *uiter;
448 if (i.next())
450 WvString username = i().key().printable();
451 MY_RETURN(_nss_uniconf_getgrnam_r(username, gr,
452 buf, buflen, errnop));
454 else
455 i.rewind();
457 errno = *errnop = ENOENT;
458 MY_RETURN(NSS_STATUS_NOTFOUND);
461 extern "C" enum nss_status _nss_uniconf_getspent_r(
462 struct spwd *sp, char *buf,
463 int buflen, int *errnop)
465 MY_INIT;
466 LOG("%s() called.\n", __func__);
468 UniConf::Iter &i = *uiter;
470 if (i.next())
472 WvString username = i().key().printable();
473 MY_RETURN(_nss_uniconf_getspnam_r(username, sp,
474 buf, buflen, errnop));
476 else
477 i.rewind();
479 errno = *errnop = ENOENT;
480 MY_RETURN(NSS_STATUS_NOTFOUND);
483 extern "C" struct passwd *_nss_uniconf_getpwent(void)
485 MY_INIT;
486 LOG("%s() called.\n", __func__);
488 struct passwd *ret = (struct passwd *)malloc(sizeof(struct passwd));
489 if (!ret)
490 MY_RETURN(NULL);
492 int errnum = 0;
493 size_t bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
494 UniConf::Iter &i = *uiter;
496 if (i.next())
498 char *buf = (char *)malloc(bufsize);
499 if (!buf)
500 goto fail;
502 WvString username = i().key().printable();
504 // don't double-lock
505 UNLOCK;
506 enum nss_status status =
507 _nss_uniconf_getpwnam_r(username, ret,
508 buf, bufsize, &errnum);
509 LOCK;
511 if (NSS_STATUS_SUCCESS == status)
512 MY_RETURN(ret);
513 else
514 free(buf);
516 else
517 i.rewind();
519 fail:
520 free(ret);
522 MY_RETURN(NULL);
525 extern "C" struct group *_nss_uniconf_getgrent(void)
527 MY_INIT;
528 LOG("%s() called.\n", __func__);
530 struct group *ret = (struct group *)malloc(sizeof(struct group));
531 if (!ret)
532 MY_RETURN(NULL);
534 int errnum = 0;
535 size_t bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
536 UniConf::Iter &i = *giter;
538 if (i.next())
540 char *buf = (char *)malloc(bufsize);
541 if (!buf)
542 goto fail;
544 WvString username = i().key().printable();
546 // don't double-lock
547 UNLOCK;
548 enum nss_status status =
549 _nss_uniconf_getgrnam_r(username, ret,
550 buf, bufsize, &errnum);
551 LOCK;
553 if (NSS_STATUS_SUCCESS == status)
554 MY_RETURN(ret);
555 else
556 free(buf);
558 else
559 i.rewind();
561 fail:
562 free(ret);
564 MY_RETURN(NULL);
567 extern "C" struct spwd *_nss_uniconf_getspent(void)
569 MY_INIT;
570 LOG("%s() called.\n", __func__);
572 struct spwd *ret = (struct spwd *)malloc(sizeof(struct spwd));
573 if (!ret)
574 return NULL;
576 int errnum = 0;
577 size_t bufsize = UT_NAMESIZE;
578 UniConf::Iter &i = *siter;
580 if (i.next())
582 char *buf = (char *)malloc(bufsize);
583 if (!buf)
584 goto fail;
586 WvString username = i().key().printable();
588 // don't double-lock
589 UNLOCK;
590 enum nss_status status =
591 _nss_uniconf_getspnam_r(username, ret,
592 buf, bufsize, &errnum);
593 LOCK;
595 if (NSS_STATUS_SUCCESS == status)
596 MY_RETURN(ret);
597 else
598 free(buf);
600 else
601 i.rewind();
603 fail:
604 free(ret);
606 MY_RETURN(NULL);
609 extern "C" enum nss_status _nss_uniconf_initgroups_dyn(
610 const char *user, gid_t group, long int *start, long int *size,
611 gid_t **groupsp, long int limit, int *errnop)
613 MY_INIT;
614 LOG("%s() called.\n", __func__);
616 if (!user || !uids[user].exists())
618 errno = *errnop = ENOENT;
619 MY_RETURN(NSS_STATUS_NOTFOUND);
622 if (*start > *size)
624 errno = *errnop = EINVAL;
625 MY_RETURN(NSS_STATUS_UNAVAIL);
628 gid_t curr = 65534;
629 int nmem = 0;
630 WvString members;
631 WvStringList mlist;
633 UniConf::Iter i(groups);
634 for (i.rewind(); i.next(); )
636 members = i().getme();
637 mlist.split(members, " ");
638 curr = uids[i().key().printable()].getmeint();
639 nmem = mlist.count();
641 if (curr == group || !nmem)
642 continue;
644 while (nmem)
646 if (mlist.popstr() == user)
648 LOG("I think %s is in group %s.\n", user, curr);
649 if (!putgroup(curr, groupsp, start, size, limit))
651 errno = *errnop = ENOMEM;
652 MY_RETURN(NSS_STATUS_TRYAGAIN);
655 break;
658 nmem--;
662 errno = *errnop = 0;
663 MY_RETURN(NSS_STATUS_SUCCESS);