1dfb12944abe101e42c628d8120e5ba61982b33e
[kopensolaris-gnu/glibc.git] / nis / nis_call.c
1 /* Copyright (C) 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <fcntl.h>
21 #include <string.h>
22 #include <rpc/rpc.h>
23 #include <rpc/auth.h>
24 #include <rpcsvc/nis.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include "nis_intern.h"
29
30 static struct timeval TIMEOUT = {10, 0};
31
32 unsigned long
33 inetstr2int (const char *str)
34 {
35   char buffer[strlen (str) + 3];
36   size_t buflen;
37   size_t i, j;
38
39   buflen = stpcpy (buffer, str) - buffer;
40
41   j = 0;
42   for (i = 0; i < buflen; ++i)
43     if (buffer[i] == '.')
44       {
45         ++j;
46         if (j == 4)
47           {
48             buffer[i] = '\0';
49             break;
50           }
51       }
52
53   return inet_addr (buffer);
54 }
55
56 static void
57 __bind_destroy (dir_binding *bind)
58 {
59   if (bind->clnt != NULL)
60     {
61       if (bind->use_auth)
62         auth_destroy (bind->clnt->cl_auth);
63       clnt_destroy (bind->clnt);
64     }
65   free (bind->server_val);
66   free (bind);
67 }
68
69 static nis_error
70 __bind_next (dir_binding *bind)
71 {
72   u_int j;
73
74   if (bind->clnt != NULL)
75     {
76       if (bind->use_auth)
77         auth_destroy (bind->clnt->cl_auth);
78       clnt_destroy (bind->clnt);
79       bind->clnt = NULL;
80     }
81
82   if (bind->trys >= bind->server_len)
83     return NIS_FAIL;
84
85   for (j = bind->current_ep + 1;
86        j < bind->server_val[bind->server_used].ep.ep_len; ++j)
87     if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
88                 "inet") == 0)
89       if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
90                   "-") == 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 (j = bind->current_ep + 1;
102        j < bind->server_val[bind->server_used].ep.ep_len; ++j)
103     if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
104                 "inet") == 0)
105       if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
106                   "-") == 0)
107         {
108           bind->current_ep = j;
109           return NIS_SUCCESS;
110         }
111
112   return NIS_FAIL;
113 }
114
115 static nis_error
116 __bind_connect (dir_binding *dbp)
117 {
118   struct sockaddr_in check;
119   nis_server *serv;
120   int checklen;
121
122   if (dbp == NULL)
123     return NIS_FAIL;
124
125   serv = &dbp->server_val[dbp->server_used];
126
127   memset (&dbp->addr, '\0', sizeof (dbp->addr));
128   dbp->addr.sin_family = AF_INET;
129
130   dbp->addr.sin_addr.s_addr =
131     inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
132
133   if (dbp->addr.sin_addr.s_addr == 0)
134     return NIS_FAIL;
135
136   dbp->socket = RPC_ANYSOCK;
137   if (dbp->use_udp)
138     dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
139                                  TIMEOUT, &dbp->socket);
140   else
141     dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
142                                  &dbp->socket, 0, 0);
143
144   if (dbp->clnt == NULL)
145     return NIS_RPCERROR;
146
147   clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&TIMEOUT);
148   /* If the program exists, close the socket */
149   if (fcntl (dbp->socket, F_SETFD, 1) == -1)
150     perror (_("fcntl: F_SETFD"));
151
152   if (dbp->use_auth)
153     {
154 #if defined(HAVE_SECURE_RPC)
155       if (serv->key_type == NIS_PK_DH)
156         {
157           char netname[MAXNETNAMELEN+1];
158           char *p;
159
160           p = stpcpy (netname, "unix.");
161           strncpy (p, serv->name,MAXNETNAMELEN-5);
162           netname[MAXNETNAMELEN] = '\0';
163           p = strchr (netname, '.');
164           *p = '@';
165           dbp->clnt->cl_auth =
166             authdes_pk_create (netname, &serv->pkey, 300, NULL, NULL);
167           if (!dbp->clnt->cl_auth)
168             dbp->clnt->cl_auth = authunix_create_default ();
169         }
170       else
171 #endif
172         dbp->clnt->cl_auth = authunix_create_default ();
173       dbp->use_auth = TRUE;
174     }
175
176   /* Get port for sanity checks later */
177   checklen = sizeof (struct sockaddr_in);
178   memset (&check, 0, checklen);
179   if (dbp->use_udp)
180     bind (dbp->socket, (struct sockaddr *)&check, checklen);
181   check.sin_family = AF_INET;
182   if (!getsockname (dbp->socket, (struct sockaddr *)&check, &checklen))
183     dbp->port = check.sin_port;
184
185   dbp->create = time (NULL);
186
187   return NIS_SUCCESS;
188 }
189
190 static dir_binding *
191 __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags)
192 {
193   dir_binding *dbp;
194   u_int i;
195
196   dbp = calloc (1, sizeof (dir_binding));
197   if (dbp == NULL)
198     return NULL;
199
200   dbp->server_len = serv_len;
201   dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
202   if (dbp->server_val == NULL)
203     {
204       free (dbp);
205       return NULL;
206     }
207
208   if (flags & USE_DGRAM)
209     dbp->use_udp = TRUE;
210   else
211     dbp->use_udp = FALSE;
212
213   if (flags & NO_AUTHINFO)
214     dbp->use_auth = FALSE;
215   else
216     dbp->use_auth = TRUE;
217
218   if (flags & MASTER_ONLY)
219     dbp->master_only = TRUE;
220   else
221     dbp->master_only = FALSE;
222
223   dbp->trys = 1;
224
225   for (i = 0; i < serv_len; ++i)
226     {
227       if (serv_val[i].name != NULL)
228         dbp->server_val[i].name = strdup (serv_val[i].name);
229
230       dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
231       if (dbp->server_val[i].ep.ep_len > 0)
232         {
233           unsigned long j;
234
235           dbp->server_val[i].ep.ep_val =
236             malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
237           for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
238             {
239               if (serv_val[i].ep.ep_val[j].uaddr)
240                 dbp->server_val[i].ep.ep_val[j].uaddr =
241                   strdup (serv_val[i].ep.ep_val[j].uaddr);
242               else
243                 dbp->server_val[i].ep.ep_val[j].uaddr = NULL;
244               if (serv_val[i].ep.ep_val[j].family)
245                 dbp->server_val[i].ep.ep_val[j].family =
246                   strdup (serv_val[i].ep.ep_val[j].family);
247               else
248                 dbp->server_val[i].ep.ep_val[j].family = NULL;
249               if (serv_val[i].ep.ep_val[j].proto)
250                 dbp->server_val[i].ep.ep_val[j].proto =
251                   strdup (serv_val[i].ep.ep_val[j].proto);
252               else
253                 dbp->server_val[i].ep.ep_val[j].proto = NULL;
254             }
255         }
256       else
257         dbp->server_val[i].ep.ep_val = NULL;
258       dbp->server_val[i].key_type = serv_val[i].key_type;
259       dbp->server_val[i].pkey.n_len = serv_val[i].pkey.n_len;
260       if (serv_val[i].pkey.n_len > 0)
261         {
262           dbp->server_val[i].pkey.n_bytes =
263             malloc (serv_val[i].pkey.n_len);
264           if (dbp->server_val[i].pkey.n_bytes == NULL)
265             return NULL;
266           memcpy (dbp->server_val[i].pkey.n_bytes, serv_val[i].pkey.n_bytes,
267                   serv_val[i].pkey.n_len);
268         }
269       else
270         dbp->server_val[i].pkey.n_bytes = NULL;
271     }
272
273   if (__nis_findfastest (dbp) < 1)
274     {
275       __bind_destroy (dbp);
276       return NULL;
277     }
278
279   return dbp;
280 }
281
282 nis_error
283 __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
284                xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
285                u_long flags)
286 {
287   enum clnt_stat result;
288   nis_error retcode;
289   dir_binding *dbp;
290
291   if (flags & MASTER_ONLY)
292     server_len = 1;
293
294   if ((dbp = __bind_create (server, server_len, flags)) == NULL)
295     return NIS_UNAVAIL;
296   while (__bind_connect (dbp) != NIS_SUCCESS)
297     {
298       if (__bind_next (dbp) != NIS_SUCCESS)
299         {
300           __bind_destroy (dbp);
301           return NIS_NAMEUNREACHABLE;
302         }
303     }
304
305   do
306     {
307     again:
308       result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, TIMEOUT);
309
310       if (result != RPC_SUCCESS)
311         {
312           clnt_perror (dbp->clnt, "__do_niscall2: clnt_call");
313           __bind_destroy (dbp);
314           retcode = NIS_RPCERROR;
315         }
316       else
317         {
318           switch (prog)
319             {
320             case NIS_LOOKUP:
321             case NIS_ADD:
322             case NIS_MODIFY:
323             case NIS_REMOVE:
324             case NIS_IBLIST:
325             case NIS_IBADD:
326             case NIS_IBMODIFY:
327             case NIS_IBREMOVE:
328             case NIS_IBFIRST:
329             case NIS_IBNEXT:
330               if ((((nis_result *)xres)->status == NIS_NOTFOUND) ||
331                   (((nis_result *)xres)->status == NIS_NOSUCHNAME) ||
332                   (((nis_result *)xres)->status == NIS_NOT_ME))
333                 {
334                   if (__bind_next (dbp) == NIS_SUCCESS)
335                     while (__bind_connect (dbp) != NIS_SUCCESS)
336                       {
337                         if (__bind_next (dbp) != NIS_SUCCESS)
338                           {
339                             __bind_destroy (dbp);
340                             return NIS_SUCCESS;
341                           }
342                       }
343                   goto again;
344                 }
345             case NIS_FINDDIRECTORY:
346               if ((((fd_result *)xres)->status == NIS_NOTFOUND) ||
347                   (((fd_result *)xres)->status == NIS_NOSUCHNAME) ||
348                   (((fd_result *)xres)->status == NIS_NOT_ME))
349                 {
350                   if (__bind_next (dbp) == NIS_SUCCESS)
351                     while (__bind_connect (dbp) != NIS_SUCCESS)
352                       {
353                         if (__bind_next (dbp) != NIS_SUCCESS)
354                           {
355                             __bind_destroy (dbp);
356                             return NIS_SUCCESS;
357                           }
358                       }
359                   goto again;
360                 }
361               break;
362             case NIS_DUMPLOG: /* log_result */
363             case NIS_DUMP:
364               if ((((log_result *)xres)->lr_status == NIS_NOTFOUND) ||
365                   (((log_result *)xres)->lr_status == NIS_NOSUCHNAME) ||
366                   (((log_result *)xres)->lr_status == NIS_NOT_ME))
367                 {
368                   if (__bind_next (dbp) == NIS_SUCCESS)
369                     while (__bind_connect (dbp) != NIS_SUCCESS)
370                       {
371                         if (__bind_next (dbp) != NIS_SUCCESS)
372                           {
373                             __bind_destroy (dbp);
374                             return NIS_SUCCESS;
375                           }
376                       }
377                   goto again;
378                 }
379               break;
380             default:
381               break;
382             }
383           __bind_destroy (dbp);
384           retcode = NIS_SUCCESS;
385         }
386     }
387   while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
388
389   return retcode;
390 }
391
392 static directory_obj *
393 rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
394                nis_error *status)
395 {
396   fd_result *fd_res;
397   XDR xdrs;
398   char domain [strlen (name) + 3];
399
400   nis_domain_of_r (name, domain, sizeof (domain));
401   if (strncmp (domain, "org_dir.", 8) == 0)
402     {
403       char tmp[strlen (name) + 3];
404
405       nis_domain_of_r (domain, tmp, sizeof (tmp));
406       strcpy (domain, tmp);
407     }
408   else
409     if (strncmp (domain, "groups_dir.", 11) == 0)
410       {
411         char tmp[strlen (name) + 3];
412
413         nis_domain_of_r (domain, tmp, sizeof (tmp));
414         strcpy (domain, tmp);
415       }
416     else
417       {
418         /* We have no grous_dir or org_dir, so try the complete name */
419         strcpy (domain, name);
420       }
421
422   switch (nis_dir_cmp (domain, dir->do_name))
423     {
424     case SAME_NAME:
425       *status = NIS_SUCCESS;
426       return dir;
427     case NOT_SEQUENTIAL:
428       /* NOT_SEQUENTIAL means, go one up and try it there ! */
429     case HIGHER_NAME:
430       { /* We need data from a parent domain */
431         directory_obj *obj;
432         char ndomain [strlen (name) + 3];
433
434         nis_domain_of_r (dir->do_name, ndomain, sizeof (ndomain));
435
436         /* The root server of our domain is a replica of the parent
437            domain ! (Now I understand why a root server must be a
438            replica of the parent domain) */
439         fd_res = __nis_finddirectory (dir, ndomain);
440         *status = fd_res->status;
441         if (fd_res->status != NIS_SUCCESS)
442           {
443             nis_free_directory (dir);
444             xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
445             return NULL;
446           }
447         __cache_add (fd_res);
448         obj = calloc(1, sizeof(directory_obj));
449         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
450                       fd_res->dir_data.dir_data_len, XDR_DECODE);
451         xdr_directory_obj(&xdrs, obj);
452         xdr_destroy(&xdrs);
453         xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
454         if (obj != NULL)
455           {
456             /* We have found a NIS+ server serving ndomain, now
457                let us search for "name" */
458             nis_free_directory (dir);
459             return rec_dirsearch (name, obj, flags, status);
460           }
461         else
462           {
463             /* Ups, very bad. Are we already the root server ? */
464             nis_free_directory (dir);
465             return NULL;
466           }
467       }
468     break;
469     case LOWER_NAME:
470       {
471         directory_obj *obj;
472         char leaf [strlen (name) + 3];
473         char ndomain [strlen (name) + 3];
474         char *cp;
475
476         do
477           {
478             if (strlen (domain) == 0)
479               {
480                 nis_free_directory (dir);
481                 return NULL;
482               }
483             nis_leaf_of_r (domain, leaf, sizeof (leaf));
484             nis_domain_of_r (domain, ndomain, sizeof (ndomain));
485             strcpy (domain, ndomain);
486           }
487         while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
488         cp = strchr (leaf, '\0');
489         *cp++ = '.';
490         strcpy (cp, domain);
491
492         fd_res = __nis_finddirectory (dir, leaf);
493         *status = fd_res->status;
494         if (fd_res->status != NIS_SUCCESS)
495           {
496             nis_free_directory (dir);
497             xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
498             return NULL;
499           }
500         __cache_add (fd_res);
501         obj = calloc(1, sizeof(directory_obj));
502         xdrmem_create(&xdrs, fd_res->dir_data.dir_data_val,
503                       fd_res->dir_data.dir_data_len, XDR_DECODE);
504         xdr_directory_obj(&xdrs, obj);
505         xdr_destroy(&xdrs);
506         xdr_free((xdrproc_t)xdr_fd_result, (caddr_t)fd_res);
507         if (obj != NULL)
508           {
509             /* We have found a NIS+ server serving ndomain, now
510                let us search for "name" */
511             nis_free_directory (dir);
512             return rec_dirsearch (name, obj, flags, status);
513           }
514       }
515     break;
516     case BAD_NAME:
517       nis_free_directory (dir);
518       *status = NIS_BADNAME;
519       return NULL;
520     }
521   nis_free_directory (dir);
522   *status = NIS_FAIL;
523   return NULL;
524 }
525
526 nis_error
527 __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
528               caddr_t req, xdrproc_t xres, caddr_t resp, u_long flags)
529 {
530   nis_error retcode;
531   directory_obj *dir = NULL;
532   nis_server *server;
533   u_int server_len;
534
535   if (name == NULL)
536     return NIS_BADNAME;
537
538   if ((flags & NO_CACHE) !=  NO_CACHE)
539     dir = __cache_search (name);
540
541   if (dir == NULL)
542     {
543       nis_error status;
544       dir = readColdStartFile ();
545       if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
546         return NIS_UNAVAIL;
547
548       dir = rec_dirsearch (name, dir, flags, &status);
549       if (dir == NULL)
550         return status;
551     }
552
553   if (flags & MASTER_ONLY)
554     {
555       server = dir->do_servers.do_servers_val;
556       server_len = 1;
557     }
558   else
559     {
560       server = dir->do_servers.do_servers_val;
561       server_len = dir->do_servers.do_servers_len;
562     }
563
564
565   retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
566                            flags);
567
568   nis_free_directory (dir);
569
570   return retcode;
571 }