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 |
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 | |