2 /*****************************************************************************
3 * libnss-afs (nss_afs.c)
5 * Copyright 2008, licensed under GNU Library General Public License (LGPL)
6 * see COPYING file for details
8 * by Adam Megacz <megacz@hcoop.net>
9 * derived from Frank Burkhardt's libnss_ptdb,
10 * which was derived from Todd M. Lewis' libnss_pts
11 *****************************************************************************/
14 * If you are reading this code for the first time, read the rest of
15 * this comment block, then start at the bottom of the file and work
18 * All functions which return an int use zero to signal success --
19 * except cpstr(), which returns zero on *failure*. This should be
22 * A note about memory allocation:
24 * NSS plugins generally ought to work without attempting to call
25 * malloc() (which may fail). Therefore, glibc allocates a buffer
26 * before calling NSS library functions, and passes that buffer to
27 * the NSS library; library functions store their results in the
28 * buffer and return pointers into that buffer.
30 * The convention used throughout this library is to pass around a
31 * char** which points to a pointer to the first unused byte in the
32 * provided buffer, and a size_t* which points to an int indicating
33 * how many bytes are left between the char** and the end of the
43 #include <netinet/in.h>
53 #include <sys/select.h>
54 #include <sys/socket.h>
57 #include <sys/types.h>
60 #include <afs/afsutil.h>
61 #include <afs/cellconfig.h>
62 #include <afs/com_err.h>
63 #include <afs/param.h>
64 #include <afs/ptclient.h>
65 #include <afs/pterror.h>
68 #define HOMEDIR_AUTO 0
69 #define HOMEDIR_ADMINLINK 1
70 #define HOMEDIR_PREFIX 2
72 #define SHELL_ADMINLINK 1
73 #define SHELL_USERLINK 2
75 #define AFS_MAGIC_ANONYMOUS_USERID 32766
76 #define MIN_PAG_GID 0x41000000L
77 #define MAX_PAG_GID 0x41FFFFFFL
78 #define MIN_OLDPAG_GID 0x3f00
79 #define MAX_OLDPAG_GID 0xff00
81 #define MAXCELLNAMELEN 256
82 #define MAXUSERNAMELEN 256
84 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
86 extern struct ubik_client *pruclient;
88 int afs_initialized = 0;
89 char cellname[MAXCELLNAMELEN];
90 char homedir_prefix[MAXPATHLEN];
91 char cell_root[MAXPATHLEN];
92 int homedir_prefix_len=0;
93 char homedirs_method=0;
97 * The cpstr() function copies a null-terminated string from str*
98 * (the first argument) into buf and updates both buf and buflen. If
99 * the string would overflow the buffer, no action is taken. The
100 * number of bytes copied is returned (zero indicates failure).
102 int cpstr( char *str, char **buf, size_t *buflen) {
103 int len = strlen(str);
104 if ( len >= *buflen-1 ) return 0;
112 * Look up the name corresponding to uid, store in buffer.
114 enum nss_status ptsid2name(int uid, char **buffer, int *buflen) {
121 if (uid==AFS_MAGIC_ANONYMOUS_USERID) {
122 if (!cpstr("anonymous", buffer, buflen)) return NSS_STATUS_UNAVAIL;
123 return NSS_STATUS_SUCCESS;
126 if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
128 lid.idlist_val = (afs_int32*)&uid;
130 lnames.namelist_val = 0;
131 lnames.namelist_len = 0;
133 if (ubik_Call(PR_IDToName,pruclient,0,&lid,&lnames) != PRSUCCESS) {
134 perror("ubik_Call() in ptsid2name() failed\n");
135 pthread_mutex_unlock(&mutex);
136 return NSS_STATUS_UNAVAIL;
139 ret = NSS_STATUS_NOTFOUND;
140 for (i=0;i<lnames.namelist_len;i++) {
141 int delta = strlen(lnames.namelist_val[i]);
142 if ( (delta < buflen) && islower(*(lnames.namelist_val[i])) ) {
143 cpstr(lnames.namelist_val[i], buffer, buflen);
144 ret = NSS_STATUS_SUCCESS;
147 free(lnames.namelist_val);
148 /* free(lid.idlist_val); */
152 pthread_mutex_unlock(&mutex);
157 * Look up the uid corresponding to name in ptserver.
159 enum nss_status ptsname2id(char *name, uid_t* uid) {
163 char uname[MAXUSERNAMELEN];
167 if (!strcmp(name,"anonymous")) {
168 *uid = AFS_MAGIC_ANONYMOUS_USERID;
169 return NSS_STATUS_SUCCESS;
172 if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
176 lnames.namelist_val = (prname*)uname;
177 // apparently ubik expects to be able to modify this?
178 strncpy(uname, name, MAXUSERNAMELEN);
179 lnames.namelist_len = 1;
181 if (ubik_Call(PR_NameToID,pruclient,0,&lnames,&lid) != PRSUCCESS) {
182 perror("ubik_Call() in ptsname2id() failed\n");
183 pthread_mutex_unlock(&mutex);
184 return NSS_STATUS_UNAVAIL;
186 pthread_mutex_unlock(&mutex);
188 res = (uid_t)lid.idlist_val[0];
189 if (res == AFS_MAGIC_ANONYMOUS_USERID) return NSS_STATUS_NOTFOUND;
191 return NSS_STATUS_SUCCESS;
195 * Initialize the library; returns zero on success
202 if (afs_initialized) {
203 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
204 if (stat(cell_root, &statbuf)) return -1;
208 if (pthread_mutex_lock(&mutex)) return -1;
210 homedirs_method=HOMEDIR_PREFIX;
211 shells_method=SHELL_USERLINK;
213 len = snprintf(cellname, MAXCELLNAMELEN,
214 "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH);
215 if (len < 0 || len >= MAXCELLNAMELEN) return -1;
217 thiscell=fopen(cellname,"r");
218 if (thiscell == NULL) break;
219 len=fread(cellname,1,MAXCELLNAMELEN,thiscell);
220 if (!feof(thiscell)) {
223 strcpy(homedir_prefix,"/tmp/\0");
224 homedir_prefix_len=5;
229 if (cellname[len-1] == '\n') len--;
232 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
233 sprintf(cell_root,"/afs/%s/",cellname);
234 if (stat(cell_root, &statbuf)) break;
236 sprintf(homedir_prefix,"/afs/%s/user/",cellname);
237 homedir_prefix_len=strlen(homedir_prefix);
239 /* time out requests after 5 seconds to avoid hanging things */
242 if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH, 0)) {
243 perror("pr_Initialize() failed\n");
248 pthread_mutex_unlock(&mutex);
252 pthread_mutex_unlock(&mutex);
258 * Retrieves the homedir for a given user; returns 0 on success.
260 int get_homedir(char *name, char **buffer, size_t *buflen) {
265 switch (homedirs_method) {
267 homedir_prefix[homedir_prefix_len+0]=name[0];
268 homedir_prefix[homedir_prefix_len+1]='/';
269 homedir_prefix[homedir_prefix_len+2]=name[0];
270 homedir_prefix[homedir_prefix_len+3]=name[1];
271 homedir_prefix[homedir_prefix_len+4]='/';
272 homedir_prefix[homedir_prefix_len+5]=0;
273 strncpy(&homedir_prefix[homedir_prefix_len+5],name,40);
274 if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
277 homedir_prefix[homedir_prefix_len]=0;
278 strncpy(&homedir_prefix[homedir_prefix_len],name,40);
279 if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
281 case HOMEDIR_ADMINLINK:
282 if ( snprintf(buf,256,"/afs/%s/admin/homedirs/%s",cellname,name) > 0 ) {
283 temp=readlink(buf,*buffer,*buflen);
286 *buflen = *buflen - temp - 1;
290 if (! cpstr("/tmp",buffer,buflen) ) return -1;
297 * Retrieves the shell for a given user; returns 0 on success.
299 int get_shell(char *name, char **buffer, size_t *buflen) {
307 switch (shells_method) {
311 case SHELL_ADMINLINK:
312 if (snprintf(buf,256,"/afs/%s/admin/shells/%s",cellname,name)<=0) break;
313 temp = readlink(buf,*buffer,*buflen);
316 *buflen = *buflen - temp - 1;
320 if (get_homedir(name, &bufx, &bufxlen)) break;
321 if (strncpy(buf+strlen(buf),"/.loginshell",bufxlen)<=0) break;
322 temp = readlink(buf,*buffer,*buflen);
325 *buflen = *buflen - temp - 1;
328 if (! cpstr("/bin/bash",buffer,buflen) )
335 * This function is exported; glibc will invoke it in order to find
336 * the name and list of members of a group specified by a numerical
339 enum nss_status _nss_afs_getgrgid_r (gid_t gid,
340 struct group *result,
346 if (gid >= MIN_PAG_GID && gid <= MAX_PAG_GID) {
347 showgid = gid-MIN_PAG_GID;
348 } else if (gid >= MIN_OLDPAG_GID && gid <= MAX_OLDPAG_GID) {
349 showgid = gid-MIN_OLDPAG_GID;
352 return NSS_STATUS_NOTFOUND;
357 result->gr_name=buffer;
358 length=snprintf(buffer,buflen,"AfsPag-%x",showgid);
360 if (length < 0) break;
365 result->gr_passwd=buffer;
367 if (!cpstr("x",&buffer,&buflen)) break;
369 if (buflen < sizeof(char*)) break;
370 result->gr_mem=buffer;
371 result->gr_mem[0] = NULL;
374 return NSS_STATUS_SUCCESS;
378 return NSS_STATUS_UNAVAIL;
382 * A helper function to fill in the fields of "struct passwd"; used by
383 * both _nss_afs_getpwuid_r() and _nss_afs_getpwnam_r().
385 enum nss_status fill_result_buf(uid_t uid,
387 struct passwd *result_buf,
391 result_buf->pw_name = name;
393 /* set the password to "x" */
394 result_buf->pw_passwd = buffer;
395 if ( ! cpstr("x",&buffer, &buflen) ) break;
397 /* the uid and gid are both the uid passed in */
398 result_buf->pw_uid = uid;
399 result_buf->pw_gid = 65534;
401 /* make the gecos the same as the PTS name */
402 result_buf->pw_gecos = buffer;
403 if ( ! cpstr(result_buf->pw_name, &buffer, &buflen ) ) break;
405 // Set the homedirectory
406 result_buf->pw_dir = buffer;
407 if ( get_homedir(result_buf->pw_name,&buffer,&buflen) ) break;
409 // Set the login shell
410 result_buf->pw_shell = buffer;
411 if ( get_shell(result_buf->pw_name,&buffer,&buflen) ) break;
413 #ifdef LIMIT_USERNAME_CHARS
414 if ( strlen(result_buf->pw_name) > LIMIT_USERNAME_CHARS ) {
415 result_buf->pw_name[LIMIT_USERNAME_CHARS] = '\0';
416 buflen = buflen + ( buffer - &result_buf->pw_name[LIMIT_USERNAME_CHARS+1] );
417 buffer = &result_buf->pw_name[LIMIT_USERNAME_CHARS+1];
422 return NSS_STATUS_SUCCESS;
426 return NSS_STATUS_UNAVAIL;
431 * This function is exported; glibc will invoke it in order to gather
432 * the user information (userid, homedir, shell) associated with a
435 enum nss_status _nss_afs_getpwuid_r (uid_t uid,
436 struct passwd *result_buf,
443 if (init_afs()) return NSS_STATUS_UNAVAIL;
446 temp = ptsid2name( uid, &buffer, &buflen);
447 if (temp != NSS_STATUS_SUCCESS) {
452 return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop);
456 * This function is exported; glibc will invoke it in order to gather
457 * the user information (userid, homedir, shell) associated with a
460 enum nss_status _nss_afs_getpwnam_r (char *name,
461 struct passwd *result_buf,
468 if (init_afs()) return NSS_STATUS_UNAVAIL;
470 temp = ptsname2id(name,&uid);
471 if (temp != NSS_STATUS_SUCCESS) {
476 return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop);