release v1.02 (important bugfix)
[libnss-afs.git] / nss_afs.c
1
2 /*
3  * libnss_afs.c
4  *
5  * Copyright 2008, licensed under GNU Library General Public License (LGPL)
6  * see COPYING file for details
7  *
8  * derived from Frank Burkhardt's libnss_ptdb,
9  * which was derived from Todd M. Lewis' libnss_pts
10  */
11
12 /*
13  * if you are reading this code for the first time, start at the
14  * bottom and work your way upwards
15  */
16
17 #include <ctype.h>
18 #include <err.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <grp.h>
23 #include <netinet/in.h>
24 #include <nss.h>
25 #include <pthread.h>
26 #include <pwd.h>
27 #include <rx/rx.h>
28 #include <rx/xdr.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/select.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <afs/afs.h>
39 #include <afs/afsutil.h>
40 #include <afs/cellconfig.h>
41 #include <afs/com_err.h>
42 #include <afs/param.h>
43 #include <afs/ptclient.h>
44 #include <afs/pterror.h>
45 #include <afs/stds.h>
46
47 #define HOMEDIR_AUTO 0
48 #define HOMEDIR_ADMINLINK 1
49 #define HOMEDIR_PREFIX 2
50 #define SHELL_BASH 0
51 #define SHELL_ADMINLINK 1
52 #define SHELL_USERLINK 2
53
54 #define AFS_MAGIC_ANONYMOUS_USERID 32766
55 #define MIN_PAG_GID 0x41000000L
56 #define MAX_PAG_GID 0x41FFFFFFL
57 #define MIN_OLDPAG_GID 0x3f00
58 #define MAX_OLDPAG_GID 0xff00
59
60 #define MAX_CELLNAME 256
61
62 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
63
64 extern int cpstr( char *str, char **buf, size_t *buflen);
65
66 extern struct ubik_client *pruclient;
67
68 int  afs_initialized = 0;
69 char cellname[MAX_CELLNAME];
70 char homedir_prefix[300];
71 int  homedir_prefix_len=0;
72 char homedirs_method=0;
73 char shells_method=0;
74
75 int cpstr( char *str, char **buf, size_t *buflen) {
76     int len = strlen(str);
77     if ( len >= *buflen-1 ) return 0;
78     strcpy(*buf,str);
79     *buflen -= len + 1;
80     *buf    += len + 1;
81     return len;
82 }
83
84
85 /*
86  * Look up the name corresponding to uid, store in buffer.
87  * 
88  * returns NSS_STATUS_SUCCESS, NSS_STATUS_NOTFOUND, or NSS_STATUS_UNAVAIL
89  */
90 int ptsid2name(int uid, char **buffer, int *buflen) {
91   int ret, i;
92   idlist lid;
93   namelist lnames;
94
95   init_afs();
96
97   if (uid==AFS_MAGIC_ANONYMOUS_USERID) {
98     if (!cpstr("anonymous", buffer, buflen)) return NSS_STATUS_UNAVAIL;
99     return NSS_STATUS_SUCCESS;
100   }
101
102   if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
103   
104   lid.idlist_val = (afs_int32*)&uid;
105   lid.idlist_len = 1;
106   lnames.namelist_val = 0;
107   lnames.namelist_len = 0;
108
109   if (ubik_Call(PR_IDToName,pruclient,0,&lid,&lnames) != PRSUCCESS) {
110     pthread_mutex_unlock(&mutex);
111     return NSS_STATUS_UNAVAIL;
112   }
113
114   ret = NSS_STATUS_NOTFOUND;
115   for (i=0;i<lnames.namelist_len;i++) {
116     int delta = strlen(lnames.namelist_val[i]);
117     if ( (delta < buflen) && islower(*(lnames.namelist_val[i])) ) {
118       cpstr(lnames.namelist_val[i], buffer, buflen);
119       ret = NSS_STATUS_SUCCESS;
120     }
121   }
122   free(lnames.namelist_val);
123   /* free(lid.idlist_val); */
124   lid.idlist_val = 0;
125   lid.idlist_len = 0;
126
127   pthread_mutex_unlock(&mutex);
128   return ret;
129 }
130
131 /*
132  * Look up the uid corresponding to name, stores it in *uid.
133  * 
134  * returns NSS_STATUS_SUCCESS, NSS_STATUS_NOTFOUND, or NSS_STATUS_UNAVAIL
135  */
136 int ptsname2id(char *name, uid_t* uid) {
137   int res;
138   idlist lid;
139   namelist lnames;
140
141   init_afs();
142   
143   if (!strcmp(name,"anonymous")) {
144     *uid = AFS_MAGIC_ANONYMOUS_USERID;
145     return NSS_STATUS_SUCCESS;
146   }
147
148   if (pthread_mutex_lock(&mutex)) return NSS_STATUS_UNAVAIL;
149
150   lid.idlist_val = 0;
151   lid.idlist_len = 0;
152   lnames.namelist_val = (prname*)name;
153   lnames.namelist_len = 1;
154
155   if (ubik_Call(PR_NameToID,pruclient,0,&lnames,&lid) != PRSUCCESS) {
156     pthread_mutex_unlock(&mutex);
157     return NSS_STATUS_UNAVAIL;
158   }
159   pthread_mutex_unlock(&mutex);
160
161   res = (uid_t)lid.idlist_val[0];
162   if (res == AFS_MAGIC_ANONYMOUS_USERID) return NSS_STATUS_NOTFOUND;
163   *uid = res;
164   return NSS_STATUS_SUCCESS;
165 }
166
167 /*
168  *  returns zero on success
169  */
170 int init_afs() {
171   FILE *thiscell;
172   int len;
173
174   if (afs_initialized) return 0;
175
176   if (pthread_mutex_lock(&mutex)) return -1;
177   do {
178     homedirs_method=HOMEDIR_PREFIX;
179     shells_method=SHELL_USERLINK;
180
181     len = snprintf(cellname, MAX_CELLNAME, "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH);
182     if (len < 0 || len >= MAX_CELLNAME) return -1;
183
184     thiscell=fopen(cellname,"r");
185     if (thiscell == NULL) break;
186     len=fread(cellname,1,MAX_CELLNAME,thiscell);
187     if (!feof(thiscell)) {
188       // Cellname too long
189       fclose(thiscell);
190       strcpy(homedir_prefix,"/tmp/\0");
191       homedir_prefix_len=5;
192       break;
193     }
194     fclose(thiscell);
195
196     if (cellname[len-1] == '\n') len--;
197     cellname[len]='\0';
198     sprintf(homedir_prefix,"/afs/%s/user/",cellname);
199     homedir_prefix_len=strlen(homedir_prefix);
200     
201     if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH, 0)) break;
202     
203     afs_initialized = 1;
204     pthread_mutex_unlock(&mutex);
205     return 0;
206
207   } while(0);
208   pthread_mutex_unlock(&mutex);
209   return -1;
210 }
211
212
213 /*
214   result=get_homedir(char *name,char **buffer,size_t *buflen)
215   Writes the guessed Homedirectory of a given user 'name' into
216   '*buffer', increasing *buflen accordingly.
217   result == 1, only if the buffer was big enough.
218 */
219 int get_homedir(char *name, char **buffer, size_t *buflen) {
220   char buf[256];
221   int temp;
222   char *b;
223   b=*buffer;
224   switch (homedirs_method) {
225     case HOMEDIR_PREFIX:
226       homedir_prefix[homedir_prefix_len+0]=name[0];
227       homedir_prefix[homedir_prefix_len+1]='/';
228       homedir_prefix[homedir_prefix_len+2]=name[0];
229       homedir_prefix[homedir_prefix_len+3]=name[1];
230       homedir_prefix[homedir_prefix_len+4]='/';
231       homedir_prefix[homedir_prefix_len+5]=0;
232       strncpy(&homedir_prefix[homedir_prefix_len+5],name,40);
233       if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
234       break;
235     case HOMEDIR_AUTO:
236       homedir_prefix[homedir_prefix_len]=0;
237       strncpy(&homedir_prefix[homedir_prefix_len],name,40);
238       if (! cpstr(homedir_prefix,buffer,buflen) ) return -1;
239       break;
240     case HOMEDIR_ADMINLINK:
241       if ( snprintf(buf,256,"/afs/%s/admin/homedirs/%s",cellname,name) > 0 ) {
242         temp=readlink(buf,*buffer,*buflen);
243         if ( temp > -1) {
244           b[temp]=0;
245           *buflen = *buflen - temp - 1;
246           return -1;
247         }
248       }
249       if (! cpstr("/tmp",buffer,buflen) ) return -1;
250       break;
251   }
252   return 0;
253 }
254
255 int get_shell(char *name, char **buffer, size_t *buflen) {
256   char buf[256];
257   int temp;
258   char *b;
259   char* bufx = buf;
260   int bufxlen = 256;
261   b=*buffer;
262
263   switch (shells_method) {
264     case SHELL_BASH:
265       break;
266
267     case SHELL_ADMINLINK:
268       if (snprintf(buf,256,"/afs/%s/admin/shells/%s",cellname,name)<=0) break;
269       temp = readlink(buf,*buffer,*buflen);
270       if (temp < 0) break;
271       b[temp]=0;
272       *buflen = *buflen - temp - 1;
273       return 0;
274
275     case SHELL_USERLINK:
276       if (get_homedir(name, &bufx, &bufxlen)) break;
277       if (strncpy(buf+strlen(buf),"/.loginshell",bufxlen)<=0) break;
278       temp = readlink(buf,*buffer,*buflen);
279       if (temp < 0) break;
280       b[temp]=0;
281       *buflen = *buflen - temp - 1;
282       return 0;
283   }
284   if (! cpstr("/bin/bash",buffer,buflen) )
285     return -1;
286   return 0;
287 }
288
289
290 /**
291  *  this function is invoked by glibc to resolve the name of a numeric groupid
292  */
293 enum nss_status _nss_afs_getgrgid_r (gid_t gid, struct group *result,
294                                      char *buffer, size_t buflen, int *errnop) {
295   int length;
296   int showgid = 0;
297   if (gid >= MIN_PAG_GID && gid <= MAX_PAG_GID) {
298     showgid = gid-MIN_PAG_GID;
299   } else if (gid >= MIN_OLDPAG_GID && gid <= MAX_OLDPAG_GID) {
300     showgid = gid-MIN_OLDPAG_GID;
301   } else {
302     *errnop=ENOENT;
303     return NSS_STATUS_NOTFOUND;
304   }
305   do {
306     result->gr_gid=gid;
307
308     result->gr_name=buffer;
309     length=snprintf(buffer,buflen,"AfsPag-%x",showgid);
310     
311     if (length < 0) break;
312     length += 1;
313     buflen -= length;
314     buffer += length;
315
316     result->gr_passwd=buffer;
317
318     if (!cpstr("x",&buffer,&buflen)) break;
319
320     if (buflen < sizeof(char*)) break;
321     result->gr_mem=buffer;
322     result->gr_mem[0] = NULL;
323
324     *errnop=errno;
325     return NSS_STATUS_SUCCESS;
326
327   } while(0);
328   *errnop=ENOENT;
329   return NSS_STATUS_UNAVAIL;
330 }
331
332 /*
333   This is a the ptdb-getpwuid-function.
334 */
335 enum nss_status _nss_afs_getpwuid_r (uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) {
336   int temp;
337
338   if (init_afs()) return NSS_STATUS_UNAVAIL;
339   
340   result_buf->pw_name = buffer;
341   temp = ptsid2name( uid, &buffer, &buflen);
342   if (temp != NSS_STATUS_SUCCESS) {
343     *errnop = ENOENT;
344     return temp;
345   }
346
347 #ifdef LIMIT_USERNAME_CHARS
348   if ( strlen(result_buf->pw_name) > LIMIT_USERNAME_CHARS ) {
349     result_buf->pw_name[LIMIT_USERNAME_CHARS] = '\0';
350     buflen = buflen + ( buffer - &result_buf->pw_name[LIMIT_USERNAME_CHARS+1] );
351     buffer = &result_buf->pw_name[LIMIT_USERNAME_CHARS+1];
352   }
353 #endif
354
355   do {
356     /* set the password to "x" */
357     result_buf->pw_passwd = buffer;
358     if ( ! cpstr("x",&buffer, &buflen) ) break;
359     /* the uid and gid are both the uid passed in */
360     result_buf->pw_uid = uid;
361     result_buf->pw_gid = 65534;
362     /* make the gecos the same as the PTS name */
363     result_buf->pw_gecos = buffer;
364     if ( ! cpstr(result_buf->pw_name, &buffer, &buflen ) ) break;
365
366     // Set the homedirectory
367     result_buf->pw_dir = buffer;
368     if ( get_homedir(result_buf->pw_name,&buffer,&buflen) ) break;
369
370     // Set the login shell
371     result_buf->pw_shell = buffer;
372     if ( get_shell(result_buf->pw_name,&buffer,&buflen) ) break;
373
374     *errnop = errno;
375     return NSS_STATUS_SUCCESS;
376   } while(0);
377
378   *errnop = ERANGE;
379   return NSS_STATUS_UNAVAIL;
380 }
381
382
383 /*
384  * This is the ptdb-getpwnam-function.
385  */
386 enum nss_status _nss_afs_getpwnam_r (char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) {
387   uid_t uid;
388   int temp;
389
390   if (init_afs()) return NSS_STATUS_UNAVAIL;
391
392   temp = ptsname2id(name,&uid);
393   if (temp != NSS_STATUS_SUCCESS) {
394     *errnop = ENOENT;
395     return temp;
396   }
397
398   // This causes an additional PTDB-lookup and should be removed in the future
399   return _nss_afs_getpwuid_r (uid, result_buf,buffer,buflen, errnop);
400 }
401