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