/* * Create a credential that we can send to the X server. */ static int auth_ezencode( char *servername, int window, char *cred_out, int *len) { AUTH *a; XDR xdr; #if defined(SVR4) && defined(sun) a = authdes_seccreate(servername, window, NULL, NULL); #else a = (AUTH *)authdes_create(servername, window, NULL, NULL); #endif if (a == (AUTH *)NULL) { perror("auth_create"); return 0; } xdrmem_create(&xdr, cred_out, *len, XDR_ENCODE); if (AUTH_MARSHALL(a, &xdr) == FALSE) { perror("auth_marshall"); AUTH_DESTROY(a); return 0; } *len = xdr_getpos(&xdr); AUTH_DESTROY(a); return 1; }
/* Dispose a channel. */ void nfs_rpc_destroy_chan(rpc_call_channel_t *chan) { assert(chan); /* XXX lock, wait for outstanding calls, etc */ switch (chan->type) { case RPC_CHAN_V40: /* channel has a dedicated RPC client */ if (chan->clnt) { /* clean up auth, if any */ if (chan->auth) { AUTH_DESTROY(chan->auth); chan->auth = NULL; } /* destroy it */ clnt_destroy(chan->clnt); chan->clnt = NULL; } break; case RPC_CHAN_V41: /* XXX channel is shared */ break; } chan->clnt = NULL; chan->last_called = 0; }
/* * Call the NFSv4 client's CB_NULL procedure. */ enum clnt_stat rpc_cb_null(rpc_call_channel_t *chan, struct timeval timeout) { enum clnt_stat stat = RPC_SUCCESS; /* XXX TI-RPC does the signal masking */ pthread_mutex_lock(&chan->mtx); if (! chan->clnt) { stat = RPC_INTR; goto unlock; } stat = clnt_call(chan->clnt, chan->auth, CB_NULL, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, timeout); /* If a call fails, we have to assume path down, or equally fatal * error. We may need back-off. */ if (stat != RPC_SUCCESS) { if (chan->clnt) { if (chan->auth) { AUTH_DESTROY(chan->auth); chan->auth = NULL; } clnt_destroy(chan->clnt); chan->clnt = NULL; } } unlock: pthread_mutex_unlock(&chan->mtx); return (stat); }
kadm5_ret_t kadm5_destroy(void *server_handle) { krb5_ccache ccache = NULL; int code = KADM5_OK; kadm5_server_handle_t handle = (kadm5_server_handle_t) server_handle; CHECK_HANDLE(server_handle); if (handle->destroy_cache && handle->cache_name) { if ((code = krb5_cc_resolve(handle->context, handle->cache_name, &ccache)) == 0) code = krb5_cc_destroy (handle->context, ccache); } if (handle->cache_name) free(handle->cache_name); if (handle->clnt && handle->clnt->cl_auth) AUTH_DESTROY(handle->clnt->cl_auth); if (handle->clnt) clnt_destroy(handle->clnt); if (handle->client_socket != -1) close(handle->client_socket); if (handle->lhandle) free (handle->lhandle); kadm5_free_config_params(handle->context, &handle->params); handle->magic_number = 0; free(handle); return code; }
void nsm_disconnect() { if (nsm_count == 0 && nsm_clnt != NULL) { gsh_clnt_destroy(nsm_clnt); nsm_clnt = NULL; AUTH_DESTROY(nsm_auth); nsm_auth = NULL; gsh_free(nodename); nodename = NULL; } }
enum clnt_stat clnt_broadcast(u_long prog, /* program number */ u_long vers, /* version number */ u_long proc, /* procedure number */ xdrproc_t xargs, /* xdr routine for args */ caddr_t argsp, /* pointer to args */ xdrproc_t xresults, /* xdr routine for results */ caddr_t resultsp, /* pointer to results */ resultproc_t eachresult) /* call with each result obtained */ { enum clnt_stat stat; AUTH *unix_auth; XDR xdr_stream; XDR *xdrs = &xdr_stream; int outlen, inlen, nets; socklen_t fromlen; int sock = -1; int on = 1; struct pollfd pfd[1]; int i; int timo; bool_t done = FALSE; u_long xid; u_long port; struct in_addr *addrs = NULL; struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ struct rmtcallargs a; struct rmtcallres r; struct rpc_msg msg; char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; if ((unix_auth = authunix_create_default()) == NULL) { stat = RPC_AUTHERROR; goto done_broad; } /* * initialization: create a socket, a broadcast address, and * preserialize the arguments into a send buffer. */ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { stat = RPC_CANTSEND; goto done_broad; } #ifdef SO_BROADCAST if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { stat = RPC_CANTSEND; goto done_broad; } #endif /* def SO_BROADCAST */ pfd[0].fd = sock; pfd[0].events = POLLIN; nets = newgetbroadcastnets(&addrs); if (nets == 0) { stat = RPC_CANTSEND; goto done_broad; } memset(&baddr, 0, sizeof (baddr)); baddr.sin_len = sizeof(struct sockaddr_in); baddr.sin_family = AF_INET; baddr.sin_port = htons(PMAPPORT); baddr.sin_addr.s_addr = htonl(INADDR_ANY); msg.rm_xid = xid = arc4random(); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; msg.rm_call.cb_cred = unix_auth->ah_cred; msg.rm_call.cb_verf = unix_auth->ah_verf; a.prog = prog; a.vers = vers; a.proc = proc; a.xdr_args = xargs; a.args_ptr = argsp; r.port_ptr = &port; r.xdr_results = xresults; r.results_ptr = resultsp; xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); if (!xdr_callmsg(xdrs, &msg) || !xdr_rmtcall_args(xdrs, &a)) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); /* * Basic loop: broadcast a packet and wait a while for response(s). * The response timeout grows larger per iteration. * * XXX This will loop about 5 times the stop. If there are * lots of signals being received by the process it will quit * send them all in one quick burst, not paying attention to * the intended function of sending them slowly over half a * minute or so */ for (timo = 4000; timo <= 14000; timo += 2000) { for (i = 0; i < nets; i++) { baddr.sin_addr = addrs[i]; if (sendto(sock, outbuf, outlen, 0, (struct sockaddr *)&baddr, sizeof (struct sockaddr)) != outlen) { stat = RPC_CANTSEND; goto done_broad; } } if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)&r; msg.acpted_rply.ar_results.proc = xdr_rmtcallres; switch (poll(pfd, 1, timo)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case 1: if (pfd[0].revents & POLLNVAL) errno = EBADF; else if (pfd[0].revents & POLLERR) errno = EIO; else break; /* FALLTHROUGH */ case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; stat = RPC_CANTRECV; goto done_broad; } try_again: fromlen = sizeof(struct sockaddr); inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, (struct sockaddr *)&raddr, &fromlen); if (inlen < 0) { if (errno == EINTR) goto try_again; stat = RPC_CANTRECV; goto done_broad; } if (inlen < sizeof(u_int32_t)) goto recv_again; /* * see if reply transaction id matches sent id. * If so, decode the results. */ xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_xid == xid) && (msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { raddr.sin_port = htons((u_short)port); done = (*eachresult)(resultsp, &raddr); } /* otherwise, we just ignore the errors ... */ } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = xdr_void; (void)xdr_replymsg(xdrs, &msg); (void)(*xresults)(xdrs, resultsp); xdr_destroy(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } done_broad: if (addrs) free(addrs); if (sock >= 0) (void)close(sock); if (unix_auth != NULL) AUTH_DESTROY(unix_auth); return (stat); }
enum clnt_stat clnt_broadcast( unsigned long prog, /* program number */ unsigned long vers, /* version number */ unsigned long proc, /* procedure number */ xdrproc_t xargs, /* xdr routine for args */ char* argsp, /* pointer to args */ xdrproc_t xresults, /* xdr routine for results */ char* resultsp, /* pointer to results */ resultproc_t eachresult) /* call with each result obtained */ { enum clnt_stat stat; AUTH *unix_auth = authunix_create_default(); XDR xdr_stream; register XDR *xdrs = &xdr_stream; int outlen, nets; ssize_t inlen; socklen_t fromlen; register int sock; int on = 1; #ifdef FD_SETSIZE fd_set mask; fd_set readfds; #else int readfds; register int mask; #endif /* def FD_SETSIZE */ register int i; bool_t done = FALSE; register unsigned long xid; unsigned long port; struct in_addr addrs[20]; struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ struct rmtcallargs a; struct rmtcallres r; struct rpc_msg msg; struct timeval t; char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; /* * initialization: create a socket, a broadcast address, and * preserialize the arguments into a send buffer. */ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("Cannot create socket for broadcast rpc"); stat = RPC_CANTSEND; goto done_broad; } #ifdef SO_BROADCAST if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { perror("Cannot set socket option SO_BROADCAST"); stat = RPC_CANTSEND; goto done_broad; } #endif /* def SO_BROADCAST */ #ifdef FD_SETSIZE FD_ZERO(&mask); FD_SET(sock, &mask); #else mask = (1 << sock); #endif /* def FD_SETSIZE */ nets = getbroadcastnets(addrs, sock, inbuf); bzero((char *)&baddr, sizeof (baddr)); baddr.sin_family = AF_INET; baddr.sin_port = htons(PMAPPORT); baddr.sin_addr.s_addr = htonl(INADDR_ANY); /* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */ (void)gettimeofday(&t, NULL); msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec; t.tv_usec = 0; msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; msg.rm_call.cb_cred = unix_auth->ah_cred; msg.rm_call.cb_verf = unix_auth->ah_verf; a.prog = prog; a.vers = vers; a.proc = proc; a.xdr_args = xargs; a.args_ptr = argsp; r.port_ptr = &port; r.xdr_results = xresults; r.results_ptr = resultsp; xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); /* * Basic loop: broadcast a packet and wait a while for response(s). * The response timeout grows larger per iteration. */ for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) { for (i = 0; i < nets; i++) { baddr.sin_addr = addrs[i]; if (sendto(sock, outbuf, outlen, 0, (struct sockaddr *)&baddr, sizeof (struct sockaddr)) != (ssize_t)outlen) { perror("Cannot send broadcast packet"); stat = RPC_CANTSEND; goto done_broad; } } if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (char*)&r; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; readfds = mask; { struct timeval tmp = t; switch (select(sock+1, &readfds, NULL, NULL, &tmp)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; perror("Broadcast select problem"); stat = RPC_CANTRECV; goto done_broad; } /* end of select results switch */ } /* end of temporary timeout variable */ try_again: fromlen = (socklen_t)sizeof(struct sockaddr); inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, (struct sockaddr *)&raddr, &fromlen); if (inlen < 0) { if (errno == EINTR) goto try_again; perror("Cannot receive reply to broadcast"); stat = RPC_CANTRECV; goto done_broad; } if (inlen < sizeof(uint32_t)) goto recv_again; /* * see if reply transaction id matches sent id. * If so, decode the results. */ xdrmem_create(xdrs, inbuf, (unsigned)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_xid == xid) && (msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { raddr.sin_port = htons((unsigned short)port); done = (*eachresult)(resultsp, &raddr); } /* otherwise, we just ignore the errors ... */ } else { #ifdef notdef /* some kind of deserialization problem ... */ if (msg.rm_xid == xid) (void)fprintf(stderr, "Broadcast deserialization problem"); /* otherwise, just random garbage */ #endif } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; (void)xdr_replymsg(xdrs, &msg); (void)(*xresults)(xdrs, resultsp); xdr_destroy(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } done_broad: (void)close(sock); AUTH_DESTROY(unix_auth); return (stat); }
/* * This routine will open a device as it is known by the V2 OBP. It * then goes thru the stuff necessary to initialize the network device, * get our network parameters, (using DHCP or rarp/bootparams), and * finally actually go and get the root filehandle. Sound like fun? * Suuurrrree. Take a look. * * Returns 0 if things worked. -1 if we crashed and burned. */ int boot_nfs_mountroot(char *str) { int status; enum clnt_stat rpc_stat; char *root_path = &root_pathbuf[0]; /* to make XDR happy */ struct timeval wait; int fd; int bufsize; char *opts, *val; int nfs_version = 0; int istcp = 1; int nfs_port = 0; /* Cause pmap to get port */ struct sockaddr_in tmp_addr; /* throw away */ if (root_CLIENT != NULL) { AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; } root_to.sin_family = AF_INET; root_to.sin_addr.s_addr = htonl(INADDR_ANY); root_to.sin_port = htons(0); mac_init(str); (void) ipv4_setpromiscuous(TRUE); if (get_netconfig_strategy() == NCT_BOOTP_DHCP) { if (boothowto & RB_VERBOSE) printf("Using BOOTP/DHCP...\n"); if (dhcp() != 0 || setup_root_vars() != 0) { (void) ipv4_setpromiscuous(FALSE); if (boothowto & RB_VERBOSE) printf("BOOTP/DHCP configuration failed!\n"); return (-1); } /* now that we have an IP address, turn off promiscuous mode */ (void) ipv4_setpromiscuous(FALSE); } else { /* Use RARP/BOOTPARAMS. RARP will try forever... */ if (boothowto & RB_VERBOSE) printf("Using RARP/BOOTPARAMS...\n"); mac_call_rarp(); /* * Since there is no way to determine our netmask, and therefore * figure out if the router we got is useful, we assume all * services are local. Use DHCP if this bothers you. */ dontroute = TRUE; /* * We are trying to keep the ARP response * timeout on the lower side with BOOTP/RARP. * We are doing this for BOOTP/RARP where policy * doesn't allow to route the packets outside * the subnet as it has no idea about the * netmask. By doing so, we are reducing * ARP response timeout for any packet destined * for outside booting clients subnet. Client can * not expect such ARP replies and will finally * timeout after a long delay. This would cause * booting client to get stalled for a longer * time. We can not avoid accepting any outside * subnet packets accidentally destined for the * booting client. */ mac_set_arp_timeout(ARP_INETBOOT_TIMEOUT); /* now that we have an IP address, turn off promiscuous mode */ (void) ipv4_setpromiscuous(FALSE); /* get our hostname */ if (whoami() == FALSE) return (-1); /* get our bootparams. */ if (getfile("root", root_hostname, &root_to.sin_addr, root_pathbuf) == FALSE) return (-1); /* get our rootopts. */ (void) getfile("rootopts", root_hostname, &tmp_addr.sin_addr, rootopts); } /* mount root */ if (boothowto & RB_VERBOSE) { printf("root server: %s (%s)\n", root_hostname, inet_ntoa(root_to.sin_addr)); printf("root directory: %s\n", root_pathbuf); } /* * Assumes we've configured the stack and thus know our * IP address/hostname, either by using DHCP or rarp/bootparams. */ gethostname(my_hostname, sizeof (my_hostname)); wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; wait.tv_usec = 0; /* * Parse out the interesting root options, if an invalid * or unknown option is provided, silently ignore it and * use the defaults. */ opts = rootopts; while (*opts) { int ival; switch (getsubopt(&opts, optlist, &val)) { case OPT_RSIZE: if (val == NULL || !isdigit(*val)) break; nfs_readsize = atoi(val); break; case OPT_TIMEO: if (val == NULL || !isdigit(*val)) break; ival = atoi(val); wait.tv_sec = ival / 10; wait.tv_usec = (ival % 10) * 100000; break; case OPT_VERS: if (val == NULL || !isdigit(*val)) break; nfs_version = atoi(val); break; case OPT_PROTO: if (val == NULL || isdigit(*val)) break; if ((strncmp(val, "udp", 3) == 0)) istcp = 0; else istcp = 1; /* must be tcp */ break; case OPT_PORT: if (val == NULL || !isdigit(*val)) break; nfs_port = atoi(val); /* * Currently nfs_dlinet.c doesn't support setting * the root NFS port. Delete this when it does. */ nfs_port = 0; break; default: /* * Unknown options are silently ignored */ break; } } /* * If version is set, then try that version first. */ switch (nfs_version) { case NFS_VERSION: if (nfsmountroot(root_path, &roothandle) == 0) goto domount; break; case NFS_V3: if (nfs3mountroot(root_path, &roothandle) == 0) goto domount; break; case NFS_V4: /* * With v4 we skip the mount and go straight to * setting the root filehandle. Because of this we * do things slightly differently and obtain our * client handle first. */ if (istcp && nfs4init(root_path, nfs_port) == 0) { /* * If v4 init succeeded then we are done. Just return. */ return (0); } } /* * If there was no chosen version or the chosen version failed * try all versions in order, this may still fail to boot * at the kernel level if the options are not right, but be * generous at this early stage. */ if (istcp && nfs4init(root_path, nfs_port) == 0) { /* * If v4 init succeeded then we are done. Just return. */ return (0); } if (nfs3mountroot(root_path, &roothandle) == 0) goto domount; if ((status = nfsmountroot(root_path, &roothandle)) != 0) return (status); domount: /* * Only v2 and v3 go on from here. */ roothandle.offset = (uint_t)0; /* it's a directory! */ root_to.sin_port = htons(nfs_port); /* NFS is next after mount */ /* * Create the CLIENT handle for NFS operations */ if (roothandle.version == NFS_VERSION) bufsize = NFSBUF_SIZE; else bufsize = NFS3BUF_SIZE; /* * First try TCP then UDP (unless UDP asked for explicitly), if mountd * alows this version but neither transport is available we are stuck. */ if (istcp) { fd = -1; root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, roothandle.version, wait, &fd, bufsize, bufsize); if (root_CLIENT != NULL) { root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc, check if the server really exists */ rpc_stat = CLNT_CALL(root_CLIENT, 0, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat == RPC_SUCCESS) return (0); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; } /* Fall through to UDP case */ } fd = -1; root_CLIENT = clntbudp_bufcreate(&root_to, NFS_PROGRAM, roothandle.version, wait, &fd, bufsize, bufsize); if (root_CLIENT == NULL) return (-1); root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc, check if the server really exists */ rpc_stat = CLNT_CALL(root_CLIENT, 0, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat == RPC_SUCCESS) return (0); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_CLIENT = NULL; return (-1); }
/* * Setup v4 client for inetboot */ static int nfs4init(char *path, uint16_t nfs_port) { struct timeval wait; int fd = -1; int error = 0; enum clnt_stat rpc_stat; struct nfs_file rootpath; wait.tv_sec = RPC_RCVWAIT_MSEC / 1000; wait.tv_usec = 0; /* * If we haven't explicitly set the port number, set to the standard * 2049 and don't cause a rpcbind request. */ if (nfs_port == 0) nfs_port = 2049; root_to.sin_port = htons(nfs_port); /* * Support TCP only */ root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM, NFS_V4, wait, &fd, NFS4BUF_SIZE, NFS4BUF_SIZE); if (root_CLIENT == NULL) { root_to.sin_port = 0; return (-1); } root_CLIENT->cl_auth = authunix_create(my_hostname, 0, 1, 1, &fake_gids); /* * Send NULL proc the server first to see if V4 exists */ rpc_stat = CLNT_CALL(root_CLIENT, NFSPROC4_NULL, xdr_void, NULL, xdr_void, NULL, wait); if (rpc_stat != RPC_SUCCESS) { dprintf("boot: NULL proc failed NFSv4 service not available\n"); AUTH_DESTROY(root_CLIENT->cl_auth); CLNT_DESTROY(root_CLIENT); root_to.sin_port = 0; return (-1); } /* * Do a lookup to get to the root_path. This is nice since it can * handle multicomponent lookups. */ roothandle.version = NFS_V4; roothandle.ftype.type4 = NF4DIR; roothandle.fh.fh4.len = 0; /* Force a PUTROOTFH */ roothandle.offset = (uint_t)0; /* it's a directory! */ error = lookup(path, &rootpath, TRUE); if (error) { printf("boot: lookup %s failed\n", path); return (-1); } roothandle = rootpath; /* structure copy */ /* * Hardwire in a known reasonable upper limit of 32K */ nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024; /* * Set a reasonable lower limit on readsize */ nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ? 512 : nfs_readsize; return (0); }
/* * This routine is designed to be able to "ping" * a list of hosts and create a list of responding * hosts sorted by response time. * This must be done without any prior * contact with the host - therefore the "ping" * must be to a "well-known" address. The outstanding * candidate here is the address of "rpcbind". * * A response to a ping is no guarantee that the host * is running NFS, has a mount daemon, or exports * the required filesystem. If the subsequent * mount attempt fails then the host will be marked * "ignore" and the host list will be re-pinged * (sans the bad host). This process continues * until a successful mount is achieved or until * there are no hosts left to try. */ enum clnt_stat nfs_cast(struct mapfs *mfs_in, struct mapfs **mfs_out, int timeout) { enum clnt_stat stat; AUTH *sys_auth = authsys_create_default(); XDR xdr_stream; register XDR *xdrs = &xdr_stream; int outlen; int if_inx; int tsec; int flag; int sent, addr_cnt, rcvd, if_cnt; fd_set readfds, mask; register ulong_t xid; /* xid - unique per addr */ register int i; struct rpc_msg msg; struct timeval t, rcv_timeout; char outbuf[UDPMSGSIZE], inbuf[UDPMSGSIZE]; struct t_unitdata t_udata, t_rdata; struct nd_hostserv hs; struct nd_addrlist *retaddrs; struct transp *tr_head; struct transp *trans, *prev_trans; struct addrs *a, *prev_addr; struct tstamps *ts, *prev_ts; NCONF_HANDLE *nc = NULL; struct netconfig *nconf; struct rlimit rl; int dtbsize; struct mapfs *mfs; /* * For each connectionless transport get a list of * host addresses. Any single host may have * addresses on several transports. */ addr_cnt = sent = rcvd = 0; tr_head = NULL; FD_ZERO(&mask); /* * Set the default select size to be the maximum FD_SETSIZE, unless * the current rlimit is lower. */ dtbsize = FD_SETSIZE; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { if (rl.rlim_cur < FD_SETSIZE) dtbsize = rl.rlim_cur; } prev_trans = NULL; prev_addr = NULL; prev_ts = NULL; for (mfs = mfs_in; mfs; mfs = mfs->mfs_next) { if (trace > 2) trace_prt(1, "nfs_cast: host=%s\n", mfs->mfs_host); nc = setnetconfig(); if (nc == NULL) { stat = RPC_CANTSEND; goto done_broad; } while (nconf = getnetconfig(nc)) { if (!(nconf->nc_flag & NC_VISIBLE) || nconf->nc_semantics != NC_TPI_CLTS || (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)) continue; trans = (struct transp *)malloc(sizeof (*trans)); if (trans == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(trans, 0, sizeof (*trans)); if (tr_head == NULL) tr_head = trans; else prev_trans->tr_next = trans; prev_trans = trans; trans->tr_fd = t_open(nconf->nc_device, O_RDWR, NULL); if (trans->tr_fd < 0) { syslog(LOG_ERR, "nfscast: t_open: %s:%m", nconf->nc_device); stat = RPC_CANTSEND; goto done_broad; } if (t_bind(trans->tr_fd, (struct t_bind *)NULL, (struct t_bind *)NULL) < 0) { syslog(LOG_ERR, "nfscast: t_bind: %m"); stat = RPC_CANTSEND; goto done_broad; } trans->tr_taddr = /* LINTED pointer alignment */ (struct t_bind *)t_alloc(trans->tr_fd, T_BIND, T_ADDR); if (trans->tr_taddr == (struct t_bind *)NULL) { syslog(LOG_ERR, "nfscast: t_alloc: %m"); stat = RPC_SYSTEMERROR; goto done_broad; } trans->tr_device = nconf->nc_device; FD_SET(trans->tr_fd, &mask); if_inx = 0; hs.h_host = mfs->mfs_host; hs.h_serv = "rpcbind"; if (netdir_getbyname(nconf, &hs, &retaddrs) == ND_OK) { /* * If mfs->ignore is previously set for * this map, clear it. Because a host can * have either v6 or v4 address */ if (mfs->mfs_ignore == 1) mfs->mfs_ignore = 0; a = (struct addrs *)malloc(sizeof (*a)); if (a == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(a, 0, sizeof (*a)); if (trans->tr_addrs == NULL) trans->tr_addrs = a; else prev_addr->addr_next = a; prev_addr = a; a->addr_if_tstamps = NULL; a->addr_mfs = mfs; a->addr_addrs = retaddrs; if_cnt = retaddrs->n_cnt; while (if_cnt--) { ts = (struct tstamps *) malloc(sizeof (*ts)); if (ts == NULL) { syslog(LOG_ERR, "no memory"); stat = RPC_CANTSEND; goto done_broad; } (void) memset(ts, 0, sizeof (*ts)); ts->ts_penalty = mfs->mfs_penalty; if (a->addr_if_tstamps == NULL) a->addr_if_tstamps = ts; else prev_ts->ts_next = ts; prev_ts = ts; ts->ts_inx = if_inx++; addr_cnt++; } break; } else { mfs->mfs_ignore = 1; if (verbose) syslog(LOG_ERR, "%s:%s address not known", mfs->mfs_host, strcmp(nconf->nc_proto, NC_INET)?"IPv6":"IPv4"); } } /* while */ endnetconfig(nc); nc = NULL; } /* for */ if (addr_cnt == 0) { syslog(LOG_ERR, "nfscast: couldn't find addresses"); stat = RPC_CANTSEND; goto done_broad; } (void) gettimeofday(&t, (struct timezone *)0); xid = (getpid() ^ t.tv_sec ^ t.tv_usec) & ~0xFF; t.tv_usec = 0; /* serialize the RPC header */ msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; /* * we can not use RPCBVERS here since it doesn't exist in 4.X, * the fix to bug 1139883 has made the 4.X portmapper silent to * version mismatches. This causes the RPC call to the remote * portmapper to simply be ignored if it's not Version 2. */ msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = NULLPROC; if (sys_auth == (AUTH *)NULL) { stat = RPC_SYSTEMERROR; goto done_broad; } msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, sizeof (outbuf), XDR_ENCODE); if (! xdr_callmsg(xdrs, &msg)) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = (int)xdr_getpos(xdrs); xdr_destroy(xdrs); t_udata.opt.len = 0; t_udata.udata.buf = outbuf; t_udata.udata.len = outlen; /* * Basic loop: send packet to all hosts and wait for response(s). * The response timeout grows larger per iteration. * A unique xid is assigned to each address in order to * correctly match the replies. */ for (tsec = 4; timeout > 0; tsec *= 2) { timeout -= tsec; if (timeout <= 0) tsec += timeout; rcv_timeout.tv_sec = tsec; rcv_timeout.tv_usec = 0; sent = 0; for (trans = tr_head; trans; trans = trans->tr_next) { for (a = trans->tr_addrs; a; a = a->addr_next) { struct netbuf *if_netbuf = a->addr_addrs->n_addrs; ts = a->addr_if_tstamps; if_cnt = a->addr_addrs->n_cnt; while (if_cnt--) { /* * xid is the first thing in * preserialized buffer */ /* LINTED pointer alignment */ *((ulong_t *)outbuf) = htonl(xid + ts->ts_inx); (void) gettimeofday(&(ts->ts_timeval), (struct timezone *)0); /* * Check if already received * from a previous iteration. */ if (ts->ts_rcvd) { sent++; ts = ts->ts_next; continue; } t_udata.addr = *if_netbuf++; if (t_sndudata(trans->tr_fd, &t_udata) == 0) { sent++; } ts = ts->ts_next; } } } if (sent == 0) { /* no packets sent ? */ stat = RPC_CANTSEND; goto done_broad; } /* * Have sent all the packets. Now collect the responses... */ rcvd = 0; recv_again: msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.proc = xdr_void; readfds = mask; switch (select(dtbsize, &readfds, (fd_set *)NULL, (fd_set *)NULL, &rcv_timeout)) { case 0: /* Timed out */ /* * If we got at least one response in the * last interval, then don't wait for any * more. In theory we should wait for * the max weighting (penalty) value so * that a very slow server has a chance to * respond but this could take a long time * if the admin has set a high weighting * value. */ if (rcvd > 0) goto done_broad; stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error */ if (errno == EINTR) goto recv_again; syslog(LOG_ERR, "nfscast: select: %m"); if (rcvd == 0) stat = RPC_CANTRECV; goto done_broad; } /* end of select results switch */ for (trans = tr_head; trans; trans = trans->tr_next) { if (FD_ISSET(trans->tr_fd, &readfds)) break; } if (trans == NULL) goto recv_again; try_again: t_rdata.addr = trans->tr_taddr->addr; t_rdata.udata.buf = inbuf; t_rdata.udata.maxlen = sizeof (inbuf); t_rdata.udata.len = 0; t_rdata.opt.len = 0; if (t_rcvudata(trans->tr_fd, &t_rdata, &flag) < 0) { if (errno == EINTR) goto try_again; syslog(LOG_ERR, "nfscast: t_rcvudata: %s:%m", trans->tr_device); stat = RPC_CANTRECV; continue; } if (t_rdata.udata.len < sizeof (ulong_t)) goto recv_again; if (flag & T_MORE) { syslog(LOG_ERR, "nfscast: t_rcvudata: %s: buffer overflow", trans->tr_device); goto recv_again; } /* * see if reply transaction id matches sent id. * If so, decode the results. * Note: received addr is ignored, it could be * different from the send addr if the host has * more than one addr. */ xdrmem_create(xdrs, inbuf, (uint_t)t_rdata.udata.len, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if (msg.rm_reply.rp_stat == MSG_ACCEPTED && (msg.rm_xid & ~0xFF) == xid) { struct addrs *curr_addr; i = msg.rm_xid & 0xFF; for (curr_addr = trans->tr_addrs; curr_addr; curr_addr = curr_addr->addr_next) { for (ts = curr_addr->addr_if_tstamps; ts; ts = ts->ts_next) if (ts->ts_inx == i && !ts->ts_rcvd) { ts->ts_rcvd = 1; calc_resp_time(&ts->ts_timeval); stat = RPC_SUCCESS; rcvd++; break; } } } /* otherwise, we just ignore the errors ... */ } xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = xdr_void; (void) xdr_replymsg(xdrs, &msg); XDR_DESTROY(xdrs); if (rcvd == sent) goto done_broad; else goto recv_again; } if (!rcvd) stat = RPC_TIMEDOUT; done_broad: if (rcvd) { *mfs_out = sort_responses(tr_head); stat = RPC_SUCCESS; } if (nc) endnetconfig(nc); free_transports(tr_head); AUTH_DESTROY(sys_auth); return (stat); }
int32_t nfs_rpc_dispatch_call(rpc_call_t *call, uint32_t flags) { int code = 0; struct timeval CB_TIMEOUT = {15, 0}; /* XXX */ /* send the call, set states, wake waiters, etc */ pthread_mutex_lock(&call->we.mtx); switch (call->states) { case NFS_CB_CALL_DISPATCH: case NFS_CB_CALL_FINISHED: /* XXX invalid entry states for nfs_rpc_dispatch_call */ abort(); } call->states = NFS_CB_CALL_DISPATCH; pthread_mutex_unlock(&call->we.mtx); /* XXX TI-RPC does the signal masking */ pthread_mutex_lock(&call->chan->mtx); if (! call->chan->clnt) { call->stat = RPC_INTR; goto unlock; } call->stat = clnt_call(call->chan->clnt, call->chan->auth, CB_COMPOUND, (xdrproc_t) xdr_CB_COMPOUND4args, &call->cbt.v_u.v4.args, (xdrproc_t) xdr_CB_COMPOUND4res, &call->cbt.v_u.v4.res, CB_TIMEOUT); /* If a call fails, we have to assume path down, or equally fatal * error. We may need back-off. */ if (call->stat != RPC_SUCCESS) { if (call->chan->clnt) { if (call->chan->auth) { AUTH_DESTROY(call->chan->auth); call->chan->auth = NULL; } clnt_destroy(call->chan->clnt); call->chan->clnt = NULL; } } unlock: pthread_mutex_unlock(&call->chan->mtx); /* signal waiter(s) */ pthread_mutex_lock(&call->we.mtx); call->states |= NFS_CB_CALL_FINISHED; /* broadcast will generally be inexpensive */ if (call->flags & NFS_RPC_CALL_BROADCAST) pthread_cond_broadcast(&call->we.cv); pthread_mutex_unlock(&call->we.mtx); /* call completion hook */ RPC_CALL_HOOK(call, RPC_CALL_COMPLETE, NULL, NFS_RPC_CALL_NONE); return (code); }
/* * our version of brpc_call(). We cache in portnumber in to->sin_port for * your convenience. to and from addresses are taken and received in network * order. */ enum clnt_stat brpc_call( rpcprog_t prog, /* rpc program number to call. */ rpcvers_t vers, /* rpc program version */ rpcproc_t proc, /* rpc procedure to call */ xdrproc_t in_xdr, /* routine to serialize arguments */ caddr_t args, /* arg vector for remote call */ xdrproc_t out_xdr, /* routine to deserialize results */ caddr_t ret, /* addr of buf to place results in */ int rexmit, /* retransmission interval (secs) */ int wait_time, /* how long (secs) to wait (resp) */ struct sockaddr_in *to, /* destination */ struct sockaddr_in *from_who, /* responder's port/address */ uint_t auth) /* type of auth wanted. */ { int s; char hostname[MAXHOSTNAMELEN]; struct sockaddr_in from; /* us. */ socklen_t from_len; XDR xmit_xdrs, rcv_xdrs; /* xdr memory */ AUTH *xmit_auth; /* our chosen auth cookie */ gid_t fake_gids = 1; /* fake gids list for auth_unix */ caddr_t trm_msg, rcv_msg; /* outgoing/incoming rpc mesgs */ struct rpc_msg reply; /* our reply msg header */ int trm_len, rcv_len; struct rpc_err rpc_error; /* to store RPC errors in on rcv. */ static uint_t xid; /* current xid */ uint_t xmit_len; /* How much of the buffer we used */ int nrefreshes = 2; /* # of times to refresh cred */ int flags = 0; /* send flags */ uint_t xdelay; int errors, preserve_errno; uint32_t timeout; socklen_t optlen; xmit_auth = NULL; trm_len = mac_get_mtu(); trm_msg = bkmem_alloc(trm_len); rcv_msg = bkmem_alloc(NFSBUF_SIZE); if (trm_msg == NULL || rcv_msg == NULL) { errno = ENOMEM; rpc_error.re_status = RPC_CANTSEND; goto gt_error; } if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { rpc_error.re_status = RPC_CANTSEND; goto gt_error; } if (dontroute) { (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (const void *)&dontroute, sizeof (dontroute)); } if (to->sin_addr.s_addr == cached_destination.s_addr) { optlen = sizeof (timeout); (void) getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, &optlen); } else { cached_destination.s_addr = htonl(INADDR_ANY); } /* Bind our endpoint. */ from.sin_family = AF_INET; ipv4_getipaddr(&from.sin_addr); from.sin_addr.s_addr = htonl(from.sin_addr.s_addr); from.sin_port = get_source_port(B_TRUE); if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) { rpc_error.re_status = RPC_CANTSEND; goto gt_error; } bzero((caddr_t)&rpc_error, sizeof (struct rpc_err)); /* initialize reply's rpc_msg struct, so we can decode later. */ reply.acpted_rply.ar_verf = _null_auth; /* struct copy */ reply.acpted_rply.ar_results.where = ret; reply.acpted_rply.ar_results.proc = out_xdr; if (ntohs(to->sin_port) == 0) { /* snag the udp port we need. */ if ((to->sin_port = (in_port_t)bpmap_getport(prog, vers, &(rpc_error.re_status), to, NULL)) == 0) goto gt_error; to->sin_port = htons(to->sin_port); } /* generate xid - increment */ if (xid == 0) xid = (uint_t)(prom_gettime() / 1000) + 1; else xid++; /* set up outgoing pkt as xdr modified. */ xdrmem_create(&xmit_xdrs, trm_msg, trm_len, XDR_ENCODE); /* setup rpc header */ if (rpc_hdr(&xmit_xdrs, xid, prog, vers, proc) != TRUE) { dprintf("brpc_call: cannot setup rpc header.\n"); rpc_error.re_status = RPC_FAILED; goto gt_error; } /* setup authentication */ switch (auth) { case AUTH_NONE: xmit_auth = authnone_create(); break; case AUTH_UNIX: /* * Assumes we've configured the stack and thus know our * IP address/hostname, either by using DHCP or rarp/bootparams. */ gethostname(hostname, sizeof (hostname)); xmit_auth = authunix_create(hostname, 0, 1, 1, &fake_gids); break; default: dprintf("brpc_call: Unsupported authentication type: %d\n", auth); rpc_error.re_status = RPC_AUTHERROR; goto gt_error; /*NOTREACHED*/ } /* * rpc_hdr puts everything in the xmit buffer for the header * EXCEPT the proc. Put it, and our authentication info into * it now, serializing as we go. We will be at the place where * we left off. */ xmit_xdrs.x_op = XDR_ENCODE; if ((XDR_PUTINT32(&xmit_xdrs, (int32_t *)&proc) == FALSE) || (AUTH_MARSHALL(xmit_auth, &xmit_xdrs, NULL) == FALSE) || ((*in_xdr)(&xmit_xdrs, args) == FALSE)) { rpc_error.re_status = RPC_CANTENCODEARGS; goto gt_error; } else xmit_len = (int)XDR_GETPOS(&xmit_xdrs); /* for sendto */ /* * Right now the outgoing packet should be all serialized and * ready to go... Set up timers. */ xdelay = (rexmit == 0) ? RPC_REXMIT_MSEC : (rexmit * 1000); (void) setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&xdelay, sizeof (xdelay)); wait_time = (wait_time == 0) ? RPC_RCVWAIT_MSEC : (wait_time * 1000); wait_time += prom_gettime(); /* * send out the request. The first item in the receive buffer will * be the xid. Check if it is correct. */ errors = 0; rpc_error.re_status = RPC_TIMEDOUT; do { if (sendto(s, trm_msg, xmit_len, flags, (struct sockaddr *)to, sizeof (struct sockaddr_in)) < 0) { /* * If errno is set to ETIMEDOUT, return * with RPC status as RPC_TIMEDOUT. Calling * funciton will take care of this error by * retrying the RPC call. */ if (errno == ETIMEDOUT) { rpc_error.re_status = RPC_TIMEDOUT; } else { rpc_error.re_status = RPC_CANTSEND; } goto gt_error; } from_len = sizeof (struct sockaddr_in); while ((rcv_len = recvfrom(s, rcv_msg, NFSBUF_SIZE, MSG_DONTWAIT, (struct sockaddr *)from_who, &from_len)) > 0 || errors < RPC_ALLOWABLE_ERRORS) { if (rcv_len < 0) { if (errno == EWOULDBLOCK || errno == ETIMEDOUT) { break; /* timeout */ } rpc_error.re_status = RPC_CANTRECV; goto gt_error; } if (ntohl(*((uint32_t *)(rcv_msg))) != xid) { dprintf("brpc_call: xid: 0x%x != 0x%x\n", *(uint32_t *)(rcv_msg), xid); continue; } /* * Let's deserialize the data into our 'ret' buffer. */ xdrmem_create(&rcv_xdrs, rcv_msg, rcv_len, XDR_DECODE); if (xdr_replymsg(&rcv_xdrs, &reply) == FALSE) { rpc_error.re_status = RPC_CANTDECODERES; goto gt_error; } _seterr_reply(&reply, &rpc_error); switch (rpc_error.re_status) { case RPC_SUCCESS: /* * XXX - validate for unix and none * always return true. */ if (AUTH_VALIDATE(xmit_auth, &reply.acpted_rply.ar_verf) == FALSE) { rpc_error.re_status = RPC_AUTHERROR; rpc_error.re_why = AUTH_INVALIDRESP; errors++; } if (reply.acpted_rply.ar_verf.oa_base != 0) { xmit_xdrs.x_op = XDR_FREE; (void) xdr_opaque_auth( &xmit_xdrs, &reply.acpted_rply.ar_verf); } break; case RPC_AUTHERROR: /* * Let's see if our credentials need * refreshing */ if (nrefreshes > 0 && AUTH_REFRESH(xmit_auth, NULL, NULL)) { nrefreshes--; } errors++; break; case RPC_PROCUNAVAIL: /* * Might be a silly portmapper implementation * erroneously responding to our rpc broadcast * indirect portmapper call. For this * particular case, we don't increment the * error counter because we want to keep * sifting for successful replies... */ if (to->sin_addr.s_addr != ntohl(INADDR_BROADCAST)) errors++; break; case RPC_PROGVERSMISMATCH: /* * Successfully talked to server, but they * don't speak our lingo. */ goto gt_error; default: /* Just keep trying till there's no data... */ errors++; break; } if (rpc_error.re_status != RPC_SUCCESS) { dprintf("brpc_call: from: %s, error: ", inet_ntoa(from_who->sin_addr)); rpc_disperr(&rpc_error); } else break; } /* * If we're having trouble reassembling datagrams, let the * application know ASAP so that it can take the appropriate * actions. */ } while (rpc_error.re_status != RPC_SUCCESS && errno != ETIMEDOUT && prom_gettime() < wait_time); gt_error: if (xmit_auth != NULL) AUTH_DESTROY(xmit_auth); if (trm_msg != NULL) bkmem_free(trm_msg, trm_len); if (rcv_msg != NULL) bkmem_free(rcv_msg, NFSBUF_SIZE); if (rpc_error.re_status != RPC_SUCCESS) rpc_disperr(&rpc_error); /* * socket calls reset errno. Since we want to hold onto the errno * value if it is ETIMEDOUT to communicate to our caller that this * RPC_TIMEDOUT situation is due to a stack problem (we're getting * a reply, but the stack simply can't assemble it.), we need to * preserve errno's value over the socket_close(). */ preserve_errno = (errno == ETIMEDOUT) ? errno : 0; (void) socket_close(s); errno = preserve_errno; return (rpc_error.re_status); }
AUTH * rpc_gss_seccreate(CLIENT *clnt, const char *principal, const char *mechanism, rpc_gss_service_t service, const char *qop, rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret) { AUTH *auth, *save_auth; rpc_gss_options_ret_t options; gss_OID oid; u_int qop_num; struct rpc_gss_data *gd; OM_uint32 maj_stat = 0, min_stat = 0; gss_buffer_desc principal_desc; /* * Bail out now if we don't know this mechanism. */ if (!rpc_gss_mech_to_oid(mechanism, &oid)) return (NULL); if (qop) { if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) return (NULL); } else { qop_num = GSS_C_QOP_DEFAULT; } /* * If the caller doesn't want the options, point at local * storage to simplify the code below. */ if (!options_ret) options_ret = &options; /* * Default service is integrity. */ if (service == rpc_gss_svc_default) service = rpc_gss_svc_integrity; memset(options_ret, 0, sizeof(*options_ret)); log_debug("in rpc_gss_seccreate()"); memset(&rpc_createerr, 0, sizeof(rpc_createerr)); auth = mem_alloc(sizeof(*auth)); if (auth == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = ENOMEM; return (NULL); } gd = mem_alloc(sizeof(*gd)); if (gd == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = ENOMEM; free(auth); return (NULL); } auth->ah_ops = &rpc_gss_ops; auth->ah_private = (caddr_t) gd; auth->ah_cred.oa_flavor = RPCSEC_GSS; principal_desc.value = (void *)(intptr_t) principal; principal_desc.length = strlen(principal); maj_stat = gss_import_name(&min_stat, &principal_desc, GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name); if (maj_stat != GSS_S_COMPLETE) { options_ret->major_status = maj_stat; options_ret->minor_status = min_stat; goto bad; } if (options_req) { gd->gd_options = *options_req; } else { gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG; gd->gd_options.time_req = 0; gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL; gd->gd_options.input_channel_bindings = NULL; } gd->gd_clnt = clnt; gd->gd_ctx = GSS_C_NO_CONTEXT; gd->gd_mech = oid; gd->gd_qop = qop_num; gd->gd_cred.gc_version = RPCSEC_GSS_VERSION; gd->gd_cred.gc_proc = RPCSEC_GSS_INIT; gd->gd_cred.gc_seq = 0; gd->gd_cred.gc_svc = service; save_auth = clnt->cl_auth; clnt->cl_auth = auth; if (!rpc_gss_init(auth, options_ret)) { clnt->cl_auth = save_auth; goto bad; } clnt->cl_auth = save_auth; return (auth); bad: AUTH_DESTROY(auth); return (NULL); }
/* * this code uses the userland rpcsec gss library to create a krb5 * context on behalf of the kernel */ static void process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, char *service) { CLIENT *rpc_clnt = NULL; AUTH *auth = NULL; struct authgss_private_data pd; gss_buffer_desc token; int err, downcall_err = -EACCES; OM_uint32 maj_stat, min_stat, lifetime_rec; pid_t pid, childpid = -1; gss_name_t gacceptor = GSS_C_NO_NAME; gss_OID mech; gss_buffer_desc acceptor = {0}; token.length = 0; token.value = NULL; memset(&pd, 0, sizeof(struct authgss_private_data)); /* * If "service" is specified, then the kernel is indicating that * we must use machine credentials for this request. (Regardless * of the uid value or the setting of root_uses_machine_creds.) * If the service value is "*", then any service name can be used. * Otherwise, it specifies the service name that should be used. * (For now, the values of service will only be "*" or "nfs".) * * Restricting gssd to use "nfs" service name is needed for when * the NFS server is doing a callback to the NFS client. In this * case, the NFS server has to authenticate itself as "nfs" -- * even if there are other service keys such as "host" or "root" * in the keytab. * * Another case when the kernel may specify the service attribute * is when gssd is being asked to create the context for a * SETCLIENT_ID operation. In this case, machine credentials * must be used for the authentication. However, the service name * used for this case is not important. * */ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && service == NULL)) { /* already running as uid 0 */ if (uid == 0) goto no_fork; pid = fork(); switch(pid) { case 0: /* Child: fall through to rest of function */ childpid = getpid(); unsetenv("KRB5CCNAME"); printerr(2, "CHILD forked pid %d \n", childpid); break; case -1: /* fork() failed! */ printerr(0, "WARNING: unable to fork() to handle" "upcall: %s\n", strerror(errno)); return; default: /* Parent: just wait on child to exit and return */ do { pid = wait(&err); } while(pid == -1 && errno != -ECHILD); if (WIFSIGNALED(err)) printerr(0, "WARNING: forked child was killed" "with signal %d\n", WTERMSIG(err)); return; } no_fork: auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err, &err, &rpc_clnt); if (err) goto out_return_error; } if (auth == NULL) { if (uid == 0 && (root_uses_machine_creds == 1 || service != NULL)) { auth = krb5_use_machine_creds(clp, uid, tgtname, service, &rpc_clnt); if (auth == NULL) goto out_return_error; } else { /* krb5_not_machine_creds logs the error */ goto out_return_error; } } if (!authgss_get_private_data(auth, &pd)) { printerr(1, "WARNING: Failed to obtain authentication " "data for user with uid %d for server %s\n", uid, clp->servername); goto out_return_error; } /* Grab the context lifetime and acceptor name out of the ctx. */ maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor, &lifetime_rec, &mech, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { printerr(1, "WARNING: Failed to inquire context " "maj_stat (0x%x)\n", maj_stat); lifetime_rec = 0; } else { get_hostbased_client_buffer(gacceptor, mech, &acceptor); gss_release_name(&min_stat, &gacceptor); } /* * The serialization can mean turning pd.pd_ctx into a lucid context. If * that happens then the pd.pd_ctx will be unusable, so we must never * try to use it after this point. */ if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) { printerr(1, "WARNING: Failed to serialize krb5 context for " "user with uid %d for server %s\n", uid, clp->servername); goto out_return_error; } do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor); out: gss_release_buffer(&min_stat, &acceptor); if (token.value) free(token.value); #ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0) authgss_free_private_data(&pd); #endif if (auth) AUTH_DESTROY(auth); if (rpc_clnt) clnt_destroy(rpc_clnt); pid = getpid(); if (pid == childpid) exit(0); else return; out_return_error: do_error_downcall(fd, uid, downcall_err); goto out; }
static kadm5_ret_t init_any(krb5_context context, char *client_name, enum init_type init_type, char *pass, krb5_ccache ccache_in, char *service_name, kadm5_config_params *params_in, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { int fd = -1; krb5_boolean iprop_enable; int port; rpcprog_t rpc_prog; rpcvers_t rpc_vers; krb5_ccache ccache; krb5_principal client = NULL, server = NULL; kadm5_server_handle_t handle; kadm5_config_params params_local; int code = 0; generic_ret *r; initialize_ovk_error_table(); /* initialize_adb_error_table(); */ initialize_ovku_error_table(); if (! server_handle) { return EINVAL; } if (! (handle = malloc(sizeof(*handle)))) { return ENOMEM; } memset(handle, 0, sizeof(*handle)); if (! (handle->lhandle = malloc(sizeof(*handle)))) { free(handle); return ENOMEM; } handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; handle->struct_version = struct_version; handle->api_version = api_version; handle->clnt = 0; handle->client_socket = -1; handle->cache_name = 0; handle->destroy_cache = 0; handle->context = 0; *handle->lhandle = *handle; handle->lhandle->api_version = KADM5_API_VERSION_4; handle->lhandle->struct_version = KADM5_STRUCT_VERSION; handle->lhandle->lhandle = handle->lhandle; handle->context = context; if(client_name == NULL) { free(handle); return EINVAL; } /* * Verify the version numbers before proceeding; we can't use * CHECK_HANDLE because not all fields are set yet. */ GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, KADM5_NEW_LIB_API_VERSION); memset(¶ms_local, 0, sizeof(params_local)); if ((code = kadm5_get_config_params(handle->context, 0, params_in, &handle->params))) { free(handle); return(code); } #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ KADM5_CONFIG_ADMIN_SERVER | \ KADM5_CONFIG_KADMIND_PORT) if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { free(handle); return KADM5_MISSING_KRB5_CONF_PARAMS; } code = krb5_parse_name(handle->context, client_name, &client); if (code) goto error; /* * Get credentials. Also does some fallbacks in case kadmin/fqdn * principal doesn't exist. */ code = get_init_creds(handle, client, init_type, pass, ccache_in, service_name, handle->params.realm, &server); if (code) goto error; /* If the service_name and client_name are iprop-centric, use the iprop * port and RPC identifiers. */ iprop_enable = (service_name != NULL && strstr(service_name, KIPROP_SVC_NAME) != NULL && strstr(client_name, KIPROP_SVC_NAME) != NULL); if (iprop_enable) { port = handle->params.iprop_port; rpc_prog = KRB5_IPROP_PROG; rpc_vers = KRB5_IPROP_VERS; } else { port = handle->params.kadmind_port; rpc_prog = KADM; rpc_vers = KADMVERS; } code = connect_to_server(handle->params.admin_server, port, &fd); if (code) goto error; handle->clnt = clnttcp_create(NULL, rpc_prog, rpc_vers, &fd, 0, 0); if (handle->clnt == NULL) { code = KADM5_RPC_ERROR; #ifdef DEBUG clnt_pcreateerror("clnttcp_create"); #endif goto error; } handle->client_socket = fd; handle->lhandle->clnt = handle->clnt; handle->lhandle->client_socket = fd; /* now that handle->clnt is set, we can check the handle */ if ((code = _kadm5_check_handle((void *) handle))) goto error; /* * The RPC connection is open; establish the GSS-API * authentication context. */ code = setup_gss(handle, params_in, (init_type == INIT_CREDS) ? client : NULL, server); if (code) goto error; /* * Bypass the remainder of the code and return straightaway * if the gss service requested is kiprop */ if (iprop_enable) { code = 0; *server_handle = (void *) handle; goto cleanup; } r = init_2(&handle->api_version, handle->clnt); if (r == NULL) { code = KADM5_RPC_ERROR; #ifdef DEBUG clnt_perror(handle->clnt, "init_2 null resp"); #endif goto error; } /* Drop down to v3 wire protocol if server does not support v4 */ if (r->code == KADM5_NEW_SERVER_API_VERSION && handle->api_version == KADM5_API_VERSION_4) { handle->api_version = KADM5_API_VERSION_3; r = init_2(&handle->api_version, handle->clnt); if (r == NULL) { code = KADM5_RPC_ERROR; goto error; } } /* Drop down to v2 wire protocol if server does not support v3 */ if (r->code == KADM5_NEW_SERVER_API_VERSION && handle->api_version == KADM5_API_VERSION_3) { handle->api_version = KADM5_API_VERSION_2; r = init_2(&handle->api_version, handle->clnt); if (r == NULL) { code = KADM5_RPC_ERROR; goto error; } } if (r->code) { code = r->code; goto error; } *server_handle = (void *) handle; goto cleanup; error: /* * Note that it is illegal for this code to execute if "handle" * has not been allocated and initialized. I.e., don't use "goto * error" before the block of code at the top of the function * that allocates and initializes "handle". */ if (handle->destroy_cache && handle->cache_name) { if (krb5_cc_resolve(handle->context, handle->cache_name, &ccache) == 0) (void) krb5_cc_destroy (handle->context, ccache); } if (handle->cache_name) free(handle->cache_name); if(handle->clnt && handle->clnt->cl_auth) AUTH_DESTROY(handle->clnt->cl_auth); if(handle->clnt) clnt_destroy(handle->clnt); if (fd != -1) close(fd); kadm5_free_config_params(handle->context, &handle->params); cleanup: krb5_free_principal(handle->context, client); krb5_free_principal(handle->context, server); if (code) free(handle); return code; }
/* * rpc_broadcast_exp() * * prog - program number * vers - version number * proc - procedure number * xargs - xdr routine for args * argsp - pointer to args * xresults - xdr routine for results * resultsp - pointer to results * eachresult - call with each result obtained * inittime - how long to wait initially * waittime - maximum time to wait * nettype - transport type */ enum clnt_stat rpc_broadcast_exp(rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp, xdrproc_t xresults, caddr_t resultsp, resultproc_t eachresult, int inittime, int waittime, const char *nettype) { enum clnt_stat stat = RPC_SUCCESS; /* Return status */ XDR xdr_stream; /* XDR stream */ XDR *xdrs = &xdr_stream; struct rpc_msg msg; /* RPC message */ struct timeval t; char *outbuf = NULL; /* Broadcast msg buffer */ char *inbuf = NULL; /* Reply buf */ int inlen; u_int maxbufsize = 0; AUTH *sys_auth = authunix_create_default(); u_int i; void *handle; char uaddress[1024]; /* A self imposed limit */ char *uaddrp = uaddress; int pmap_reply_flag; /* reply recvd from PORTMAP */ /* An array of all the suitable broadcast transports */ struct { int fd; /* File descriptor */ int af; int proto; struct netconfig *nconf; /* Netconfig structure */ u_int asize; /* Size of the addr buf */ u_int dsize; /* Size of the data buf */ struct sockaddr_storage raddr; /* Remote address */ broadlist_t nal; } fdlist[MAXBCAST]; struct pollfd pfd[MAXBCAST]; size_t fdlistno = 0; struct r_rpcb_rmtcallargs barg; /* Remote arguments */ struct r_rpcb_rmtcallres bres; /* Remote results */ size_t outlen; struct netconfig *nconf; int msec; int pollretval; int fds_found; #ifdef PORTMAP size_t outlen_pmap = 0; u_long port; /* Remote port number */ int pmap_flag = 0; /* UDP exists ? */ char *outbuf_pmap = NULL; struct rmtcallargs barg_pmap; /* Remote arguments */ struct rmtcallres bres_pmap; /* Remote results */ u_int udpbufsz = 0; #endif /* PORTMAP */ if (sys_auth == NULL) { return (RPC_SYSTEMERROR); } /* * initialization: create a fd, a broadcast address, and send the * request on the broadcast transport. * Listen on all of them and on replies, call the user supplied * function. */ if (nettype == NULL) nettype = "datagram_n"; if ((handle = __rpc_setconf(nettype)) == NULL) { AUTH_DESTROY(sys_auth); return (RPC_UNKNOWNPROTO); } while ((nconf = __rpc_getconf(handle)) != NULL) { int fd; struct __rpc_sockinfo si; if (nconf->nc_semantics != NC_TPI_CLTS) continue; if (fdlistno >= MAXBCAST) break; /* No more slots available */ if (!__rpc_nconf2sockinfo(nconf, &si)) continue; TAILQ_INIT(&fdlist[fdlistno].nal); if (__rpc_getbroadifs(si.si_af, si.si_proto, si.si_socktype, &fdlist[fdlistno].nal) == 0) continue; fd = _socket(si.si_af, si.si_socktype, si.si_proto); if (fd < 0) { stat = RPC_CANTSEND; continue; } fdlist[fdlistno].af = si.si_af; fdlist[fdlistno].proto = si.si_proto; fdlist[fdlistno].fd = fd; fdlist[fdlistno].nconf = nconf; fdlist[fdlistno].asize = __rpc_get_a_size(si.si_af); pfd[fdlistno].events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; pfd[fdlistno].fd = fdlist[fdlistno].fd = fd; fdlist[fdlistno].dsize = __rpc_get_t_size(si.si_af, si.si_proto, 0); if (maxbufsize <= fdlist[fdlistno].dsize) maxbufsize = fdlist[fdlistno].dsize; #ifdef PORTMAP if (si.si_af == AF_INET && si.si_proto == IPPROTO_UDP) { udpbufsz = fdlist[fdlistno].dsize; if ((outbuf_pmap = malloc(udpbufsz)) == NULL) { _close(fd); stat = RPC_SYSTEMERROR; goto done_broad; } pmap_flag = 1; } #endif /* PORTMAP */ fdlistno++; } if (fdlistno == 0) { if (stat == RPC_SUCCESS) stat = RPC_UNKNOWNPROTO; goto done_broad; } if (maxbufsize == 0) { if (stat == RPC_SUCCESS) stat = RPC_CANTSEND; goto done_broad; } inbuf = malloc(maxbufsize); outbuf = malloc(maxbufsize); if ((inbuf == NULL) || (outbuf == NULL)) { stat = RPC_SYSTEMERROR; goto done_broad; } /* Serialize all the arguments which have to be sent */ (void) gettimeofday(&t, NULL); msg.rm_xid = __RPC_GETXID(&t); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; msg.rm_call.cb_prog = RPCBPROG; msg.rm_call.cb_vers = RPCBVERS; msg.rm_call.cb_proc = RPCBPROC_CALLIT; barg.prog = prog; barg.vers = vers; barg.proc = proc; barg.args.args_val = argsp; barg.xdr_args = xargs; bres.addr = uaddrp; bres.results.results_val = resultsp; bres.xdr_res = xresults; msg.rm_call.cb_cred = sys_auth->ah_cred; msg.rm_call.cb_verf = sys_auth->ah_verf; xdrmem_create(xdrs, outbuf, maxbufsize, XDR_ENCODE); if ((!xdr_callmsg(xdrs, &msg)) || (!xdr_rpcb_rmtcallargs(xdrs, (struct rpcb_rmtcallargs *)(void *)&barg))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen = xdr_getpos(xdrs); xdr_destroy(xdrs); #ifdef PORTMAP /* Prepare the packet for version 2 PORTMAP */ if (pmap_flag) { msg.rm_xid++; /* One way to distinguish */ msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = PMAPPROC_CALLIT; barg_pmap.prog = prog; barg_pmap.vers = vers; barg_pmap.proc = proc; barg_pmap.args_ptr = argsp; barg_pmap.xdr_args = xargs; bres_pmap.port_ptr = &port; bres_pmap.xdr_results = xresults; bres_pmap.results_ptr = resultsp; xdrmem_create(xdrs, outbuf_pmap, udpbufsz, XDR_ENCODE); if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &barg_pmap))) { stat = RPC_CANTENCODEARGS; goto done_broad; } outlen_pmap = xdr_getpos(xdrs); xdr_destroy(xdrs); } #endif /* PORTMAP */ /* * Basic loop: broadcast the packets to transports which * support data packets of size such that one can encode * all the arguments. * Wait a while for response(s). * The response timeout grows larger per iteration. */ for (msec = inittime; msec <= waittime; msec += msec) { struct broadif *bip; /* Broadcast all the packets now */ for (i = 0; i < fdlistno; i++) { if (fdlist[i].dsize < outlen) { stat = RPC_CANTSEND; continue; } for (bip = TAILQ_FIRST(&fdlist[i].nal); bip != NULL; bip = TAILQ_NEXT(bip, link)) { void *addr; addr = &bip->broadaddr; __rpc_broadenable(fdlist[i].af, fdlist[i].fd, bip); /* * Only use version 3 if lowvers is not set */ if (!__rpc_lowvers) if (_sendto(fdlist[i].fd, outbuf, outlen, 0, (struct sockaddr*)addr, (size_t)fdlist[i].asize) != outlen) { #ifdef RPC_DEBUG perror("sendto"); #endif warnx("clnt_bcast: cannot send " "broadcast packet"); stat = RPC_CANTSEND; continue; } #ifdef RPC_DEBUG if (!__rpc_lowvers) fprintf(stderr, "Broadcast packet sent " "for %s\n", fdlist[i].nconf->nc_netid); #endif #ifdef PORTMAP /* * Send the version 2 packet also * for UDP/IP */ if (pmap_flag && fdlist[i].proto == IPPROTO_UDP) { if (_sendto(fdlist[i].fd, outbuf_pmap, outlen_pmap, 0, addr, (size_t)fdlist[i].asize) != outlen_pmap) { warnx("clnt_bcast: " "Cannot send broadcast packet"); stat = RPC_CANTSEND; continue; } } #ifdef RPC_DEBUG fprintf(stderr, "PMAP Broadcast packet " "sent for %s\n", fdlist[i].nconf->nc_netid); #endif #endif /* PORTMAP */ } /* End for sending all packets on this transport */ } /* End for sending on all transports */ if (eachresult == NULL) { stat = RPC_SUCCESS; goto done_broad; } /* * Get all the replies from these broadcast requests */ recv_again: switch (pollretval = _poll(pfd, fdlistno, msec)) { case 0: /* timed out */ stat = RPC_TIMEDOUT; continue; case -1: /* some kind of error - we ignore it */ goto recv_again; } /* end of poll results switch */ for (i = fds_found = 0; i < fdlistno && fds_found < pollretval; i++) { bool_t done = FALSE; if (pfd[i].revents == 0) continue; else if (pfd[i].revents & POLLNVAL) { /* * Something bad has happened to this descri- * ptor. We can cause _poll() to ignore * it simply by using a negative fd. We do that * rather than compacting the pfd[] and fdlist[] * arrays. */ pfd[i].fd = -1; fds_found++; continue; } else fds_found++; #ifdef RPC_DEBUG fprintf(stderr, "response for %s\n", fdlist[i].nconf->nc_netid); #endif try_again: inlen = _recvfrom(fdlist[i].fd, inbuf, fdlist[i].dsize, 0, (struct sockaddr *)(void *)&fdlist[i].raddr, &fdlist[i].asize); if (inlen < 0) { if (errno == EINTR) goto try_again; warnx("clnt_bcast: Cannot receive reply to " "broadcast"); stat = RPC_CANTRECV; continue; } if (inlen < sizeof (u_int32_t)) continue; /* Drop that and go ahead */ /* * see if reply transaction id matches sent id. * If so, decode the results. If return id is xid + 1 * it was a PORTMAP reply */ if (*((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf))) { pmap_reply_flag = 0; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rpcb_rmtcallres; #ifdef PORTMAP } else if (pmap_flag && *((u_int32_t *)(void *)(inbuf)) == *((u_int32_t *)(void *)(outbuf_pmap))) { pmap_reply_flag = 1; msg.acpted_rply.ar_verf = _null_auth; msg.acpted_rply.ar_results.where = (caddr_t)(void *)&bres_pmap; msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; #endif /* PORTMAP */ } else continue; xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE); if (xdr_replymsg(xdrs, &msg)) { if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && (msg.acpted_rply.ar_stat == SUCCESS)) { struct netbuf taddr, *np; struct sockaddr_in *sin; #ifdef PORTMAP if (pmap_flag && pmap_reply_flag) { sin = (struct sockaddr_in *) (void *)&fdlist[i].raddr; sin->sin_port = htons((u_short)port); taddr.len = taddr.maxlen = fdlist[i].raddr.ss_len; taddr.buf = &fdlist[i].raddr; done = (*eachresult)(resultsp, &taddr, fdlist[i].nconf); } else { #endif /* PORTMAP */ #ifdef RPC_DEBUG fprintf(stderr, "uaddr %s\n", uaddrp); #endif np = uaddr2taddr( fdlist[i].nconf, uaddrp); done = (*eachresult)(resultsp, np, fdlist[i].nconf); free(np); #ifdef PORTMAP } #endif /* PORTMAP */ } /* otherwise, we just ignore the errors ... */ } /* else some kind of deserialization problem ... */ xdrs->x_op = XDR_FREE; msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; (void) xdr_replymsg(xdrs, &msg); (void) (*xresults)(xdrs, resultsp); XDR_DESTROY(xdrs); if (done) { stat = RPC_SUCCESS; goto done_broad; } else { goto recv_again; } } /* The recv for loop */ } /* The giant for loop */ done_broad: free(inbuf); free(outbuf); #ifdef PORTMAP free(outbuf_pmap); #endif /* PORTMAP */ for (i = 0; i < fdlistno; i++) { (void)_close(fdlist[i].fd); __rpc_freebroadifs(&fdlist[i].nal); } AUTH_DESTROY(sys_auth); (void) __rpc_endconf(handle); return (stat); }
/* * nfs_request - goes something like this * - fill in request struct * - links it into list * - calls nfs_send() for first transmit * - calls nfs_receive() to get reply * - break down rpc header and return with nfs reply pointed to * by mrep or error * nb: always frees up mreq mbuf list */ int nfs_request(struct vnode *vp, struct mbuf *mreq, int procnum, struct thread *td, struct ucred *cred, struct mbuf **mrp, struct mbuf **mdp, caddr_t *dposp) { struct mbuf *mrep; u_int32_t *tl; struct nfsmount *nmp; struct mbuf *md; time_t waituntil; caddr_t dpos; int error = 0, timeo; AUTH *auth = NULL; enum nfs_rto_timer_t timer; struct nfs_feedback_arg nf; struct rpc_callextra ext; enum clnt_stat stat; struct timeval timo; /* Reject requests while attempting a forced unmount. */ if (vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF) { m_freem(mreq); return (ESTALE); } nmp = VFSTONFS(vp->v_mount); bzero(&nf, sizeof(struct nfs_feedback_arg)); nf.nf_mount = nmp; nf.nf_td = td; nf.nf_lastmsg = time_uptime - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay)); /* * XXX if not already connected call nfs_connect now. Longer * term, change nfs_mount to call nfs_connect unconditionally * and let clnt_reconnect_create handle reconnects. */ if (!nmp->nm_client) nfs_connect(nmp); auth = nfs_getauth(nmp, cred); if (!auth) { m_freem(mreq); return (EACCES); } bzero(&ext, sizeof(ext)); ext.rc_auth = auth; ext.rc_feedback = nfs_feedback; ext.rc_feedback_arg = &nf; /* * Use a conservative timeout for RPCs other than getattr, * lookup, read or write. The justification for doing "other" * this way is that these RPCs happen so infrequently that * timer est. would probably be stale. Also, since many of * these RPCs are non-idempotent, a conservative timeout is * desired. */ timer = nfs_rto_timer(procnum); if (timer != NFS_DEFAULT_TIMER) ext.rc_timers = &nmp->nm_timers[timer - 1]; else ext.rc_timers = NULL; #ifdef KDTRACE_HOOKS if (dtrace_nfsclient_nfs23_start_probe != NULL) { uint32_t probe_id; int probe_procnum; if (nmp->nm_flag & NFSMNT_NFSV3) { probe_id = nfsclient_nfs3_start_probes[procnum]; probe_procnum = procnum; } else { probe_id = nfsclient_nfs2_start_probes[procnum]; probe_procnum = nfsv2_procid[procnum]; } if (probe_id != 0) (dtrace_nfsclient_nfs23_start_probe)(probe_id, vp, mreq, cred, probe_procnum); } #endif nfsstats.rpcrequests++; tryagain: /* * This timeout specifies when a new socket should be created, * along with new xid values. For UDP, this should be done * infrequently, since retransmits of RPC requests should normally * use the same xid. */ if (nmp->nm_sotype == SOCK_DGRAM) { if ((nmp->nm_flag & NFSMNT_SOFT) != 0) { /* * CLSET_RETRIES is set to 2, so this should be half * of the total timeout required. */ timeo = nmp->nm_retry * nmp->nm_timeo / 2; if (timeo < 1) timeo = 1; timo.tv_sec = timeo / NFS_HZ; timo.tv_usec = (timeo % NFS_HZ) * 1000000 / NFS_HZ; } else { /* For UDP hard mounts, use a large value. */ timo.tv_sec = NFS_MAXTIMEO / NFS_HZ; timo.tv_usec = 0; } } else { timo.tv_sec = nmp->nm_timeo / NFS_HZ; timo.tv_usec = (nmp->nm_timeo % NFS_HZ) * 1000000 / NFS_HZ; } mrep = NULL; stat = CLNT_CALL_MBUF(nmp->nm_client, &ext, (nmp->nm_flag & NFSMNT_NFSV3) ? procnum : nfsv2_procid[procnum], mreq, &mrep, timo); /* * If there was a successful reply and a tprintf msg. * tprintf a response. */ if (stat == RPC_SUCCESS) error = 0; else if (stat == RPC_TIMEDOUT) { nfsstats.rpctimeouts++; error = ETIMEDOUT; } else if (stat == RPC_VERSMISMATCH) { nfsstats.rpcinvalid++; error = EOPNOTSUPP; } else if (stat == RPC_PROGVERSMISMATCH) { nfsstats.rpcinvalid++; error = EPROTONOSUPPORT; } else if (stat == RPC_INTR) { error = EINTR; } else { nfsstats.rpcinvalid++; error = EACCES; } if (error) goto nfsmout; KASSERT(mrep != NULL, ("mrep shouldn't be NULL if no error\n")); /* * Search for any mbufs that are not a multiple of 4 bytes long * or with m_data not longword aligned. * These could cause pointer alignment problems, so copy them to * well aligned mbufs. */ error = nfs_realign(&mrep, M_NOWAIT); if (error == ENOMEM) { m_freem(mrep); AUTH_DESTROY(auth); nfsstats.rpcinvalid++; return (error); } md = mrep; dpos = mtod(mrep, caddr_t); tl = nfsm_dissect(u_int32_t *, NFSX_UNSIGNED); if (*tl != 0) { error = fxdr_unsigned(int, *tl); if ((nmp->nm_flag & NFSMNT_NFSV3) && error == NFSERR_TRYLATER) { m_freem(mrep); error = 0; waituntil = time_second + nfs3_jukebox_delay; while (time_second < waituntil) (void)tsleep(&fake_wchan, PSOCK, "nqnfstry", hz); goto tryagain; } /* * Make sure NFSERR_RETERR isn't bogusly set by a server * such as amd. (No actual NFS error has bit 31 set.) */ error &= ~NFSERR_RETERR; /* * If the File Handle was stale, invalidate the lookup * cache, just in case. */ if (error == ESTALE) nfs_purgecache(vp); /* * Skip wcc data on non-ENOENT NFS errors for now. * NetApp filers return corrupt postop attrs in the * wcc data for NFS err EROFS. Not sure if they could * return corrupt postop attrs for others errors. * Blocking ENOENT post-op attributes breaks negative * name caching, so always allow it through. */ if ((nmp->nm_flag & NFSMNT_NFSV3) && (!nfs_skip_wcc_data_onerr || error == ENOENT)) { *mrp = mrep; *mdp = md; *dposp = dpos; error |= NFSERR_RETERR; } else m_freem(mrep); goto nfsmout; }
/* * nfs_request - goes something like this * - fill in request struct * - links it into list * - calls nfs_send() for first transmit * - calls nfs_receive() to get reply * - break down rpc header and return with nfs reply pointed to * by mrep or error * nb: always frees up mreq mbuf list */ int nfs_request(struct vnode *vp, struct mbuf *mreq, int procnum, struct thread *td, struct ucred *cred, struct mbuf **mrp, struct mbuf **mdp, caddr_t *dposp) { struct mbuf *mrep; u_int32_t *tl; struct nfsmount *nmp; struct mbuf *md; time_t waituntil; caddr_t dpos; int error = 0; struct timeval now; AUTH *auth = NULL; enum nfs_rto_timer_t timer; struct nfs_feedback_arg nf; struct rpc_callextra ext; enum clnt_stat stat; struct timeval timo; /* Reject requests while attempting a forced unmount. */ if (vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF) { m_freem(mreq); return (ESTALE); } nmp = VFSTONFS(vp->v_mount); bzero(&nf, sizeof(struct nfs_feedback_arg)); nf.nf_mount = nmp; nf.nf_td = td; getmicrouptime(&now); nf.nf_lastmsg = now.tv_sec - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay)); /* * XXX if not already connected call nfs_connect now. Longer * term, change nfs_mount to call nfs_connect unconditionally * and let clnt_reconnect_create handle reconnects. */ if (!nmp->nm_client) nfs_connect(nmp); auth = nfs_getauth(nmp, cred); if (!auth) { m_freem(mreq); return (EACCES); } bzero(&ext, sizeof(ext)); ext.rc_auth = auth; ext.rc_feedback = nfs_feedback; ext.rc_feedback_arg = &nf; /* * Use a conservative timeout for RPCs other than getattr, * lookup, read or write. The justification for doing "other" * this way is that these RPCs happen so infrequently that * timer est. would probably be stale. Also, since many of * these RPCs are non-idempotent, a conservative timeout is * desired. */ timer = nfs_rto_timer(procnum); if (timer != NFS_DEFAULT_TIMER) ext.rc_timers = &nmp->nm_timers[timer - 1]; else ext.rc_timers = NULL; #ifdef KDTRACE_HOOKS if (dtrace_nfsclient_nfs23_start_probe != NULL) { uint32_t probe_id; int probe_procnum; if (nmp->nm_flag & NFSMNT_NFSV3) { probe_id = nfsclient_nfs3_start_probes[procnum]; probe_procnum = procnum; } else { probe_id = nfsclient_nfs2_start_probes[procnum]; probe_procnum = nfsv2_procid[procnum]; } if (probe_id != 0) (dtrace_nfsclient_nfs23_start_probe)(probe_id, vp, mreq, cred, probe_procnum); } #endif nfsstats.rpcrequests++; tryagain: timo.tv_sec = nmp->nm_timeo / NFS_HZ; timo.tv_usec = (nmp->nm_timeo * 1000000) / NFS_HZ; mrep = NULL; stat = CLNT_CALL_MBUF(nmp->nm_client, &ext, (nmp->nm_flag & NFSMNT_NFSV3) ? procnum : nfsv2_procid[procnum], mreq, &mrep, timo); /* * If there was a successful reply and a tprintf msg. * tprintf a response. */ if (stat == RPC_SUCCESS) error = 0; else if (stat == RPC_TIMEDOUT) error = ETIMEDOUT; else if (stat == RPC_VERSMISMATCH) error = EOPNOTSUPP; else if (stat == RPC_PROGVERSMISMATCH) error = EPROTONOSUPPORT; else error = EACCES; if (error) goto nfsmout; KASSERT(mrep != NULL, ("mrep shouldn't be NULL if no error\n")); /* * Search for any mbufs that are not a multiple of 4 bytes long * or with m_data not longword aligned. * These could cause pointer alignment problems, so copy them to * well aligned mbufs. */ error = nfs_realign(&mrep, M_DONTWAIT); if (error == ENOMEM) { m_freem(mrep); AUTH_DESTROY(auth); return (error); } md = mrep; dpos = mtod(mrep, caddr_t); tl = nfsm_dissect(u_int32_t *, NFSX_UNSIGNED); if (*tl != 0) { error = fxdr_unsigned(int, *tl); if ((nmp->nm_flag & NFSMNT_NFSV3) && error == NFSERR_TRYLATER) { m_freem(mrep); error = 0; waituntil = time_second + nfs3_jukebox_delay; while (time_second < waituntil) (void)tsleep(&fake_wchan, PSOCK, "nqnfstry", hz); goto tryagain; } /* * If the File Handle was stale, invalidate the lookup * cache, just in case. */ if (error == ESTALE) nfs_purgecache(vp); /* * Skip wcc data on NFS errors for now. NetApp filers * return corrupt postop attrs in the wcc data for NFS * err EROFS. Not sure if they could return corrupt * postop attrs for others errors. */ if ((nmp->nm_flag & NFSMNT_NFSV3) && !nfs_skip_wcc_data_onerr) { *mrp = mrep; *mdp = md; *dposp = dpos; error |= NFSERR_RETERR; } else m_freem(mrep); goto nfsmout; }
static void test_client(int argc, const char **argv) { rpcproc_t prog = 123456; rpcvers_t vers = 1; const char *netid = "tcp"; char hostname[128], service[128+5]; CLIENT *client; AUTH *auth; const char **mechs; rpc_gss_options_req_t options_req; rpc_gss_options_ret_t options_ret; rpc_gss_service_t svc; struct timeval tv; enum clnt_stat stat; if (argc == 2) strlcpy(hostname, argv[1], sizeof(hostname)); else gethostname(hostname, sizeof(hostname)); client = clnt_create(hostname, prog, vers, netid); if (!client) { printf("rpc_createerr.cf_stat = %d\n", rpc_createerr.cf_stat); printf("rpc_createerr.cf_error.re_errno = %d\n", rpc_createerr.cf_error.re_errno); return; } strcpy(service, "host"); strcat(service, "@"); strcat(service, hostname); mechs = rpc_gss_get_mechanisms(); auth = NULL; while (*mechs) { options_req.req_flags = GSS_C_MUTUAL_FLAG; options_req.time_req = 600; options_req.my_cred = GSS_C_NO_CREDENTIAL; options_req.input_channel_bindings = NULL; auth = rpc_gss_seccreate(client, service, *mechs, rpc_gss_svc_none, NULL, &options_req, &options_ret); if (auth) break; mechs++; } if (!auth) { clnt_pcreateerror("rpc_gss_seccreate"); printf("Can't authenticate with server %s.\n", hostname); exit(1); } client->cl_auth = auth; for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) { const char *svc_names[] = { "rpc_gss_svc_default", "rpc_gss_svc_none", "rpc_gss_svc_integrity", "rpc_gss_svc_privacy" }; int num; rpc_gss_set_defaults(auth, svc, NULL); tv.tv_sec = 5; tv.tv_usec = 0; num = 42; stat = CLNT_CALL(client, 1, (xdrproc_t) xdr_int, (char *) &num, (xdrproc_t) xdr_int, (char *) &num, tv); if (stat == RPC_SUCCESS) { printf("succeeded with %s\n", svc_names[svc]); if (num != 142) printf("unexpected reply %d\n", num); } else { clnt_perror(client, "call failed"); } } AUTH_DESTROY(auth); CLNT_DESTROY(client); }