Merged revisions 10129-10142 via svnmerge from
[wvapps.git] / nss-uniconf / libnss_uniconf.cc
blob3a156c17ef8f5bbe78916800d092a9decdd864bf
1 /*
2 * Copyright (C) 2002-2004 Net Integration Technologies, Inc.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "uniconfroot.h"
20 #include "wvlog.h"
21 #include "wvlogfile.h"
22 #include "wvtclstring.h"
24 #include <stdlib.h>
25 #include <nss.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <shadow.h>
29 #include <utmp.h>
30 #include <time.h>
31 #include <sys/types.h>
32 #include <pthread.h>
33 #include <errno.h>
35 #define MY_RETURN(x) do \
36 { \
37 pthread_mutex_unlock(&nssunilock); \
38 LOG("%s() returning.\n", __func__); \
39 return x; \
40 } while(0)
41 #define MY_INIT do \
42 { \
43 pthread_mutex_lock(&nssunilock); \
44 LOG("%s() executing.\n", __func__); \
45 } while (0)
47 pthread_mutex_t nssunilock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
49 //#define DOLOG 1
50 #ifdef DOLOG
51 #define LOG(info...) log(info)
52 static WvLog log("nss-uniconf", WvLog::Debug4);
53 static WvLogFile logger("/tmp/nss-uniconf", WvLog::Debug5);
54 #else
55 #define LOG(info...) ;
56 static WvLogFileBase logger("/dev/null", WvLog::Debug5);
57 #endif
59 class NssUniConf
61 public:
62 NssUniConf() :
63 uiter(NULL),
64 giter(NULL)
66 // connect to the local UniConf daemon
67 cfg = UniConf(cfgroot);
69 // cache in the daemon, not here
70 cfg.mount("auto:ca/nit/nssuniconf");
72 fnames = cfg["Full Names"];
73 uids = cfg["UIDs"];
74 groups = cfg["Groups"];
75 admins = cfg["Admins"];
77 ~NssUniConf()
79 LOG("Program that uses us closing, cleaning up..\n");
80 delete uiter;
81 delete giter;
84 UniConf::Iter & get_uiter()
86 if (!uiter)
88 uiter = new UniConf::Iter(uids);
89 uiter->rewind();
91 return *uiter;
93 void release_uiter() { delete uiter; uiter = NULL; }
95 UniConf::Iter & get_giter()
97 if (!giter)
99 giter = new UniConf::Iter(groups);
100 giter->rewind();
102 return *giter;
104 void release_giter() { delete giter; giter = NULL; }
106 UniConfRoot cfgroot;
107 UniConf cfg, fnames, uids, groups, admins;
109 private:
110 UniConf::Iter *uiter;
111 UniConf::Iter *giter;
114 NssUniConf uniconf;
116 static bool putgroup(
117 gid_t newgrp, gid_t **groupsp,
118 long int *start, long int *size, long int limit)
120 LOG("%s(%s) called.\n", __func__, newgrp);
122 if (*start == *size && limit <= 0)
124 LOG("Not enough room for group entries. Reallocating.\n");
125 gid_t *newgroups =
126 (gid_t *)realloc((*groupsp), (2 * (*size) + 1) * sizeof(gid_t));
128 if (!newgroups)
130 LOG("Failed to reallocate.\n");
131 return false;
134 *groupsp = newgroups;
135 *size = 2 * (*size) + 1;
138 if (*start < *size)
140 (*groupsp)[*start] = newgrp;
141 *start += 1;
144 LOG("Successfully added group %s to the list.\n", newgrp);
145 return true;
148 static char *putmatch(WvBuf &out, WvStringParm s)
150 LOG("%s(%s) called.\n", __func__, s);
152 WvConstStringBuffer buf(s);
154 int startofs = out.used();
155 while (buf.used())
157 out.putstr(wvtcl_getword(buf));
158 if (buf.used())
159 out.putstr(" ");
161 out.putch(0);
163 LOG("Putmatch: %s\n", (char *)out.mutablepeek(startofs, out.used() - (unsigned)startofs));
165 return (char *)out.mutablepeek(startofs, out.used() - (unsigned)startofs);
168 static char **putmatches(WvBuf &out, WvStringParm s)
170 LOG("%s(%s) called.\n", __func__, s);
172 WvStringList users;
173 users.split(s, " ");
175 if (0 == users.count())
176 return NULL;
178 int startofl = out.used();
179 int currptr = startofl;
181 // an extra ptr for NULL at the end
182 size_t ptrsize = (users.count() + 1)*sizeof(char *);
183 char *ptrs = new char[ptrsize];
185 memset(ptrs, 0, ptrsize);
186 out.put(ptrs, ptrsize);
188 deletev ptrs;
190 // dump the strings into the buffer
191 WvStringList::Iter i(users);
192 for (i.rewind(); i.next(); )
194 char *bufstr = putmatch(out, i());
195 out.poke(&bufstr, currptr, sizeof(char *));
196 currptr += sizeof(char *);
199 return (char **)out.mutablepeek(startofl, out.used() - (unsigned)startofl);
203 static enum nss_status pwmatch(struct passwd *pw, int *errnop,
204 char *buf, size_t buflen,
205 WvStringParm username)
207 LOG("%s(%s) called.\n", __func__, username);
209 WvInPlaceBuf out(buf, 0, buflen);
211 WvString homedir("/export/home/");
212 if (!!username)
213 homedir.append(username);
214 else
215 homedir.append("nobody");
216 #if 0
217 WvString shell;
218 // This call doesn't work if anonymous...
219 if (uniconf.cfg["users"].getme() == "NOLOGIN")
220 shell = "/bin/false";
221 else
223 shell = uniconf.shells[username].getme();
224 if (!shell)
225 shell = "/bin/bash";
227 #endif
228 WvString shell = "/bin/bash";
229 WvString gecos(uniconf.fnames[username].getme());
230 if (!gecos)
231 gecos = username;
233 pw->pw_name = putmatch(out, username);
234 pw->pw_passwd = putmatch(out, "x");
235 #ifndef NOTNITIX
236 if (uniconf.admins[username].getmeint(0))
238 pw->pw_uid = 0;
239 pw->pw_gid = 0;
240 pw->pw_dir = putmatch(out, "/root");
242 else
244 #endif
245 pw->pw_uid = uniconf.uids[username].getmeint(65534);
246 pw->pw_gid = uniconf.uids[username].getmeint(65534);
247 pw->pw_dir = putmatch(out, homedir);
248 #ifndef NOTNITIX
250 #endif
251 pw->pw_gecos = putmatch(out, gecos);
252 pw->pw_shell = putmatch(out, shell);
254 if (out.free() == 0)
256 *errnop = errno = ERANGE;
257 return NSS_STATUS_TRYAGAIN;
260 *errnop = errno = 0;
261 LOG("Success! (apparently)\n");
262 return NSS_STATUS_SUCCESS;
266 static enum nss_status grmatch(struct group *gr, int *errnop,
267 char *buf, size_t buflen,
268 WvStringParm username)
270 LOG("%s(%s) called.\n", __func__, username);
272 WvInPlaceBuf out(buf, 0, buflen);
274 WvString members = uniconf.groups[username].getme();
275 if (!members)
276 members = username;
278 gr->gr_name = putmatch(out, username);
279 gr->gr_passwd = putmatch(out, "x");
280 gr->gr_gid = uniconf.uids[username].getmeint(65534);
281 gr->gr_mem = putmatches(out, members);
283 if (out.free() == 0)
285 *errnop = errno = ERANGE;
286 return NSS_STATUS_TRYAGAIN;
289 *errnop = errno = 0;
290 return NSS_STATUS_SUCCESS;
293 // Shadow Password...
294 static enum nss_status spmatch(struct spwd *sp, int *errnop,
295 char *buf, size_t buflen,
296 WvStringParm username)
298 LOG("%s(%s) called.\n", __func__, username);
299 WvInPlaceBuf out(buf, 0, buflen);
300 time_t now = time(NULL);
301 long lastchg = (now / (60*60*24)) - 1;
303 sp->sp_namp = putmatch(out, username);
304 sp->sp_pwdp = NULL;
305 sp->sp_lstchg = lastchg;
306 sp->sp_min = 0;
307 sp->sp_max = LONG_MAX;
308 sp->sp_warn = 10;
309 sp->sp_inact = LONG_MAX;
310 sp->sp_expire = LONG_MAX;
312 if (out.free() == 0)
314 *errnop = errno = ERANGE;
315 return NSS_STATUS_TRYAGAIN;
318 *errnop = errno = 0;
319 return NSS_STATUS_SUCCESS;
323 extern "C" enum nss_status _nss_uniconf_getpwnam_r(
324 const char *user, struct passwd *pw,
325 char *buf, size_t buflen, int *errnop)
327 MY_INIT;
328 LOG("- with params: user:%s\n", user);
330 if (user == NULL)
332 *errnop = EINVAL;
333 return NSS_STATUS_UNAVAIL;
336 enum nss_status ret = NSS_STATUS_NOTFOUND;
337 *errnop = errno = ENOENT;
339 if (uniconf.uids[user].exists())
340 ret = pwmatch(pw, errnop, buf, buflen, user);
341 else
343 buf = NULL;
344 pw = NULL;
347 MY_RETURN(ret);
351 extern "C" enum nss_status _nss_uniconf_getpwuid_r(
352 uid_t uid, struct passwd *pw,
353 char *buf, size_t buflen, int *errnop)
355 MY_INIT;
357 enum nss_status ret = NSS_STATUS_NOTFOUND;
358 *errnop = errno = ENOENT;
360 LOG("Iterating\n");
361 UniConf::Iter i(uniconf.uids);
362 for (i.rewind(); i.next(); )
364 LOG("%s considering uid %s\n", __func__, i._value().num());
365 if (uid == (unsigned)i._value().num())
367 LOG("Got a match for '%s'\n", uid);
368 WvString username = i().key();
369 LOG("And I think it is: %s\n", username);
370 ret = pwmatch(pw, errnop, buf, buflen, username);
371 break;
374 LOG("Returning\n");
376 MY_RETURN(ret);
381 extern "C" enum nss_status _nss_uniconf_getgrnam_r(
382 const char *user, struct group *gr,
383 char *buf, size_t buflen, int *errnop)
385 MY_INIT;
386 LOG("- with params: user:%s\n", user);
388 enum nss_status ret = NSS_STATUS_NOTFOUND;
389 *errnop = errno = ENOENT;
391 if (uniconf.uids[user].exists())
392 ret = grmatch(gr, errnop, buf, buflen, user);
394 MY_RETURN(ret);
398 extern "C" enum nss_status _nss_uniconf_getgrgid_r(
399 gid_t gid, struct group *gr,
400 char *buf, size_t buflen, int *errnop)
402 MY_INIT;
403 LOG("- with params: gid:%s\n", gid);
405 enum nss_status ret = NSS_STATUS_NOTFOUND;
406 *errnop = errno = ENOENT;
408 UniConf::Iter i(uniconf.uids);
409 for (i.rewind(); i.next(); )
411 if (gid == (unsigned)i._value().num())
413 LOG("Got a match for: %s\n", gid);
414 WvString username = i().key();
415 LOG("It is %s\n", username);
416 ret = grmatch(gr, errnop, buf, buflen, username);
417 break;
421 MY_RETURN(ret);
424 extern "C" enum nss_status _nss_uniconf_getspnam_r(
425 const char *user, struct spwd *sp,
426 char *buf, size_t buflen, int *errnop)
428 MY_INIT;
429 LOG("- with params: user:%s\n", user);
431 enum nss_status ret = NSS_STATUS_NOTFOUND;
432 *errnop = errno = ENOENT;
434 if (user && uniconf.uids[user].exists())
436 LOG("Found '%s'\n", user);
437 ret = spmatch(sp, errnop, buf, buflen, user);
440 MY_RETURN(ret);
444 extern "C" enum nss_status _nss_uniconf_getspuid_r(
445 uid_t uid, struct spwd *sp,
446 char *buf, size_t buflen, int *errnop)
448 MY_INIT;
449 LOG("- with params: uid:%s\n", uid);
451 WvString username;
452 enum nss_status ret = NSS_STATUS_NOTFOUND;
453 *errnop = errno = ENOENT;
455 UniConf::Iter i(uniconf.uids);
456 for (i.rewind(); i.next(); )
458 if (uid == (unsigned)i._value().num())
460 username = i().key();
461 ret = spmatch(sp, errnop, buf, buflen, username);
462 break;
466 MY_RETURN(ret);
469 extern "C" enum nss_status _nss_uniconf_setpwent()
471 MY_INIT;
472 LOG("UNIMPLEMENTED %s() called.\n", __func__);
474 MY_RETURN(NSS_STATUS_SUCCESS);
477 extern "C" enum nss_status _nss_uniconf_setgrent()
479 MY_INIT;
480 LOG("UNIMPLEMENTED %s() called.\n", __func__);
482 MY_RETURN(NSS_STATUS_SUCCESS);
485 extern "C" enum nss_status _nss_uniconf_endpwent()
487 MY_INIT;
488 LOG("UNIMPLEMENTED %s() called.\n", __func__);
490 MY_RETURN(NSS_STATUS_SUCCESS);
493 extern "C" enum nss_status _nss_uniconf_endgrent()
495 MY_INIT;
496 LOG("UNIMPLEMENTED %s() called.\n", __func__);
498 MY_RETURN(NSS_STATUS_SUCCESS);
501 extern "C" enum nss_status _nss_uniconf_setspent()
503 MY_INIT;
504 LOG("UNIMPLEMENTED %s() called.\n", __func__);
506 MY_RETURN(NSS_STATUS_SUCCESS);
509 extern "C" enum nss_status _nss_uniconf_endspent()
511 MY_INIT;
512 LOG("UNIMPLEMENTED %s() called.\n", __func__);
514 MY_RETURN(NSS_STATUS_SUCCESS);
518 // FIXME: this is unlikely to work at all on a read-only mount
519 extern "C" enum nss_status _nss_uniconf_putpwent (
520 struct passwd *p, FILE *f)
522 MY_INIT;
524 uniconf.cfg["Users"].xset(p->pw_name, p->pw_passwd);
525 uniconf.uids.xset(p->pw_name, p->pw_uid);
526 uniconf.fnames.xset(p->pw_name, p->pw_gecos);
527 #if 0
528 uniconf.shells.xset(p->pw_name, p->pw_shell);
529 #endif
531 MY_RETURN(NSS_STATUS_SUCCESS);
534 extern "C" enum nss_status _nss_uniconf_getpwent_r(
535 struct passwd *pw, char *buf,
536 int buflen, int *errnop)
538 MY_INIT;
540 UniConf::Iter &i = uniconf.get_uiter();
542 if (i.next())
544 WvString username = i().key();
545 MY_RETURN(_nss_uniconf_getpwnam_r(username, pw,
546 buf, buflen, errnop));
548 else
549 uniconf.release_uiter();
551 errno = *errnop = ENOENT;
552 MY_RETURN(NSS_STATUS_NOTFOUND);
555 extern "C" enum nss_status _nss_uniconf_getgrent_r(
556 struct group *gr, char *buf,
557 int buflen, int *errnop)
559 MY_INIT;
561 UniConf::Iter &i = uniconf.get_uiter();
563 if (i.next())
565 WvString username = i().key();
566 MY_RETURN(_nss_uniconf_getgrnam_r(username, gr,
567 buf, buflen, errnop));
569 else
570 uniconf.release_uiter();
572 errno = *errnop = ENOENT;
573 MY_RETURN(NSS_STATUS_NOTFOUND);
576 extern "C" enum nss_status _nss_uniconf_getspent_r(
577 struct spwd *sp, char *buf,
578 int buflen, int *errnop)
580 MY_INIT;
582 UniConf::Iter &i = uniconf.get_uiter();
584 if (i.next())
586 WvString username = i().key();
587 MY_RETURN(_nss_uniconf_getspnam_r(username, sp,
588 buf, buflen, errnop));
590 else
591 uniconf.release_uiter();
593 errno = *errnop = ENOENT;
594 MY_RETURN(NSS_STATUS_NOTFOUND);
597 extern "C" struct passwd *_nss_uniconf_getpwent(void)
599 MY_INIT;
601 struct passwd *ret = (struct passwd *)malloc(sizeof(struct passwd));
602 if (!ret)
603 MY_RETURN(NULL);
605 int errnum = 0;
606 size_t bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
607 UniConf::Iter &i = uniconf.get_uiter();
609 if (i.next())
611 char *buf = (char *)malloc(bufsize);
612 if (!buf)
613 goto fail;
615 WvString username = i().key();
617 // don't double-lock
618 enum nss_status status =
619 _nss_uniconf_getpwnam_r(username, ret,
620 buf, bufsize, &errnum);
622 if (NSS_STATUS_SUCCESS == status)
623 MY_RETURN(ret);
624 else
625 free(buf);
627 else
628 uniconf.release_uiter();
630 fail:
631 free(ret);
633 MY_RETURN(NULL);
636 extern "C" struct group *_nss_uniconf_getgrent(void)
638 MY_INIT;
640 struct group *ret = (struct group *)malloc(sizeof(struct group));
641 if (!ret)
642 MY_RETURN(NULL);
644 int errnum = 0;
645 size_t bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
646 UniConf::Iter &i = uniconf.get_giter();
648 if (i.next())
650 char *buf = (char *)malloc(bufsize);
651 if (!buf)
652 goto fail;
654 WvString username = i().key();
656 // don't double-lock
657 enum nss_status status =
658 _nss_uniconf_getgrnam_r(username, ret,
659 buf, bufsize, &errnum);
661 if (NSS_STATUS_SUCCESS == status)
662 MY_RETURN(ret);
663 else
664 free(buf);
666 else
667 uniconf.release_giter();
669 fail:
670 free(ret);
672 MY_RETURN(NULL);
675 extern "C" struct spwd *_nss_uniconf_getspent(void)
677 MY_INIT;
679 struct spwd *ret = (struct spwd *)malloc(sizeof(struct spwd));
680 if (!ret)
681 MY_RETURN(NULL);
683 int errnum = 0;
684 size_t bufsize = UT_NAMESIZE;
685 UniConf::Iter &i = uniconf.get_uiter();
687 if (i.next())
689 char *buf = (char *)malloc(bufsize);
690 if (!buf)
691 goto fail;
693 WvString username = i().key();
695 // don't double-lock
696 enum nss_status status =
697 _nss_uniconf_getspnam_r(username, ret,
698 buf, bufsize, &errnum);
700 if (NSS_STATUS_SUCCESS == status)
701 MY_RETURN(ret);
702 else
703 free(buf);
705 else
706 uniconf.release_uiter();
708 fail:
709 free(ret);
711 MY_RETURN(NULL);
714 extern "C" enum nss_status _nss_uniconf_initgroups_dyn(
715 const char *user, gid_t group, long int *start, long int *size,
716 gid_t **groupsp, long int limit, int *errnop)
718 MY_INIT;
719 LOG("- with params: user:%s\n", user);
721 if (!user || !uniconf.uids[user].exists())
723 errno = *errnop = ENOENT;
724 LOG("I can't find that user!\n");
725 MY_RETURN(NSS_STATUS_NOTFOUND);
728 if (*start > *size)
730 errno = *errnop = EINVAL;
731 MY_RETURN(NSS_STATUS_UNAVAIL);
734 gid_t curr = 65534;
735 int nmem = 0;
736 WvString members;
737 WvStringList mlist;
739 UniConf::Iter i(uniconf.groups);
740 for (i.rewind(); i.next(); )
742 members = i._value();
743 mlist.split(members, " ");
744 curr = uniconf.uids[i().key()].getmeint();
745 nmem = mlist.count();
747 if (curr == group || !nmem)
748 continue;
750 while (nmem)
752 if (mlist.popstr() == user)
754 LOG("I think %s is in group %s.\n", user, curr);
755 if (!putgroup(curr, groupsp, start, size, limit))
757 errno = *errnop = ENOMEM;
758 MY_RETURN(NSS_STATUS_TRYAGAIN);
761 break;
764 nmem--;
768 errno = *errnop = 0;
769 MY_RETURN(NSS_STATUS_SUCCESS);