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