initial import
[hcoop/debian/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