source: trunk/athena/bin/attach/rpc.c @ 9701

Revision 9701, 8.9 KB checked in by ghudson, 27 years ago (diff)
Remove code to bind to a reserved port; clntudp_create() does that for us. This fixes a bug on Solaris because clntudp_create() was passing back something that wasn't a socket and bind() was having unpredictable results.
Line 
1/*      Created by:     Robert French
2 *
3 *      $Source: /afs/dev.mit.edu/source/repository/athena/bin/attach/rpc.c,v $
4 *      $Author: ghudson $
5 *
6 *      Copyright (c) 1988 by the Massachusetts Institute of Technology.
7 */
8
9static char *rcsid_rpc_c = "$Header: /afs/dev.mit.edu/source/repository/athena/bin/attach/rpc.c,v 1.15 1997-03-06 00:52:12 ghudson Exp $";
10
11#include "attach.h"
12#ifdef NFS
13#include <krb.h>
14#if defined(_AIX) && (AIXV < 30)
15#include <rpc/rpcmount.h>
16#include <rpc/nfsmount.h>
17#endif
18#include <rpcsvc/mount.h>
19
20#if defined(SOLARIS)
21#include <limits.h>
22#define NGROUPS NGROUPS_MAX
23#endif
24
25extern bool_t   xdr_void();
26extern char     *krb_getrealm();
27
28static struct cache_ent rpc_cache[RPC_MAXCACHE];
29static int first_free = 0;
30
31/*
32 * XDR for sending a Kerberos ticket - sends the whole KTEXT block,
33 * but this is very old lossage, and nothing that can really be fixed
34 * now.
35 */
36
37bool_t xdr_krbtkt(xdrs, authp)
38    XDR *xdrs;
39    KTEXT authp;
40{
41    KTEXT_ST auth;
42
43    auth = *authp;
44    auth.length = htonl(authp->length);
45    return xdr_opaque(xdrs, &auth, sizeof(KTEXT_ST));
46}
47
48/*
49 * Maintain a cache of RPC handles and error status.
50 */
51
52struct cache_ent *lookup_cache_ent(addr)
53    struct in_addr addr;
54{
55    int i;
56
57    for (i=0;i<first_free;i++)
58        if (!memcmp(&addr, &rpc_cache[i].addr, sizeof(addr)))
59            return (&rpc_cache[i]);
60
61    return (NULL);
62}
63
64add_cache_ent(addr, handle, fd, sin)
65    struct in_addr addr;
66    CLIENT *handle;
67    int fd;
68    struct sockaddr_in *sin;
69{
70    int ind;
71
72    /* We lose a tiny bit of efficiency if we overflow the cache */
73    if (first_free == RPC_MAXCACHE) {
74        ind = 0;
75        clnt_destroy(rpc_cache[0].handle);
76        close(rpc_cache[0].fd);
77    }
78    else
79        ind = first_free++;
80    rpc_cache[ind].addr = addr;
81    rpc_cache[ind].handle = handle;
82    rpc_cache[ind].fd = fd;
83    rpc_cache[ind].sin = *sin;
84    rpc_cache[ind].error = 0;
85}
86
87errored_out(addr)
88    struct in_addr addr;
89{
90    struct cache_ent *ent;
91
92    if (ent = lookup_cache_ent(addr))
93        return (ent->error);
94    return (0);
95}
96
97mark_errored(addr)
98    struct in_addr addr;
99{
100    struct cache_ent *ent;
101
102    if (ent = lookup_cache_ent(addr))
103        ent->error = 1;
104}
105
106clear_errored(addr)
107    struct in_addr addr;
108{
109    struct cache_ent *ent;
110
111    if (ent = lookup_cache_ent(addr))
112        ent->error = 0;
113}
114
115/*
116 * Create an RPC handle for the given address.  Attempt to use one
117 * already cached if possible.
118 */
119
120CLIENT *rpc_create(addr, sinp)
121    struct in_addr addr;
122    struct sockaddr_in *sinp;
123{
124    CLIENT *client;
125    struct sockaddr_in sin;
126    struct timeval timeout;
127    struct cache_ent *ent;
128    int s;
129    u_short port;
130
131    if (debug_flag)
132        printf("Getting RPC handle for %s\n", inet_ntoa(addr));
133
134    if (ent = lookup_cache_ent(addr)) {
135        if (debug_flag)
136            printf("Using cached entry! FD = %d Error = %d\n", ent->fd,
137                   ent->error);
138        if (ent->error)
139            /* Error status will already be set */
140            return ((CLIENT *)0);
141        else {
142            *sinp = ent->sin;
143            return (ent->handle);
144        }
145    }
146   
147    sin.sin_family = AF_INET;
148    sin.sin_addr = addr;
149    sin.sin_port = 0;
150    s = RPC_ANYSOCK;
151    timeout.tv_usec = 0;
152    timeout.tv_sec = 20;
153
154    if (!(client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS,
155                                  timeout, &s))) {
156        if (debug_flag)
157            printf("clntudp_create failed!\n");
158        add_cache_ent(addr, 0, 0, sinp);
159        mark_errored(addr);
160        error_status = ERR_HOST;
161        return (client);
162    }
163
164    *sinp = sin;
165   
166    add_cache_ent(addr, client, s, sinp);
167    if (debug_flag)
168        printf("Adding cache entry with FD = %d\n", s);
169
170    return (client);
171}
172
173/*
174 * spoofunix_create_default -- varient of authunix_create_default, with
175 *      a neat trick....
176 *
177 * Written by Mark W. Eichin <eichin@athena.mit.edu>
178 */
179
180/* In our case, NGRPS (for RPC) is 8, but NGROUPS (kernel) is 16.  So
181 * getgroups fails causing abort() if the user is in >8 groups.  We
182 * ask for all groups, but only pass on the first 8. */
183
184AUTH *
185spoofunix_create_default(spoofname, uid)
186        char *spoofname;
187        int uid;
188{
189        register int len;
190        char machname[MAX_MACHINE_NAME + 1];
191        register int gid;
192        int gids[NGROUPS];
193
194        if (spoofname)
195                (void) strncpy(machname, spoofname, MAX_MACHINE_NAME);
196        else
197                if (gethostname(machname, MAX_MACHINE_NAME) == -1) {
198                        perror("gethostname");
199                        abort();
200                }
201        machname[MAX_MACHINE_NAME] = 0;
202        gid = getegid();
203        if ((len = getgroups(NGROUPS,gids)) < 0) {
204                fprintf(stderr,"%s: Fatal error: User in too many groups!\n",
205                        progname);
206                exit(1);
207        }
208        return (authunix_create(machname, uid, gid,
209                                (len > NGRPS) ? NGRPS : len, gids));
210}
211
212/*
213 * Perform a mapping operation of some kind on the indicated host.  If
214 * errorout is zero, don't print error messages.  errname is the name
215 * of the filesystem/host/whatever to prefix error message with.
216 */
217
218int nfsid(host, addr, op, errorout, errname, inattach, uid)
219    const char *host;
220    struct in_addr addr;
221    int op, errorout;
222    const char *errname;
223    int inattach, uid;
224{
225    CLIENT *client;
226    KTEXT_ST authent;
227    int status;
228    struct timeval timeout;
229    struct sockaddr_in sin;
230    enum clnt_stat rpc_stat;
231    char instance[INST_SZ+REALM_SZ+1];
232    char *realm;
233    char *ptr;
234
235    if (debug_flag)
236        printf("nfsid operation on %s, op = %d\n", inet_ntoa(addr), op);
237   
238    /*
239     * Check for previous error condition on this host
240     */
241    if (errored_out(addr)) {
242        if (debug_flag)
243            printf("Host previously errored - ignoring\n");
244        if (errorout)
245            fprintf(stderr, "%s: Ignoring host %s for filesystem %s due to previous host errors\n",
246                    progname, host, errname);
247        /*
248         * Don't bother setting error status...we already will have
249         * before
250         */
251        return (FAILURE);
252    }
253
254#ifdef KERBEROS
255    /*
256     * Mapping and user purging are the only authenticated functions...
257     */
258    if (op == MOUNTPROC_KUIDMAP || op == MOUNTPROC_KUIDUPURGE) {
259        if (debug_flag)
260            printf("nfsid operation requiring authentication...op = %d\n",
261                   op);
262        realm = (char *) krb_realmofhost(host);
263        strcpy(instance, host);
264        ptr = strchr(instance, '.');
265        if (ptr)
266            *ptr = '\0';
267        for (ptr=instance;*ptr;ptr++)
268            if (isupper(*ptr))
269                *ptr = tolower(*ptr);
270
271        if (debug_flag)
272            printf("krb_mk_req for instance %s, realm %s\n", instance, realm);
273
274        status = krb_mk_req(&authent, KERB_NFSID_INST, instance,
275                           realm, 0);
276        if (status != KSUCCESS) {
277            if (debug_flag)
278                printf("krb_mk_req failed! status = %d\n", status);
279            if (status == KDC_PR_UNKNOWN) {
280              if(inattach) {
281                fprintf(stderr,
282                        "%s: (warning) Host %s isn't registered with kerberos\n",
283                        progname, host);
284                fprintf(stderr, "\tmapping failed for filesystem %s.\n",
285                        errname);
286                return(SUCCESS);
287              } else {
288                fprintf(stderr, "%s: Host %s isn't registered with kerberos\n",
289                        progname, host);
290              }
291            } else {
292              if (errorout)
293                {
294                  fprintf(stderr, "%s: Could not get Kerberos ticket (%s.%s@%s)\n\tfor filesystem %s, kerberos error is: %s\n",
295                          progname, KERB_NFSID_INST, instance, realm,
296                          errname, krb_err_txt[status]);
297                }
298            }
299            error_status = ERR_KERBEROS;
300            return (FAILURE);
301        }
302    }
303#endif
304
305    /*
306     * Get an RPC handle
307     */
308    if ((client = rpc_create(addr, &sin)) == NULL) {
309        if (errorout)
310            fprintf(stderr, "%s: server %s not responding (for filesystem %s)\n",
311                    progname, host, errname);
312        /*
313         * Error status set by rpc_create
314         */
315        return (FAILURE);
316    }
317
318    client->cl_auth = spoofunix_create_default(spoofhost, uid);
319       
320    timeout.tv_usec = 0;
321    timeout.tv_sec = 20;
322
323    if (op == MOUNTPROC_KUIDMAP || op == MOUNTPROC_KUIDUPURGE)
324        rpc_stat = clnt_call(client, op, xdr_krbtkt, &authent,
325                             xdr_void, 0, timeout);
326    else
327        rpc_stat = clnt_call(client, op, xdr_void, 0, xdr_void,
328                             0, timeout);
329    if (rpc_stat != RPC_SUCCESS) {
330        if (errorout) {
331            switch (rpc_stat) {
332            case RPC_TIMEDOUT:
333                fprintf(stderr, "%s: timeout while contacting mount daemon on %s (for filesystem %s)\n",
334                        progname, host, errname);
335                if (inattach)
336                    fprintf(stderr, "\twhile mapping - try attach -n\n",
337                            errname);
338                error_status = ERR_HOST;
339                break;
340            case RPC_AUTHERROR:
341                fprintf(stderr, "%s: Authentication failed to host %s for filesystem %s\n",
342                        progname, host, errname);
343                error_status = ERR_AUTHFAIL;
344                break;
345            case RPC_PMAPFAILURE:
346                fprintf(stderr, "%s: Can't find mount daemon on %s for filesystem %s\n",
347                        progname, host, errname);
348                error_status = ERR_HOST;
349                break;
350            case RPC_PROGUNAVAIL:
351            case RPC_PROGNOTREGISTERED:
352                fprintf(stderr, "%s: Mount daemon not available on %s (filesystem %s)\n",
353                        progname, host, errname);
354                error_status = ERR_HOST;
355                break;
356            case RPC_PROCUNAVAIL:
357                fprintf(stderr, "%s: Warning: mount daemon on %s doesn't understand UID maps\n\t(filesystem %s)\n",
358                       progname, host, errname);
359                return(SUCCESS);
360            default:
361                fprintf(stderr, "%s: System error contacting server %s for filesystem %s\n",
362                        progname, host, errname);
363                error_status = ERR_HOST;
364                break;
365            }
366        }
367        mark_errored(addr);
368        if (debug_flag)
369            clnt_perror(client, "RPC return status");
370        return (FAILURE);
371    }
372    return (SUCCESS);
373}
374#endif
Note: See TracBrowser for help on using the repository browser.