v1.04 fix potential segfault
[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 #define MAXUSERNAMELEN             256
82
83 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
84
85 extern struct ubik_client *pruclient;
86
87 int  afs_initialized = 0;
88 char cellname[MAXCELLNAMELEN];
89 char homedir_prefix[MAXPATHLEN];
90 int  homedir_prefix_len=0;
91 char homedirs_method=0;
92 char shells_method=0;
93
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  */
100 int cpstr( char *str, char **buf, size_t *buflen) {
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;
107 }
108
109 /**
110  * Look up the name corresponding to uid, store in buffer.
111  */
112 enum nss_status ptsid2name(int uid, char **buffer, int *buflen) {
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
153 /**
154  * Look up the uid corresponding to name in ptserver.
155  */
156 enum nss_status ptsname2id(char *name, uid_t* uid) {
157   int res;
158   idlist lid;
159   namelist lnames;
160   char uname[MAXUSERNAMELEN];
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;
173   lnames.namelist_val = (prname*)uname;
174   // apparently ubik expects to be able to modify this?
175   strncpy(uname, name, MAXUSERNAMELEN);
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
190 /**
191  *  Initialize the library; returns zero on success
192  */
193 int 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;
203
204     len = snprintf(cellname, MAXCELLNAMELEN,
205                    "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH);
206     if (len < 0 || len >= MAXCELLNAMELEN) return -1;
207
208     thiscell=fopen(cellname,"r");
209     if (thiscell == NULL) break;
210     len=fread(cellname,1,MAXCELLNAMELEN,thiscell);
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);
219
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
237 /**
238  * Retrieves the homedir for a given user; returns 0 on success.
239  */
240 int 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
276 /**
277  * Retrieves the shell for a given user; returns 0 on success.
278  */
279 int 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:
289       break;
290
291     case SHELL_ADMINLINK:
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;
298
299     case SHELL_USERLINK:
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;
307   }
308   if (! cpstr("/bin/bash",buffer,buflen) )
309     return -1;
310   return 0;
311 }
312
313
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.
318  */
319 enum nss_status _nss_afs_getgrgid_r (gid_t gid,
320                                      struct group *result,
321                                      char *buffer,
322                                      size_t buflen,
323                                      int *errnop) {
324   int length;
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 {
331     *errnop=ENOENT;
332     return NSS_STATUS_NOTFOUND;
333   }
334   do {
335     result->gr_gid=gid;
336
337     result->gr_name=buffer;
338     length=snprintf(buffer,buflen,"AfsPag-%x",showgid);
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
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  */
365 enum 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;
372   do {
373     /* set the password to "x" */
374     result_buf->pw_passwd = buffer;
375     if ( ! cpstr("x",&buffer, &buflen) ) break;
376
377     /* the uid and gid are both the uid passed in */
378     result_buf->pw_uid = uid;
379     result_buf->pw_gid = 65534;
380
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
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  */
407 enum 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.
439  */
440 enum nss_status _nss_afs_getpwnam_r (char *name,
441                                      struct passwd *result_buf,
442                                      char *buffer,
443                                      size_t buflen,
444                                      int *errnop) {
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
456   return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop);
457 }
458