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