return NSS_STATUS_UNAVAIL if used without nscd
[hcoop/debian/libnss-afs.git] / nss_afs.c
CommitLineData
03b6b479 1
908a6ebe 2/*****************************************************************************
3 * libnss-afs (nss_afs.c)
03b6b479 4 *
5 * Copyright 2008, licensed under GNU Library General Public License (LGPL)
6 * see COPYING file for details
7 *
908a6ebe 8 * by Adam Megacz <megacz@hcoop.net>
03b6b479 9 * derived from Frank Burkhardt's libnss_ptdb,
10 * which was derived from Todd M. Lewis' libnss_pts
908a6ebe 11 *****************************************************************************/
03b6b479 12
13/*
908a6ebe 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
16 * your way upwards.
17 *
18 * All functions which return an int use zero to signal success --
19 * except cpstr(), which returns zero on *failure*. This should be
20 * fixed.
21 *
22 * A note about memory allocation:
23 *
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.
29 *
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
34 * available region.
03b6b479 35 */
36
37#include <ctype.h>
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <getopt.h>
42#include <grp.h>
43#include <netinet/in.h>
44#include <nss.h>
45#include <pthread.h>
46#include <pwd.h>
47#include <rx/rx.h>
48#include <rx/xdr.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <sys/select.h>
54#include <sys/socket.h>
43d1801e 55#include <sys/stat.h>
03b6b479 56#include <sys/time.h>
57#include <sys/types.h>
58#include <unistd.h>
59#include <afs/afs.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>
66#include <afs/stds.h>
67
908a6ebe 68#define HOMEDIR_AUTO 0
03b6b479 69#define HOMEDIR_ADMINLINK 1
908a6ebe 70#define HOMEDIR_PREFIX 2
71#define SHELL_BASH 0
72#define SHELL_ADMINLINK 1
73#define SHELL_USERLINK 2
03b6b479 74
75#define AFS_MAGIC_ANONYMOUS_USERID 32766
908a6ebe 76#define MIN_PAG_GID 0x41000000L
77#define MAX_PAG_GID 0x41FFFFFFL
78#define MIN_OLDPAG_GID 0x3f00
79#define MAX_OLDPAG_GID 0xff00
03b6b479 80
908a6ebe 81#define MAXCELLNAMELEN 256
abfd757b 82#define MAXUSERNAMELEN 256
91bb9ebc 83
03b6b479 84static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
85
03b6b479 86extern struct ubik_client *pruclient;
87
88int afs_initialized = 0;
908a6ebe 89char cellname[MAXCELLNAMELEN];
90char homedir_prefix[MAXPATHLEN];
43d1801e 91char cell_root[MAXPATHLEN];
03b6b479 92int homedir_prefix_len=0;
93char homedirs_method=0;
94char shells_method=0;
95
908a6ebe 96/**
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).
101 */
03b6b479 102int cpstr( char *str, char **buf, size_t *buflen) {
908a6ebe 103 int len = strlen(str);
104 if ( len >= *buflen-1 ) return 0;
105 strcpy(*buf,str);
106 *buflen -= len + 1;
107 *buf += len + 1;
108 return len;
03b6b479 109}
110
908a6ebe 111/**
03b6b479 112 * Look up the name corresponding to uid, store in buffer.
03b6b479 113 */
908a6ebe 114enum nss_status ptsid2name(int uid, char **buffer, int *buflen) {
03b6b479 115 int ret, i;
116 idlist lid;
117 namelist lnames;
118
b5f46b9e 119 if (init_afs()) return NSS_STATUS_UNAVAIL;
03b6b479 120
121 if (uid==AFS_MAGIC_ANONYMOUS_USERID) {
122 if (!cpstr("anonymous", buffer, buflen)) return NSS_STATUS_UNAVAIL;
123 return NSS_STATUS_SUCCESS;
124 }
125
126 if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
127
128 lid.idlist_val = (afs_int32*)&uid;
129 lid.idlist_len = 1;
130 lnames.namelist_val = 0;
131 lnames.namelist_len = 0;
132
133 if (ubik_Call(PR_IDToName,pruclient,0,&lid,&lnames) != PRSUCCESS) {
be93aa7b 134 perror("ubik_Call() in ptsid2name() failed\n");
03b6b479 135 pthread_mutex_unlock(&mutex);
136 return NSS_STATUS_UNAVAIL;
137 }
138
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;
145 }
146 }
147 free(lnames.namelist_val);
148 /* free(lid.idlist_val); */
149 lid.idlist_val = 0;
150 lid.idlist_len = 0;
151
152 pthread_mutex_unlock(&mutex);
153 return ret;
154}
155
908a6ebe 156/**
157 * Look up the uid corresponding to name in ptserver.
03b6b479 158 */
908a6ebe 159enum nss_status ptsname2id(char *name, uid_t* uid) {
03b6b479 160 int res;
161 idlist lid;
162 namelist lnames;
abfd757b 163 char uname[MAXUSERNAMELEN];
03b6b479 164
b5f46b9e 165 if (init_afs()) return NSS_STATUS_UNAVAIL;
03b6b479 166
167 if (!strcmp(name,"anonymous")) {
168 *uid = AFS_MAGIC_ANONYMOUS_USERID;
169 return NSS_STATUS_SUCCESS;
170 }
171
172 if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
173
174 lid.idlist_val = 0;
175 lid.idlist_len = 0;
be93aa7b 176 lnames.namelist_val = (prname*)uname;
abfd757b 177 // apparently ubik expects to be able to modify this?
178 strncpy(uname, name, MAXUSERNAMELEN);
03b6b479 179 lnames.namelist_len = 1;
180
181 if (ubik_Call(PR_NameToID,pruclient,0,&lnames,&lid) != PRSUCCESS) {
be93aa7b 182 perror("ubik_Call() in ptsname2id() failed\n");
03b6b479 183 pthread_mutex_unlock(&mutex);
184 return NSS_STATUS_UNAVAIL;
185 }
186 pthread_mutex_unlock(&mutex);
187
188 res = (uid_t)lid.idlist_val[0];
189 if (res == AFS_MAGIC_ANONYMOUS_USERID) return NSS_STATUS_NOTFOUND;
190 *uid = res;
191 return NSS_STATUS_SUCCESS;
192}
193
908a6ebe 194/**
195 * Initialize the library; returns zero on success
03b6b479 196 */
197int init_afs() {
198 FILE *thiscell;
199 int len;
43d1801e 200 struct stat statbuf;
03b6b479 201
b5f46b9e
AM
202 char buf[6];
203 int fd;
204 int pos;
205
43d1801e 206 if (afs_initialized) {
207 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
208 if (stat(cell_root, &statbuf)) return -1;
209 return 0;
210 }
b5f46b9e
AM
211
212 // check to make sure that we are running inside nscd
213 pos = 0;
214 fd = open("/proc/self/cmdline", O_RDONLY);
215 if (fd==-1) return -1;
216 while(1) {
217 int numread;
218 numread = read(fd, buf+pos, 1);
219 if (buf[ (pos+5)%6 ] == 'd' &&
220 buf[ (pos+4)%6 ] == 'c' &&
221 buf[ (pos+3)%6 ] == 's' &&
222 buf[ (pos+2)%6 ] == 'n' &&
223 (buf[(pos+1)%6 ] == '/' || pos==4) &&
224 (buf[(pos+0)%6 ] == 0 || numread==-1)
225 )
226 break;
227 pos = (pos+1)%6;
228 if (numread==0) { close(fd); return -1; }
229 }
230 close(fd);
43d1801e 231
03b6b479 232 if (pthread_mutex_lock(&mutex)) return -1;
233 do {
234 homedirs_method=HOMEDIR_PREFIX;
235 shells_method=SHELL_USERLINK;
91bb9ebc 236
908a6ebe 237 len = snprintf(cellname, MAXCELLNAMELEN,
238 "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH);
be93aa7b 239 if (len < 0 || len >= MAXCELLNAMELEN) return -1;
91bb9ebc 240
241 thiscell=fopen(cellname,"r");
03b6b479 242 if (thiscell == NULL) break;
908a6ebe 243 len=fread(cellname,1,MAXCELLNAMELEN,thiscell);
03b6b479 244 if (!feof(thiscell)) {
245 // Cellname too long
246 fclose(thiscell);
247 strcpy(homedir_prefix,"/tmp/\0");
248 homedir_prefix_len=5;
249 break;
250 }
251 fclose(thiscell);
91bb9ebc 252
03b6b479 253 if (cellname[len-1] == '\n') len--;
254 cellname[len]='\0';
43d1801e 255
256 /* wait until /afs/@cell/ appears as a proxy for "the network is up" */
257 sprintf(cell_root,"/afs/%s/",cellname);
258 if (stat(cell_root, &statbuf)) break;
259
03b6b479 260 sprintf(homedir_prefix,"/afs/%s/user/",cellname);
261 homedir_prefix_len=strlen(homedir_prefix);
43d1801e 262
263 /* time out requests after 5 seconds to avoid hanging things */
264 rx_SetRxDeadTime(5);
265
93d939f4 266 if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH, 0)) {
be93aa7b 267 perror("pr_Initialize() failed\n");
93d939f4 268 break;
269 }
03b6b479 270
271 afs_initialized = 1;
272 pthread_mutex_unlock(&mutex);
273 return 0;
274
275 } while(0);
276 pthread_mutex_unlock(&mutex);
277 return -1;
278}
279
280
908a6ebe 281/**
282 * Retrieves the homedir for a given user; returns 0 on success.
283 */
03b6b479 284int get_homedir(char *name, char **buffer, size_t *buflen) {
285 char buf[256];
286 int temp;
287 char *b;
288 b=*buffer;
289 switch (homedirs_method) {
290 case HOMEDIR_PREFIX:
291 homedir_prefix[homedir_prefix_len+0]=name[0];
292 homedir_prefix[homedir_prefix_len+1]='/';
293 homedir_prefix[homedir_prefix_len+2]=name[0];
294 homedir_prefix[homedir_prefix_len+3]=name[1];
295 homedir_prefix[homedir_prefix_len+4]='/';
296 homedir_prefix[homedir_prefix_len+5]=0;
297 strncpy(&homedir_prefix[homedir_prefix_len+5],name,40);
298 if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
299 break;
300 case HOMEDIR_AUTO:
301 homedir_prefix[homedir_prefix_len]=0;
302 strncpy(&homedir_prefix[homedir_prefix_len],name,40);
303 if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
304 break;
305 case HOMEDIR_ADMINLINK:
306 if ( snprintf(buf,256,"/afs/%s/admin/homedirs/%s",cellname,name) > 0 ) {
307 temp=readlink(buf,*buffer,*buflen);
308 if ( temp > -1) {
309 b[temp]=0;
310 *buflen = *buflen - temp - 1;
311 return -1;
312 }
313 }
314 if (! cpstr("/tmp",buffer,buflen) ) return -1;
315 break;
316 }
317 return 0;
318}
319
908a6ebe 320/**
321 * Retrieves the shell for a given user; returns 0 on success.
322 */
03b6b479 323int get_shell(char *name, char **buffer, size_t *buflen) {
324 char buf[256];
325 int temp;
326 char *b;
327 char* bufx = buf;
328 int bufxlen = 256;
329 b=*buffer;
330
331 switch (shells_method) {
332 case SHELL_BASH:
03b6b479 333 break;
334
335 case SHELL_ADMINLINK:
91bb9ebc 336 if (snprintf(buf,256,"/afs/%s/admin/shells/%s",cellname,name)<=0) break;
337 temp = readlink(buf,*buffer,*buflen);
338 if (temp < 0) break;
339 b[temp]=0;
340 *buflen = *buflen - temp - 1;
341 return 0;
03b6b479 342
343 case SHELL_USERLINK:
91bb9ebc 344 if (get_homedir(name, &bufx, &bufxlen)) break;
345 if (strncpy(buf+strlen(buf),"/.loginshell",bufxlen)<=0) break;
346 temp = readlink(buf,*buffer,*buflen);
347 if (temp < 0) break;
348 b[temp]=0;
349 *buflen = *buflen - temp - 1;
350 return 0;
03b6b479 351 }
91bb9ebc 352 if (! cpstr("/bin/bash",buffer,buflen) )
353 return -1;
03b6b479 354 return 0;
355}
356
357
908a6ebe 358/*
359 * This function is exported; glibc will invoke it in order to find
360 * the name and list of members of a group specified by a numerical
361 * groupid.
03b6b479 362 */
908a6ebe 363enum nss_status _nss_afs_getgrgid_r (gid_t gid,
364 struct group *result,
365 char *buffer,
366 size_t buflen,
367 int *errnop) {
03b6b479 368 int length;
582fb19e 369 int showgid = 0;
370 if (gid >= MIN_PAG_GID && gid <= MAX_PAG_GID) {
371 showgid = gid-MIN_PAG_GID;
372 } else if (gid >= MIN_OLDPAG_GID && gid <= MAX_OLDPAG_GID) {
373 showgid = gid-MIN_OLDPAG_GID;
374 } else {
03b6b479 375 *errnop=ENOENT;
376 return NSS_STATUS_NOTFOUND;
377 }
378 do {
379 result->gr_gid=gid;
380
381 result->gr_name=buffer;
582fb19e 382 length=snprintf(buffer,buflen,"AfsPag-%x",showgid);
03b6b479 383
384 if (length < 0) break;
385 length += 1;
386 buflen -= length;
387 buffer += length;
388
389 result->gr_passwd=buffer;
390
391 if (!cpstr("x",&buffer,&buflen)) break;
392
393 if (buflen < sizeof(char*)) break;
394 result->gr_mem=buffer;
395 result->gr_mem[0] = NULL;
396
397 *errnop=errno;
398 return NSS_STATUS_SUCCESS;
399
400 } while(0);
401 *errnop=ENOENT;
402 return NSS_STATUS_UNAVAIL;
403}
404
908a6ebe 405/**
406 * A helper function to fill in the fields of "struct passwd"; used by
407 * both _nss_afs_getpwuid_r() and _nss_afs_getpwnam_r().
408 */
409enum nss_status fill_result_buf(uid_t uid,
410 char* name,
411 struct passwd *result_buf,
412 char *buffer,
413 size_t buflen,
414 int *errnop) {
415 result_buf->pw_name = name;
03b6b479 416 do {
417 /* set the password to "x" */
418 result_buf->pw_passwd = buffer;
419 if ( ! cpstr("x",&buffer, &buflen) ) break;
908a6ebe 420
03b6b479 421 /* the uid and gid are both the uid passed in */
422 result_buf->pw_uid = uid;
423 result_buf->pw_gid = 65534;
908a6ebe 424
03b6b479 425 /* make the gecos the same as the PTS name */
426 result_buf->pw_gecos = buffer;
427 if ( ! cpstr(result_buf->pw_name, &buffer, &buflen ) ) break;
428
429 // Set the homedirectory
430 result_buf->pw_dir = buffer;
431 if ( get_homedir(result_buf->pw_name,&buffer,&buflen) ) break;
432
433 // Set the login shell
434 result_buf->pw_shell = buffer;
435 if ( get_shell(result_buf->pw_name,&buffer,&buflen) ) break;
436
e8c23dae 437#ifdef LIMIT_USERNAME_CHARS
438 if ( strlen(result_buf->pw_name) > LIMIT_USERNAME_CHARS ) {
439 result_buf->pw_name[LIMIT_USERNAME_CHARS] = '\0';
440 buflen = buflen + ( buffer - &result_buf->pw_name[LIMIT_USERNAME_CHARS+1] );
441 buffer = &result_buf->pw_name[LIMIT_USERNAME_CHARS+1];
442 }
443#endif
444
03b6b479 445 *errnop = errno;
446 return NSS_STATUS_SUCCESS;
447 } while(0);
448
449 *errnop = ERANGE;
450 return NSS_STATUS_UNAVAIL;
451}
452
453
908a6ebe 454/**
455 * This function is exported; glibc will invoke it in order to gather
456 * the user information (userid, homedir, shell) associated with a
457 * numerical userid.
458 */
459enum nss_status _nss_afs_getpwuid_r (uid_t uid,
460 struct passwd *result_buf,
461 char *buffer,
462 size_t buflen,
463 int *errnop) {
464 int temp;
465 char* name;
466
467 if (init_afs()) return NSS_STATUS_UNAVAIL;
468
469 name = buffer;
470 temp = ptsid2name( uid, &buffer, &buflen);
471 if (temp != NSS_STATUS_SUCCESS) {
472 *errnop = ENOENT;
473 return temp;
474 }
475
908a6ebe 476 return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop);
477}
478
479/**
480 * This function is exported; glibc will invoke it in order to gather
481 * the user information (userid, homedir, shell) associated with a
482 * username.
03b6b479 483 */
908a6ebe 484enum nss_status _nss_afs_getpwnam_r (char *name,
485 struct passwd *result_buf,
486 char *buffer,
487 size_t buflen,
488 int *errnop) {
03b6b479 489 uid_t uid;
490 int temp;
491
492 if (init_afs()) return NSS_STATUS_UNAVAIL;
493
494 temp = ptsname2id(name,&uid);
495 if (temp != NSS_STATUS_SUCCESS) {
496 *errnop = ENOENT;
497 return temp;
498 }
499
908a6ebe 500 return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop);
03b6b479 501}
502