1 /* Copyright (C) 1997, 1998, 2001, 2004, 2005, 2006, 2007
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 #include <rpcsvc/nis.h>
28 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <bits/libc-lock.h>
36 #include "nis_intern.h"
39 static const struct timeval RPCTIMEOUT
= {10, 0};
40 static const struct timeval UDPTIMEOUT
= {5, 0};
42 extern u_short
__pmap_getnisport (struct sockaddr_in
*address
, u_long program
,
43 u_long version
, u_int protocol
);
46 inetstr2int (const char *str
)
49 for (size_t i
= 0; str
[i
] != '\0'; ++i
)
50 if (str
[i
] == '.' && __builtin_expect (++j
== 4, 0))
54 return inet_addr (memcpy (buffer
, str
, i
));
57 return inet_addr (str
);
61 __nisbind_destroy (dir_binding
*bind
)
63 if (bind
->clnt
!= NULL
)
66 auth_destroy (bind
->clnt
->cl_auth
);
67 clnt_destroy (bind
->clnt
);
70 libnsl_hidden_def (__nisbind_destroy
)
73 __nisbind_next (dir_binding
*bind
)
75 if (bind
->clnt
!= NULL
)
78 auth_destroy (bind
->clnt
->cl_auth
);
79 clnt_destroy (bind
->clnt
);
83 if (bind
->trys
>= bind
->server_len
)
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
,
90 if (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].proto
[0] == '-')
98 if (bind
->server_used
>= bind
->server_len
)
99 bind
->server_used
= 0;
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
,
104 if (bind
->server_val
[bind
->server_used
].ep
.ep_val
[j
].proto
[0] == '-')
106 bind
->current_ep
= j
;
112 libnsl_hidden_def (__nisbind_next
)
114 static struct ckey_cache_entry
116 struct in_addr inaddr
;
118 unsigned int protocol
;
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
)
128 get_ckey (des_block
*ckey
, struct sockaddr_in
*addr
, unsigned int protocol
)
131 pid_t pid
= getpid ();
132 uid_t euid
= geteuid ();
135 __libc_lock_lock (ckey_cache_lock
);
137 if (ckey_cache_pid
!= pid
|| ckey_cache_euid
!= euid
)
140 ckey_cache_pid
= pid
;
141 ckey_cache_euid
= euid
;
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)
150 *ckey
= ckey_cache
[i
].ckey
;
155 if (!ret
&& key_gendes (ckey
) >= 0)
158 /* Don't grow the cache indefinitely. */
159 if (ckey_cache_size
== 256)
161 if (ckey_cache_size
== ckey_cache_allocated
)
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
)
168 ckey_cache
= new_cache
;
169 ckey_cache_allocated
= size
;
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
;
178 __libc_lock_unlock (ckey_cache_lock
);
183 __nisbind_connect (dir_binding
*dbp
)
191 serv
= &dbp
->server_val
[dbp
->server_used
];
193 memset (&dbp
->addr
, '\0', sizeof (dbp
->addr
));
194 dbp
->addr
.sin_family
= AF_INET
;
196 dbp
->addr
.sin_addr
.s_addr
=
197 inetstr2int (serv
->ep
.ep_val
[dbp
->current_ep
].uaddr
);
199 if (dbp
->addr
.sin_addr
.s_addr
== INADDR_NONE
)
202 /* Check, if the host is online and rpc.nisd is running. Much faster
203 then the clnt*_create functions: */
204 port
= __pmap_getnisport (&dbp
->addr
, NIS_PROG
, NIS_VERSION
,
205 dbp
->use_udp
? IPPROTO_UDP
: IPPROTO_TCP
);
209 dbp
->addr
.sin_port
= htons (port
);
210 dbp
->socket
= RPC_ANYSOCK
;
212 dbp
->clnt
= clntudp_create (&dbp
->addr
, NIS_PROG
, NIS_VERSION
,
213 UDPTIMEOUT
, &dbp
->socket
);
215 dbp
->clnt
= clnttcp_create (&dbp
->addr
, NIS_PROG
, NIS_VERSION
,
218 if (dbp
->clnt
== NULL
)
221 clnt_control (dbp
->clnt
, CLSET_TIMEOUT
, (caddr_t
) &RPCTIMEOUT
);
222 /* If the program exists, close the socket */
223 if (fcntl (dbp
->socket
, F_SETFD
, 1) == -1)
224 perror ("fcntl: F_SETFD");
228 if (serv
->key_type
== NIS_PK_DH
)
230 char netname
[MAXNETNAMELEN
+ 1];
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
))
241 authdes_pk_create (netname
, &serv
->pkey
, 300, NULL
, &ckey
);
242 if (!dbp
->clnt
->cl_auth
)
243 dbp
->clnt
->cl_auth
= authunix_create_default ();
246 dbp
->clnt
->cl_auth
= authunix_create_default ();
251 libnsl_hidden_def (__nisbind_connect
)
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
)
260 dbp
->server_len
= serv_len
;
261 dbp
->server_val
= (nis_server
*)serv_val
;
263 if (flags
& USE_DGRAM
)
266 dbp
->use_udp
= FALSE
;
268 if (flags
& NO_AUTHINFO
)
269 dbp
->use_auth
= FALSE
;
271 dbp
->use_auth
= TRUE
;
273 if (flags
& MASTER_ONLY
)
274 dbp
->master_only
= TRUE
;
276 dbp
->master_only
= FALSE
;
278 /* We try the first server */
282 if (server_used
== ~0)
284 if (__nis_findfastest (dbp
) < 1)
285 return NIS_NAMEUNREACHABLE
;
289 dbp
->server_used
= server_used
;
290 dbp
->current_ep
= current_ep
;
295 libnsl_hidden_def (__nisbind_create
)
297 /* __nisbind_connect (dbp) must be run before calling this function !
298 So we could use the same binding twice */
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
)
303 enum clnt_stat result
;
307 return NIS_NAMEUNREACHABLE
;
312 result
= clnt_call (dbp
->clnt
, prog
, xargs
, req
, xres
, resp
, RPCTIMEOUT
);
314 if (result
!= RPC_SUCCESS
)
315 retcode
= NIS_RPCERROR
;
321 if ((((nis_result
*)resp
)->status
== NIS_CBRESULTS
) &&
324 __nis_do_callback (dbp
, &((nis_result
*) resp
)->cookie
, cb
);
327 /* Yes, the missing break is correct. If we doesn't have to
328 start a callback, look if we have to search another server */
338 if (((nis_result
*)resp
)->status
== NIS_SYSTEMERROR
339 || ((nis_result
*)resp
)->status
== NIS_NOSUCHNAME
340 || ((nis_result
*)resp
)->status
== NIS_NOT_ME
)
343 if (__nisbind_next (dbp
) == NIS_SUCCESS
)
345 while (__nisbind_connect (dbp
) != NIS_SUCCESS
)
347 if (__nisbind_next (dbp
) != NIS_SUCCESS
)
352 break; /* No more servers to search in */
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
)
362 case NIS_DUMPLOG
: /* log_result */
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
)
372 retcode
= NIS_SUCCESS
;
375 while ((flags
& HARD_LOOKUP
) && retcode
== NIS_RPCERROR
);
379 libnsl_hidden_def (__do_niscall3
)
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
)
390 if (flags
& MASTER_ONLY
)
393 status
= __nisbind_create (&dbp
, server
, server_len
, ~0, ~0, flags
);
394 if (status
!= NIS_SUCCESS
)
397 while (__nisbind_connect (&dbp
) != NIS_SUCCESS
)
398 if (__nisbind_next (&dbp
) != NIS_SUCCESS
)
399 return NIS_NAMEUNREACHABLE
;
401 status
= __do_niscall3 (&dbp
, prog
, xargs
, req
, xres
, resp
, flags
, cb
);
403 __nisbind_destroy (&dbp
);
409 static directory_obj
*
410 rec_dirsearch (const_nis_name name
, directory_obj
*dir
, nis_error
*status
)
415 switch (nis_dir_cmp (name
, dir
->do_name
))
418 *status
= NIS_SUCCESS
;
421 /* NOT_SEQUENTIAL means, go one up and try it there ! */
423 { /* We need data from a parent domain */
425 char ndomain
[strlen (dir
->do_name
) + 3];
427 nis_domain_of_r (dir
->do_name
, ndomain
, sizeof (ndomain
));
429 /* The root server of our domain is a replica of the parent
430 domain ! (Now I understand why a root server must be a
431 replica of the parent domain) */
432 fd_res
= __nis_finddirectory (dir
, ndomain
);
435 nis_free_directory (dir
);
436 *status
= NIS_NOMEMORY
;
439 *status
= fd_res
->status
;
440 if (fd_res
->status
!= NIS_SUCCESS
)
442 /* Try the current directory obj, maybe it works */
443 __free_fdresult (fd_res
);
446 nis_free_directory (dir
);
447 obj
= calloc (1, sizeof (directory_obj
));
450 __free_fdresult (fd_res
);
451 *status
= NIS_NOMEMORY
;
454 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
455 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
456 _xdr_directory_obj (&xdrs
, obj
);
458 __free_fdresult (fd_res
);
460 /* We have found a NIS+ server serving ndomain, now
461 let us search for "name" */
462 return rec_dirsearch (name
, obj
, status
);
468 size_t namelen
= strlen (name
);
469 char leaf
[namelen
+ 3];
470 char domain
[namelen
+ 3];
471 char ndomain
[namelen
+ 3];
474 strcpy (domain
, name
);
478 if (domain
[0] == '\0')
480 nis_free_directory (dir
);
483 nis_leaf_of_r (domain
, leaf
, sizeof (leaf
));
484 nis_domain_of_r (domain
, ndomain
, sizeof (ndomain
));
485 strcpy (domain
, ndomain
);
487 while (nis_dir_cmp (domain
, dir
->do_name
) != SAME_NAME
);
489 cp
= rawmemchr (leaf
, '\0');
493 fd_res
= __nis_finddirectory (dir
, leaf
);
496 nis_free_directory (dir
);
497 *status
= NIS_NOMEMORY
;
500 *status
= fd_res
->status
;
501 if (fd_res
->status
!= NIS_SUCCESS
)
503 /* Try the current directory object, maybe it works */
504 __free_fdresult (fd_res
);
507 nis_free_directory (dir
);
508 obj
= calloc (1, sizeof(directory_obj
));
511 __free_fdresult (fd_res
);
512 *status
= NIS_NOMEMORY
;
515 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
516 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
517 _xdr_directory_obj (&xdrs
, obj
);
519 __free_fdresult (fd_res
);
520 /* We have found a NIS+ server serving ndomain, now
521 let us search for "name" */
522 return rec_dirsearch (name
, obj
, status
);
526 nis_free_directory (dir
);
527 *status
= NIS_BADNAME
;
530 nis_free_directory (dir
);
535 /* We try to query the current server for the searched object,
536 maybe he know about it ? */
537 static directory_obj
*
538 first_shoot (const_nis_name name
, int search_parent_first
, directory_obj
*dir
)
540 directory_obj
*obj
= NULL
;
543 char domain
[strlen (name
) + 3];
546 if (nis_dir_cmp (name
, dir
->do_name
) == SAME_NAME
)
550 const char *search_name
= name
;
551 if (search_parent_first
)
553 nis_domain_of_r (name
, domain
, sizeof (domain
));
554 search_name
= domain
;
557 if (nis_dir_cmp (search_name
, dir
->do_name
) == SAME_NAME
)
560 fd_res
= __nis_finddirectory (dir
, search_name
);
563 if (fd_res
->status
== NIS_SUCCESS
564 && (obj
= calloc (1, sizeof (directory_obj
))) != NULL
)
566 xdrmem_create (&xdrs
, fd_res
->dir_data
.dir_data_val
,
567 fd_res
->dir_data
.dir_data_len
, XDR_DECODE
);
568 _xdr_directory_obj (&xdrs
, obj
);
571 if (strcmp (dir
->do_name
, obj
->do_name
) != 0)
573 nis_free_directory (obj
);
578 __free_fdresult (fd_res
);
581 nis_free_directory (dir
);
586 static struct nis_server_cache
588 int search_parent_first
;
591 unsigned int server_used
;
592 unsigned int current_ep
;
595 } *nis_server_cache
[16];
596 static time_t nis_cold_start_mtime
;
597 __libc_lock_define_initialized (static, nis_server_cache_lock
)
599 static directory_obj
*
600 nis_server_cache_search (const_nis_name name
, int search_parent_first
,
601 unsigned int *server_used
, unsigned int *current_ep
,
604 directory_obj
*ret
= NULL
;
610 if (stat64 ("/var/nis/NIS_COLD_START", &st
) < 0)
611 st
.st_mtime
= nis_cold_start_mtime
+ 1;
613 __libc_lock_lock (nis_server_cache_lock
);
615 for (i
= 0; i
< 16; ++i
)
616 if (nis_server_cache
[i
] == NULL
)
618 else if (st
.st_mtime
!= nis_cold_start_mtime
619 || now
->tv_sec
> nis_server_cache
[i
]->expires
)
621 free (nis_server_cache
[i
]);
622 nis_server_cache
[i
] = NULL
;
624 else if (nis_server_cache
[i
]->search_parent_first
== search_parent_first
625 && strcmp (nis_server_cache
[i
]->name
, name
) == 0)
627 ret
= calloc (1, sizeof (directory_obj
));
631 addr
= rawmemchr (nis_server_cache
[i
]->name
, '\0') + 8;
632 addr
= (char *) ((uintptr_t) addr
& ~(uintptr_t) 7);
633 xdrmem_create (&xdrs
, addr
, nis_server_cache
[i
]->size
, XDR_DECODE
);
634 if (!_xdr_directory_obj (&xdrs
, ret
))
639 free (nis_server_cache
[i
]);
640 nis_server_cache
[i
] = NULL
;
644 *server_used
= nis_server_cache
[i
]->server_used
;
645 *current_ep
= nis_server_cache
[i
]->current_ep
;
649 nis_cold_start_mtime
= st
.st_mtime
;
651 __libc_lock_unlock (nis_server_cache_lock
);
656 nis_server_cache_add (const_nis_name name
, int search_parent_first
,
657 directory_obj
*dir
, unsigned int server_used
,
658 unsigned int current_ep
, struct timeval
*now
)
660 struct nis_server_cache
**loc
;
661 struct nis_server_cache
*new;
662 struct nis_server_cache
*old
;
671 size
= xdr_sizeof ((xdrproc_t
) _xdr_directory_obj
, (char *) dir
);
672 new = calloc (1, sizeof (*new) + strlen (name
) + 8 + size
);
675 new->search_parent_first
= search_parent_first
;
677 new->expires
= now
->tv_sec
+ dir
->do_ttl
;
679 new->server_used
= server_used
;
680 new->current_ep
= current_ep
;
681 addr
= stpcpy (new->name
, name
) + 8;
682 addr
= (char *) ((uintptr_t) addr
& ~(uintptr_t) 7);
684 xdrmem_create(&xdrs
, addr
, size
, XDR_ENCODE
);
685 if (!_xdr_directory_obj (&xdrs
, dir
))
693 __libc_lock_lock (nis_server_cache_lock
);
695 /* Choose which entry should be evicted from the cache. */
696 loc
= &nis_server_cache
[0];
698 for (i
= 1; i
< 16; ++i
)
699 if (nis_server_cache
[i
] == NULL
)
701 loc
= &nis_server_cache
[i
];
704 else if ((*loc
)->uses
> nis_server_cache
[i
]->uses
705 || ((*loc
)->uses
== nis_server_cache
[i
]->uses
706 && (*loc
)->expires
> nis_server_cache
[i
]->expires
))
707 loc
= &nis_server_cache
[i
];
711 __libc_lock_unlock (nis_server_cache_lock
);
716 __nisfind_server (const_nis_name name
, int search_parent_first
,
717 directory_obj
**dir
, dir_binding
*dbp
, unsigned int flags
)
719 nis_error result
= NIS_SUCCESS
;
723 unsigned int server_used
= ~0;
724 unsigned int current_ep
= ~0;
732 (void) gettimeofday (&now
, NULL
);
734 if ((flags
& NO_CACHE
) == 0)
735 *dir
= nis_server_cache_search (name
, search_parent_first
, &server_used
,
739 unsigned int server_len
= (*dir
)->do_servers
.do_servers_len
;
740 if (flags
& MASTER_ONLY
)
743 if (server_used
!= 0)
749 result
= __nisbind_create (dbp
, (*dir
)->do_servers
.do_servers_val
,
750 server_len
, server_used
, current_ep
, flags
);
751 if (result
!= NIS_SUCCESS
)
753 nis_free_directory (*dir
);
759 *dir
= readColdStartFile ();
761 /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
764 /* Try at first, if servers in "dir" know our object */
765 obj
= first_shoot (name
, search_parent_first
, *dir
);
768 obj
= rec_dirsearch (name
, *dir
, &status
);
773 if (result
== NIS_SUCCESS
)
775 unsigned int server_len
= obj
->do_servers
.do_servers_len
;
776 if (flags
& MASTER_ONLY
)
778 result
= __nisbind_create (dbp
, obj
->do_servers
.do_servers_val
,
779 server_len
, ~0, ~0, flags
);
780 if (result
== NIS_SUCCESS
)
782 if ((flags
& MASTER_ONLY
) == 0
783 || obj
->do_servers
.do_servers_len
== 1)
785 server_used
= dbp
->server_used
;
786 current_ep
= dbp
->current_ep
;
788 if ((flags
& NO_CACHE
) == 0)
789 nis_server_cache_add (name
, search_parent_first
, obj
,
790 server_used
, current_ep
, &now
);
794 nis_free_directory (obj
);
806 __prepare_niscall (const_nis_name name
, directory_obj
**dirp
,
807 dir_binding
*bptrp
, unsigned int flags
)
809 nis_error retcode
= __nisfind_server (name
, 1, dirp
, bptrp
, flags
);
810 if (__builtin_expect (retcode
!= NIS_SUCCESS
, 0))
814 if (__nisbind_connect (bptrp
) == NIS_SUCCESS
)
816 while (__nisbind_next (bptrp
) == NIS_SUCCESS
);
818 __nisbind_destroy (bptrp
);
819 memset (bptrp
, '\0', sizeof (*bptrp
));
821 retcode
= NIS_NAMEUNREACHABLE
;
822 nis_free_directory (*dirp
);
827 libnsl_hidden_def (__prepare_niscall
)
831 __do_niscall (const_nis_name name
, u_long prog
, xdrproc_t xargs
,
832 caddr_t req
, xdrproc_t xres
, caddr_t resp
, unsigned int flags
,
836 directory_obj
*dir
= NULL
;
837 int saved_errno
= errno
;
839 nis_error retcode
= __prepare_niscall (name
, &dir
, &bptr
, flags
);
840 if (retcode
== NIS_SUCCESS
)
842 retcode
= __do_niscall3 (&bptr
, prog
, xargs
, req
, xres
, resp
, flags
, cb
);
844 __nisbind_destroy (&bptr
);
846 nis_free_directory (dir
);
849 __set_errno (saved_errno
);