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"
21 #include "wvlogfile.h"
22 #include "wvtclstring.h"
31 #include <sys/types.h>
35 #define MY_RETURN(x) do \
37 pthread_mutex_unlock(&nssunilock); \
38 LOG("%s() returning.\n", __func__); \
43 pthread_mutex_lock(&nssunilock); \
44 LOG("%s() executing.\n", __func__); \
47 pthread_mutex_t nssunilock
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
51 #define LOG(info...) log(info)
52 static WvLog
log("nss-uniconf", WvLog::Debug4
);
53 static WvLogFile
logger("/tmp/nss-uniconf", WvLog::Debug5
);
55 #define LOG(info...) ;
56 static WvLogFileBase
logger("/dev/null", WvLog::Debug5
);
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"];
74 groups
= cfg
["Groups"];
75 admins
= cfg
["Admins"];
79 LOG("Program that uses us closing, cleaning up..\n");
84 UniConf::Iter
& get_uiter()
88 uiter
= new UniConf::Iter(uids
);
93 void release_uiter() { delete uiter
; uiter
= NULL
; }
95 UniConf::Iter
& get_giter()
99 giter
= new UniConf::Iter(groups
);
104 void release_giter() { delete giter
; giter
= NULL
; }
107 UniConf cfg
, fnames
, uids
, groups
, admins
;
110 UniConf::Iter
*uiter
;
111 UniConf::Iter
*giter
;
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");
126 (gid_t
*)realloc((*groupsp
), (2 * (*size
) + 1) * sizeof(gid_t
));
130 LOG("Failed to reallocate.\n");
134 *groupsp
= newgroups
;
135 *size
= 2 * (*size
) + 1;
140 (*groupsp
)[*start
] = newgrp
;
144 LOG("Successfully added group %s to the list.\n", newgrp
);
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();
157 out
.putstr(wvtcl_getword(buf
));
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
);
175 if (0 == users
.count())
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
);
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/");
213 homedir
.append(username
);
215 homedir
.append("nobody");
218 // This call doesn't work if anonymous...
219 if (uniconf
.cfg
["users"].getme() == "NOLOGIN")
220 shell
= "/bin/false";
223 shell
= uniconf
.shells
[username
].getme();
228 WvString shell
= "/bin/bash";
229 WvString
gecos(uniconf
.fnames
[username
].getme());
233 pw
->pw_name
= putmatch(out
, username
);
234 pw
->pw_passwd
= putmatch(out
, "x");
236 if (uniconf
.admins
[username
].getmeint(0))
240 pw
->pw_dir
= putmatch(out
, "/root");
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
);
251 pw
->pw_gecos
= putmatch(out
, gecos
);
252 pw
->pw_shell
= putmatch(out
, shell
);
256 *errnop
= errno
= ERANGE
;
257 return NSS_STATUS_TRYAGAIN
;
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();
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
);
285 *errnop
= errno
= ERANGE
;
286 return NSS_STATUS_TRYAGAIN
;
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
);
305 sp
->sp_lstchg
= lastchg
;
307 sp
->sp_max
= LONG_MAX
;
309 sp
->sp_inact
= LONG_MAX
;
310 sp
->sp_expire
= LONG_MAX
;
314 *errnop
= errno
= ERANGE
;
315 return NSS_STATUS_TRYAGAIN
;
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
)
328 LOG("- with params: user:%s\n", user
);
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
);
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
)
357 enum nss_status ret
= NSS_STATUS_NOTFOUND
;
358 *errnop
= errno
= ENOENT
;
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
);
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
)
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
);
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
)
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
);
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
)
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
);
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
)
449 LOG("- with params: uid:%s\n", uid
);
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
);
469 extern "C" enum nss_status
_nss_uniconf_setpwent()
472 LOG("UNIMPLEMENTED %s() called.\n", __func__
);
474 MY_RETURN(NSS_STATUS_SUCCESS
);
477 extern "C" enum nss_status
_nss_uniconf_setgrent()
480 LOG("UNIMPLEMENTED %s() called.\n", __func__
);
482 MY_RETURN(NSS_STATUS_SUCCESS
);
485 extern "C" enum nss_status
_nss_uniconf_endpwent()
488 LOG("UNIMPLEMENTED %s() called.\n", __func__
);
490 MY_RETURN(NSS_STATUS_SUCCESS
);
493 extern "C" enum nss_status
_nss_uniconf_endgrent()
496 LOG("UNIMPLEMENTED %s() called.\n", __func__
);
498 MY_RETURN(NSS_STATUS_SUCCESS
);
501 extern "C" enum nss_status
_nss_uniconf_setspent()
504 LOG("UNIMPLEMENTED %s() called.\n", __func__
);
506 MY_RETURN(NSS_STATUS_SUCCESS
);
509 extern "C" enum nss_status
_nss_uniconf_endspent()
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
)
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
);
528 uniconf
.shells
.xset(p
->pw_name
, p
->pw_shell
);
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
)
540 UniConf::Iter
&i
= uniconf
.get_uiter();
544 WvString username
= i().key();
545 MY_RETURN(_nss_uniconf_getpwnam_r(username
, pw
,
546 buf
, buflen
, errnop
));
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
)
561 UniConf::Iter
&i
= uniconf
.get_uiter();
565 WvString username
= i().key();
566 MY_RETURN(_nss_uniconf_getgrnam_r(username
, gr
,
567 buf
, buflen
, errnop
));
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
)
582 UniConf::Iter
&i
= uniconf
.get_uiter();
586 WvString username
= i().key();
587 MY_RETURN(_nss_uniconf_getspnam_r(username
, sp
,
588 buf
, buflen
, errnop
));
591 uniconf
.release_uiter();
593 errno
= *errnop
= ENOENT
;
594 MY_RETURN(NSS_STATUS_NOTFOUND
);
597 extern "C" struct passwd
*_nss_uniconf_getpwent(void)
601 struct passwd
*ret
= (struct passwd
*)malloc(sizeof(struct passwd
));
606 size_t bufsize
= sysconf(_SC_GETPW_R_SIZE_MAX
);
607 UniConf::Iter
&i
= uniconf
.get_uiter();
611 char *buf
= (char *)malloc(bufsize
);
615 WvString username
= i().key();
618 enum nss_status status
=
619 _nss_uniconf_getpwnam_r(username
, ret
,
620 buf
, bufsize
, &errnum
);
622 if (NSS_STATUS_SUCCESS
== status
)
628 uniconf
.release_uiter();
636 extern "C" struct group
*_nss_uniconf_getgrent(void)
640 struct group
*ret
= (struct group
*)malloc(sizeof(struct group
));
645 size_t bufsize
= sysconf(_SC_GETGR_R_SIZE_MAX
);
646 UniConf::Iter
&i
= uniconf
.get_giter();
650 char *buf
= (char *)malloc(bufsize
);
654 WvString username
= i().key();
657 enum nss_status status
=
658 _nss_uniconf_getgrnam_r(username
, ret
,
659 buf
, bufsize
, &errnum
);
661 if (NSS_STATUS_SUCCESS
== status
)
667 uniconf
.release_giter();
675 extern "C" struct spwd
*_nss_uniconf_getspent(void)
679 struct spwd
*ret
= (struct spwd
*)malloc(sizeof(struct spwd
));
684 size_t bufsize
= UT_NAMESIZE
;
685 UniConf::Iter
&i
= uniconf
.get_uiter();
689 char *buf
= (char *)malloc(bufsize
);
693 WvString username
= i().key();
696 enum nss_status status
=
697 _nss_uniconf_getspnam_r(username
, ret
,
698 buf
, bufsize
, &errnum
);
700 if (NSS_STATUS_SUCCESS
== status
)
706 uniconf
.release_uiter();
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
)
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
);
730 errno
= *errnop
= EINVAL
;
731 MY_RETURN(NSS_STATUS_UNAVAIL
);
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
)
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
);
769 MY_RETURN(NSS_STATUS_SUCCESS
);