03b6b479 |
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 |
582fb19e |
57 | #define MIN_OLDPAG_GID 0x3f00 |
58 | #define MAX_OLDPAG_GID 0xff00 |
03b6b479 |
59 | |
91bb9ebc |
60 | #define MAX_CELLNAME 256 |
61 | |
03b6b479 |
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; |
91bb9ebc |
69 | char cellname[MAX_CELLNAME]; |
03b6b479 |
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; |
91bb9ebc |
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"); |
03b6b479 |
185 | if (thiscell == NULL) break; |
91bb9ebc |
186 | len=fread(cellname,1,MAX_CELLNAME,thiscell); |
03b6b479 |
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); |
91bb9ebc |
195 | |
03b6b479 |
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: |
03b6b479 |
265 | break; |
266 | |
267 | case SHELL_ADMINLINK: |
91bb9ebc |
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; |
03b6b479 |
274 | |
275 | case SHELL_USERLINK: |
91bb9ebc |
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; |
03b6b479 |
283 | } |
91bb9ebc |
284 | if (! cpstr("/bin/bash",buffer,buflen) ) |
285 | return -1; |
03b6b479 |
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; |
582fb19e |
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 { |
03b6b479 |
302 | *errnop=ENOENT; |
303 | return NSS_STATUS_NOTFOUND; |
304 | } |
305 | do { |
306 | result->gr_gid=gid; |
307 | |
308 | result->gr_name=buffer; |
582fb19e |
309 | length=snprintf(buffer,buflen,"AfsPag-%x",showgid); |
03b6b479 |
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 | |