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