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