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>
59 #include <afs/afsutil.h>
60 #include <afs/cellconfig.h>
61 #include <afs/com_err.h>
62 #include <afs/param.h>
63 #include <afs/ptclient.h>
64 #include <afs/pterror.h>
65 #include <afs/ptuser.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;
99 * The cpstr() function copies a null-terminated string from str*
100 * (the first argument) into buf and updates both buf and buflen. If
101 * the string would overflow the buffer, no action is taken. The
102 * number of bytes copied is returned (zero indicates failure).
104 int cpstr( char *str
, char **buf
, size_t *buflen
) {
105 int len
= strlen(str
);
106 if ( len
>= *buflen
-1 ) return 0;
114 * Look up the name corresponding to uid, store in buffer.
116 enum nss_status
ptsid2name(int uid
, char **buffer
, size_t *buflen
) {
121 if (init_afs()) return NSS_STATUS_UNAVAIL
;
123 if (uid
==AFS_MAGIC_ANONYMOUS_USERID
) {
124 if (!cpstr("anonymous", buffer
, buflen
)) return NSS_STATUS_UNAVAIL
;
125 return NSS_STATUS_SUCCESS
;
128 if (pthread_mutex_lock(&mutex
)) return NSS_STATUS_UNAVAIL
;
130 lid
.idlist_val
= (afs_int32
*)&uid
;
132 lnames
.namelist_val
= 0;
133 lnames
.namelist_len
= 0;
135 if (ubik_PR_IDToName(pruclient
,0,&lid
,&lnames
) != PRSUCCESS
) {
136 perror("ubik_Call() in ptsid2name() failed\n");
137 pthread_mutex_unlock(&mutex
);
138 return NSS_STATUS_UNAVAIL
;
141 ret
= NSS_STATUS_NOTFOUND
;
142 for (i
=0;i
<lnames
.namelist_len
;i
++) {
143 int delta
= strlen(lnames
.namelist_val
[i
]);
144 if ( (delta
< *buflen
) && islower(*(lnames
.namelist_val
[i
])) ) {
145 cpstr(lnames
.namelist_val
[i
], buffer
, buflen
);
146 ret
= NSS_STATUS_SUCCESS
;
149 free(lnames
.namelist_val
);
150 /* free(lid.idlist_val); */
154 pthread_mutex_unlock(&mutex
);
159 * Look up the uid corresponding to name in ptserver.
161 enum nss_status
ptsname2id(char *name
, uid_t
* uid
) {
165 char uname
[MAXUSERNAMELEN
];
167 if (init_afs()) return NSS_STATUS_UNAVAIL
;
169 if (!strcmp(name
,"anonymous")) {
170 *uid
= AFS_MAGIC_ANONYMOUS_USERID
;
171 return NSS_STATUS_SUCCESS
;
174 if (pthread_mutex_lock(&mutex
)) return NSS_STATUS_UNAVAIL
;
178 lnames
.namelist_val
= (prname
*)uname
;
179 // apparently ubik expects to be able to modify this?
180 strncpy(uname
, name
, MAXUSERNAMELEN
);
181 lnames
.namelist_len
= 1;
183 if (ubik_PR_NameToID(pruclient
,0,&lnames
,&lid
) != PRSUCCESS
) {
184 perror("ubik_Call() in ptsname2id() failed\n");
185 pthread_mutex_unlock(&mutex
);
186 return NSS_STATUS_UNAVAIL
;
188 pthread_mutex_unlock(&mutex
);
190 res
= (uid_t
)lid
.idlist_val
[0];
191 if (res
== AFS_MAGIC_ANONYMOUS_USERID
) return NSS_STATUS_NOTFOUND
;
193 return NSS_STATUS_SUCCESS
;
197 * Initialize the library; returns zero on success
208 if (afs_initialized
) {
209 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
210 if (stat(cell_root
, &statbuf
)) return -1;
214 // check to make sure that we are running inside nscd
216 fd
= open("/proc/self/cmdline", O_RDONLY
);
217 if (fd
==-1) return -1;
220 numread
= read(fd
, buf
+pos
, 1);
221 if (buf
[ (pos
+5)%6 ] == 'd' &&
222 buf
[ (pos
+4)%6 ] == 'c' &&
223 buf
[ (pos
+3)%6 ] == 's' &&
224 buf
[ (pos
+2)%6 ] == 'n' &&
225 (buf
[(pos
+1)%6 ] == '/' || pos
==4) &&
226 (buf
[(pos
+0)%6 ] == 0 || numread
==-1)
230 if (numread
==0) { close(fd
); return -1; }
234 if (pthread_mutex_lock(&mutex
)) return -1;
236 homedirs_method
=HOMEDIR_PREFIX
;
237 shells_method
=SHELL_USERLINK
;
239 len
= snprintf(cellname
, MAXCELLNAMELEN
,
240 "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH
);
241 if (len
< 0 || len
>= MAXCELLNAMELEN
) return -1;
243 thiscell
=fopen(cellname
,"r");
244 if (thiscell
== NULL
) break;
245 len
=fread(cellname
,1,MAXCELLNAMELEN
,thiscell
);
246 if (!feof(thiscell
)) {
249 strcpy(homedir_prefix
,"/tmp/\0");
250 homedir_prefix_len
=5;
255 if (cellname
[len
-1] == '\n') len
--;
258 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
259 sprintf(cell_root
,"/afs/%s/",cellname
);
260 if (stat(cell_root
, &statbuf
)) break;
262 sprintf(homedir_prefix
,"/afs/%s/user/",cellname
);
263 homedir_prefix_len
=strlen(homedir_prefix
);
265 /* time out requests after 5 seconds to avoid hanging things */
268 if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH
, 0)) {
269 perror("pr_Initialize() failed\n");
274 pthread_mutex_unlock(&mutex
);
278 pthread_mutex_unlock(&mutex
);
284 * Retrieves the homedir for a given user; returns 0 on success.
286 int get_homedir(char *name
, char **buffer
, size_t *buflen
) {
291 switch (homedirs_method
) {
293 homedir_prefix
[homedir_prefix_len
+0]=name
[0];
294 homedir_prefix
[homedir_prefix_len
+1]='/';
295 homedir_prefix
[homedir_prefix_len
+2]=name
[0];
296 homedir_prefix
[homedir_prefix_len
+3]=name
[1];
297 homedir_prefix
[homedir_prefix_len
+4]='/';
298 homedir_prefix
[homedir_prefix_len
+5]=0;
299 strncpy(&homedir_prefix
[homedir_prefix_len
+5],name
,40);
300 if (! cpstr(homedir_prefix
,buffer
,buflen
) ) return -1;
303 homedir_prefix
[homedir_prefix_len
]=0;
304 strncpy(&homedir_prefix
[homedir_prefix_len
],name
,40);
305 if (! cpstr(homedir_prefix
,buffer
,buflen
) ) return -1;
307 case HOMEDIR_ADMINLINK
:
308 if ( snprintf(buf
,256,"/afs/%s/admin/homedirs/%s",cellname
,name
) > 0 ) {
309 temp
=readlink(buf
,*buffer
,*buflen
);
312 *buflen
= *buflen
- temp
- 1;
316 if (! cpstr("/tmp",buffer
,buflen
) ) return -1;
323 * Retrieves the shell for a given user; returns 0 on success.
325 int get_shell(char *name
, char **buffer
, size_t *buflen
) {
330 size_t bufxlen
= 256;
333 switch (shells_method
) {
337 case SHELL_ADMINLINK
:
338 if (snprintf(buf
,256,"/afs/%s/admin/shells/%s",cellname
,name
)<=0) break;
339 temp
= readlink(buf
,*buffer
,*buflen
);
342 *buflen
= *buflen
- temp
- 1;
346 if (get_homedir(name
, &bufx
, &bufxlen
)) break;
347 if (strncpy(buf
+strlen(buf
),"/.loginshell",bufxlen
)<=0) break;
348 temp
= readlink(buf
,*buffer
,*buflen
);
351 *buflen
= *buflen
- temp
- 1;
354 if (! cpstr("/bin/bash",buffer
,buflen
) )
361 * This function is exported; glibc will invoke it in order to find
362 * the name and list of members of a group specified by a numerical
365 enum nss_status
_nss_afs_getgrgid_r (gid_t gid
,
366 struct group
*result
,
372 if (gid
>= MIN_PAG_GID
&& gid
<= MAX_PAG_GID
) {
373 showgid
= gid
-MIN_PAG_GID
;
374 } else if (gid
>= MIN_OLDPAG_GID
&& gid
<= MAX_OLDPAG_GID
) {
375 showgid
= gid
-MIN_OLDPAG_GID
;
378 return NSS_STATUS_NOTFOUND
;
383 result
->gr_name
=buffer
;
384 length
=snprintf(buffer
,buflen
,"AfsPag-%x",showgid
);
386 if (length
< 0) break;
391 result
->gr_passwd
=buffer
;
393 if (!cpstr("z",&buffer
,&buflen
)) break;
395 if (buflen
< sizeof(char*)) break;
396 result
->gr_mem
=&buffer
;
397 result
->gr_mem
[0] = NULL
;
400 return NSS_STATUS_SUCCESS
;
404 return NSS_STATUS_UNAVAIL
;
408 * A helper function to fill in the fields of "struct passwd"; used by
409 * both _nss_afs_getpwuid_r() and _nss_afs_getpwnam_r().
411 enum nss_status
fill_result_buf(uid_t uid
,
413 struct passwd
*result_buf
,
417 result_buf
->pw_name
= name
;
419 /* set the password to "z"; we can't use "x" because of pam_unix.so */
420 result_buf
->pw_passwd
= buffer
;
421 if ( ! cpstr("z",&buffer
, &buflen
) ) break;
423 /* the uid and gid are both the uid passed in */
424 result_buf
->pw_uid
= uid
;
425 result_buf
->pw_gid
= 65534;
427 /* make the gecos the same as the PTS name */
428 result_buf
->pw_gecos
= buffer
;
429 if ( ! cpstr(result_buf
->pw_name
, &buffer
, &buflen
) ) break;
431 // Set the homedirectory
432 result_buf
->pw_dir
= buffer
;
433 if ( get_homedir(result_buf
->pw_name
,&buffer
,&buflen
) ) break;
435 // Set the login shell
436 result_buf
->pw_shell
= buffer
;
437 if ( get_shell(result_buf
->pw_name
,&buffer
,&buflen
) ) break;
439 #ifdef LIMIT_USERNAME_CHARS
440 if ( strlen(result_buf
->pw_name
) > LIMIT_USERNAME_CHARS
) {
441 result_buf
->pw_name
[LIMIT_USERNAME_CHARS
] = '\0';
442 buflen
= buflen
+ ( buffer
- &result_buf
->pw_name
[LIMIT_USERNAME_CHARS
+1] );
443 buffer
= &result_buf
->pw_name
[LIMIT_USERNAME_CHARS
+1];
448 return NSS_STATUS_SUCCESS
;
452 return NSS_STATUS_UNAVAIL
;
457 * This function is exported; glibc will invoke it in order to gather
458 * the user information (userid, homedir, shell) associated with a
461 enum nss_status
_nss_afs_getpwuid_r (uid_t uid
,
462 struct passwd
*result_buf
,
469 if (init_afs()) return NSS_STATUS_UNAVAIL
;
472 temp
= ptsid2name( uid
, &buffer
, &buflen
);
473 if (temp
!= NSS_STATUS_SUCCESS
) {
478 return fill_result_buf(uid
, name
, result_buf
, buffer
, buflen
, errnop
);
482 * This function is exported; glibc will invoke it in order to gather
483 * the user information (userid, homedir, shell) associated with a
486 enum nss_status
_nss_afs_getpwnam_r (char *name
,
487 struct passwd
*result_buf
,
494 if (init_afs()) return NSS_STATUS_UNAVAIL
;
496 temp
= ptsname2id(name
,&uid
);
497 if (temp
!= NSS_STATUS_SUCCESS
) {
502 return fill_result_buf(uid
, name
, result_buf
, buffer
, buflen
, errnop
);