1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21: #include <errno.h>
22: #include <fcntl.h>
23: #include <string.h>
24: #include <libintl.h>
25: #include <rpc/rpc.h>
26: #include <rpc/auth.h>
27: #include <rpcsvc/nis.h>
28: #include <sys/socket.h>
29: #include <sys/stat.h>
30: #include <unistd.h>
31: #include <netinet/in.h>
32: #include <arpa/inet.h>
33: #include <bits/libc-lock.h>
34:
35: #include "nis_xdr.h"
36: #include "nis_intern.h"
37: #include <libnsl.h>
38:
39: static const struct timeval RPCTIMEOUT = {10, 0};
40: static const struct timeval UDPTIMEOUT = {5, 0};
41:
42: extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
43: u_long version, u_int protocol);
44:
45: unsigned long int
46: inetstr2int (const char *str)
47: {
48: size_t j = 0;
49: for (size_t i = 0; str[i] != '\0'; ++i)
50: if (str[i] == '.' && __builtin_expect (++j == 4, 0))
51: {
52: char buffer[i + 1];
53: buffer[i] = '\0';
54: return inet_addr (memcpy (buffer, str, i));
55: }
56:
57: return inet_addr (str);
58: }
59:
60: void
61: __nisbind_destroy (dir_binding *bind)
62: {
63: if (bind->clnt != NULL)
64: {
65: if (bind->use_auth)
66: auth_destroy (bind->clnt->cl_auth);
67: clnt_destroy (bind->clnt);
68: }
69: }
70: libnsl_hidden_def (__nisbind_destroy)
71:
72: nis_error
73: __nisbind_next (dir_binding *bind)
74: {
75: if (bind->clnt != NULL)
76: {
77: if (bind->use_auth)
78: auth_destroy (bind->clnt->cl_auth);
79: clnt_destroy (bind->clnt);
80: bind->clnt = NULL;
81: }
82:
83: if (bind->trys >= bind->server_len)
84: return NIS_FAIL;
85:
86: for (u_int j = bind->current_ep + 1;
87: j < bind->server_val[bind->server_used].ep.ep_len; ++j)
88: if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
89: "inet") == 0)
90: if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
91: {
92: bind->current_ep = j;
93: return NIS_SUCCESS;
94: }
95:
96: ++bind->trys;
97: ++bind->server_used;
98: if (bind->server_used >= bind->server_len)
99: bind->server_used = 0;
100:
101: for (u_int j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
102: if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
103: "inet") == 0)
104: if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
105: {
106: bind->current_ep = j;
107: return NIS_SUCCESS;
108: }
109:
110: return NIS_FAIL;
111: }
112: libnsl_hidden_def (__nisbind_next)
113:
114: static struct ckey_cache_entry
115: {
116: struct in_addr inaddr;
117: in_port_t port;
118: unsigned int protocol;
119: des_block ckey;
120: } *ckey_cache;
121: static size_t ckey_cache_size;
122: static size_t ckey_cache_allocated;
123: static pid_t ckey_cache_pid;
124: static uid_t ckey_cache_euid;
125: __libc_lock_define_initialized (static, ckey_cache_lock)
126:
127: static bool_t
128: get_ckey (des_block *ckey, struct sockaddr_in *addr, unsigned int protocol)
129: {
130: size_t i;
131: pid_t pid = getpid ();
132: uid_t euid = geteuid ();
133: bool_t ret = FALSE;
134:
135: __libc_lock_lock (ckey_cache_lock);
136:
137: if (ckey_cache_pid != pid || ckey_cache_euid != euid)
138: {
139: ckey_cache_size = 0;
140: ckey_cache_pid = pid;
141: ckey_cache_euid = euid;
142: }
143:
144: for (i = 0; i < ckey_cache_size; ++i)
145: if (ckey_cache[i].port == addr->sin_port
146: && ckey_cache[i].protocol == protocol
147: && memcmp (&ckey_cache[i].inaddr, &addr->sin_addr,
148: sizeof (addr->sin_addr)) == 0)
149: {
150: *ckey = ckey_cache[i].ckey;
151: ret = TRUE;
152: break;
153: }
154:
155: if (!ret && key_gendes (ckey) >= 0)
156: {
157: ret = TRUE;
158:
159: if (ckey_cache_size == 256)
160: ckey_cache_size = 0;
161: if (ckey_cache_size == ckey_cache_allocated)
162: {
163: size_t size = ckey_cache_allocated ? ckey_cache_allocated * 2 : 16;
164: struct ckey_cache_entry *new_cache
165: = realloc (ckey_cache, size * sizeof (*ckey_cache));
166: if (new_cache != NULL)
167: {
168: ckey_cache = new_cache;
169: ckey_cache_allocated = size;
170: }
171: }
172: ckey_cache[ckey_cache_size].inaddr = addr->sin_addr;
173: ckey_cache[ckey_cache_size].port = addr->sin_port;
174: ckey_cache[ckey_cache_size].protocol = protocol;
175: ckey_cache[ckey_cache_size++].ckey = *ckey;
176: }
177:
178: __libc_lock_unlock (ckey_cache_lock);
179: return ret;
180: }
181:
182: nis_error
183: __nisbind_connect (dir_binding *dbp)
184: {
185: nis_server *serv;
186: u_short port;
187:
188: if (dbp == NULL)
189: return NIS_FAIL;
190:
191: serv = &dbp->server_val[dbp->server_used];
192:
193: memset (&dbp->addr, '\0', sizeof (dbp->addr));
194: dbp->addr.sin_family = AF_INET;
195:
196: dbp->addr.sin_addr.s_addr =
197: inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
198:
199: if (dbp->addr.sin_addr.s_addr == INADDR_NONE)
200: return NIS_FAIL;
201:
202:
203:
204: port = __pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION,
205: dbp->use_udp ? IPPROTO_UDP : IPPROTO_TCP);
206: if (port == 0)
207: return NIS_RPCERROR;
208:
209: dbp->addr.sin_port = htons (port);
210: dbp->socket = RPC_ANYSOCK;
211: if (dbp->use_udp)
212: dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
213: UDPTIMEOUT, &dbp->socket);
214: else
215: dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
216: &dbp->socket, 0, 0);
217:
218: if (dbp->clnt == NULL)
219: return NIS_RPCERROR;
220:
221: clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
222:
223: if (fcntl (dbp->socket, F_SETFD, 1) == -1)
224: perror ("fcntl: F_SETFD");
225:
226: if (dbp->use_auth)
227: {
228: if (serv->key_type == NIS_PK_DH)
229: {
230: char netname[MAXNETNAMELEN + 1];
231: char *p;
232: des_block ckey;
233:
234: p = stpcpy (netname, "unix@");
235: strncpy (p, serv->name, MAXNETNAMELEN - 5);
236: netname[MAXNETNAMELEN] = '\0';
237: dbp->clnt->cl_auth = NULL;
238: if (get_ckey (&ckey, &dbp->addr,
239: dbp->use_udp ? IPPROTO_UDP : IPPROTO_TCP))
240: dbp->clnt->cl_auth =
241: authdes_pk_create (netname, &serv->pkey, 300, NULL, &ckey);
242: if (!dbp->clnt->cl_auth)
243: dbp->clnt->cl_auth = authunix_create_default ();
244: }
245: else
246: dbp->clnt->cl_auth = authunix_create_default ();
247: }
248:
249: return NIS_SUCCESS;
250: }
251: libnsl_hidden_def (__nisbind_connect)
252:
253: nis_error
254: __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
255: unsigned int serv_len, unsigned int server_used,
256: unsigned int current_ep, unsigned int flags)
257: {
258: dbp->clnt = NULL;
259:
260: dbp->server_len = serv_len;
261: dbp->server_val = (nis_server *)serv_val;
262:
263: if (flags & USE_DGRAM)
264: dbp->use_udp = TRUE;
265: else
266: dbp->use_udp = FALSE;
267:
268: if (flags & NO_AUTHINFO)
269: dbp->use_auth = FALSE;
270: else
271: dbp->use_auth = TRUE;
272:
273: if (flags & MASTER_ONLY)
274: dbp->master_only = TRUE;
275: else
276: dbp->master_only = FALSE;
277:
278:
279: dbp->trys = 1;
280:
281: dbp->class = -1;
282: if (server_used == ~0)
283: {
284: if (__nis_findfastest (dbp) < 1)
285: return NIS_NAMEUNREACHABLE;
286: }
287: else
288: {
289: dbp->server_used = server_used;
290: dbp->current_ep = current_ep;
291: }
292:
293: return NIS_SUCCESS;
294: }
295: libnsl_hidden_def (__nisbind_create)
296:
297:
298:
299: nis_error
300: __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
301: xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
302: {
303: enum clnt_stat result;
304: nis_error retcode;
305:
306: if (dbp == NULL)
307: return NIS_NAMEUNREACHABLE;
308:
309: do
310: {
311: again:
312: result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
313:
314: if (result != RPC_SUCCESS)
315: retcode = NIS_RPCERROR;
316: else
317: {
318: switch (prog)
319: {
320: case NIS_IBLIST:
321: if ((((nis_result *)resp)->status == NIS_CBRESULTS) &&
322: (cb != NULL))
323: {
324: __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
325: break;
326: }
327:
328:
329: case NIS_LOOKUP:
330: case NIS_ADD:
331: case NIS_MODIFY:
332: case NIS_REMOVE:
333: case NIS_IBADD:
334: case NIS_IBMODIFY:
335: case NIS_IBREMOVE:
336: case NIS_IBFIRST:
337: case NIS_IBNEXT:
338: if (((nis_result *)resp)->status == NIS_SYSTEMERROR
339: || ((nis_result *)resp)->status == NIS_NOSUCHNAME
340: || ((nis_result *)resp)->status == NIS_NOT_ME)
341: {
342: next_server:
343: if (__nisbind_next (dbp) == NIS_SUCCESS)
344: {
345: while (__nisbind_connect (dbp) != NIS_SUCCESS)
346: {
347: if (__nisbind_next (dbp) != NIS_SUCCESS)
348: return NIS_SUCCESS;
349: }
350: }
351: else
352: break;
353: goto again;
354: }
355: break;
356: case NIS_FINDDIRECTORY:
357: if (((fd_result *)resp)->status == NIS_SYSTEMERROR
358: || ((fd_result *)resp)->status == NIS_NOSUCHNAME
359: || ((fd_result *)resp)->status == NIS_NOT_ME)
360: goto next_server;
361: break;
362: case NIS_DUMPLOG:
363: case NIS_DUMP:
364: if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
365: || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
366: || ((log_result *)resp)->lr_status == NIS_NOT_ME)
367: goto next_server;
368: break;
369: default:
370: break;
371: }
372: retcode = NIS_SUCCESS;
373: }
374: }
375: while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
376:
377: return retcode;
378: }
379: libnsl_hidden_def (__do_niscall3)
380:
381:
382: nis_error
383: __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
384: xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
385: unsigned int flags, nis_cb *cb)
386: {
387: dir_binding dbp;
388: nis_error status;
389:
390: if (flags & MASTER_ONLY)
391: server_len = 1;
392:
393: status = __nisbind_create (&dbp, server, server_len, ~0, ~0, flags);
394: if (status != NIS_SUCCESS)
395: return status;
396:
397: while (__nisbind_connect (&dbp) != NIS_SUCCESS)
398: if (__nisbind_next (&dbp) != NIS_SUCCESS)
399: return NIS_NAMEUNREACHABLE;
400:
401: status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
402:
403: __nisbind_destroy (&dbp);
404:
405: return status;
406:
407: }
408:
409: static directory_obj *
410: rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
411: {
412: fd_result *fd_res;
413: XDR xdrs;
414:
415: switch (nis_dir_cmp (name, dir->do_name))
416: {
417: case SAME_NAME:
418: *status = NIS_SUCCESS;
419: return dir;
420: case NOT_SEQUENTIAL:
421:
422: case HIGHER_NAME:
423: {
424: directory_obj *obj;
425: const char *ndomain = __nis_domain_of (dir->do_name);
426:
427:
428:
429:
430: fd_res = __nis_finddirectory (dir, ndomain);
431: if (fd_res == NULL)
432: {
433: nis_free_directory (dir);
434: *status = NIS_NOMEMORY;
435: return NULL;
436: }
437: *status = fd_res->status;
438: if (fd_res->status != NIS_SUCCESS)
439: {
440:
441: __free_fdresult (fd_res);
442: return dir;
443: }
444: nis_free_directory (dir);
445: obj = calloc (1, sizeof (directory_obj));
446: if (obj == NULL)
447: {
448: __free_fdresult (fd_res);
449: *status = NIS_NOMEMORY;
450: return NULL;
451: }
452: xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
453: fd_res->dir_data.dir_data_len, XDR_DECODE);
454: _xdr_directory_obj (&xdrs, obj);
455: xdr_destroy (&xdrs);
456: __free_fdresult (fd_res);
457:
458:
459:
460: return rec_dirsearch (name, obj, status);
461: }
462: break;
463: case LOWER_NAME:
464: {
465: directory_obj *obj;
466: size_t namelen = strlen (name);
467: char leaf[namelen + 3];
468: char domain[namelen + 3];
469: const char *ndomain;
470: char *cp;
471:
472: strcpy (domain, name);
473:
474: do
475: {
476: if (domain[0] == '\0')
477: {
478: nis_free_directory (dir);
479: return NULL;
480: }
481: nis_leaf_of_r (domain, leaf, sizeof (leaf));
482: ndomain = __nis_domain_of (domain);
483: memmove (domain, ndomain, strlen (ndomain) + 1);
484: }
485: while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
486:
487: cp = rawmemchr (leaf, '\0');
488: *cp++ = '.';
489: strcpy (cp, domain);
490:
491: fd_res = __nis_finddirectory (dir, leaf);
492: if (fd_res == NULL)
493: {
494: nis_free_directory (dir);
495: *status = NIS_NOMEMORY;
496: return NULL;
497: }
498: *status = fd_res->status;
499: if (fd_res->status != NIS_SUCCESS)
500: {
501:
502: __free_fdresult (fd_res);
503: return dir;
504: }
505: nis_free_directory (dir);
506: obj = calloc (1, sizeof(directory_obj));
507: if (obj == NULL)
508: {
509: __free_fdresult (fd_res);
510: *status = NIS_NOMEMORY;
511: return NULL;
512: }
513: xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
514: fd_res->dir_data.dir_data_len, XDR_DECODE);
515: _xdr_directory_obj (&xdrs, obj);
516: xdr_destroy (&xdrs);
517: __free_fdresult (fd_res);
518:
519:
520: return rec_dirsearch (name, obj, status);
521: }
522: break;
523: case BAD_NAME:
524: nis_free_directory (dir);
525: *status = NIS_BADNAME;
526: return NULL;
527: }
528: nis_free_directory (dir);
529: *status = NIS_FAIL;
530: return NULL;
531: }
532:
533:
534:
535: static directory_obj *
536: first_shoot (const_nis_name name, directory_obj *dir)
537: {
538: directory_obj *obj = NULL;
539: fd_result *fd_res;
540: XDR xdrs;
541:
542: if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
543: return dir;
544:
545: fd_res = __nis_finddirectory (dir, name);
546: if (fd_res == NULL)
547: return NULL;
548: if (fd_res->status == NIS_SUCCESS
549: && (obj = calloc (1, sizeof (directory_obj))) != NULL)
550: {
551: xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
552: fd_res->dir_data.dir_data_len, XDR_DECODE);
553: _xdr_directory_obj (&xdrs, obj);
554: xdr_destroy (&xdrs);
555:
556: if (strcmp (dir->do_name, obj->do_name) != 0)
557: {
558: nis_free_directory (obj);
559: obj = NULL;
560: }
561: }
562:
563: __free_fdresult (fd_res);
564:
565: if (obj != NULL)
566: nis_free_directory (dir);
567:
568: return obj;
569: }
570:
571: static struct nis_server_cache
572: {
573: int search_parent;
574: int uses;
575: unsigned int size;
576: unsigned int server_used;
577: unsigned int current_ep;
578: time_t expires;
579: char name[];
580: } *nis_server_cache[16];
581: static time_t nis_cold_start_mtime;