/*
 * This the routine has the same definition as clnt_create_vers(),
 * except it takes an additional timeout parameter - a pointer to
 * a timeval structure.  A NULL value for the pointer indicates
 * that the default timeout value should be used.
 */
CLIENT *
clnt_create_vers_timed(const char *hostname, const rpcprog_t prog,
    rpcvers_t *vers_out, const rpcvers_t vers_low_in, const rpcvers_t vers_high_in,
    const char *nettype, const struct timeval *tp)
{
	CLIENT *clnt;
	struct timeval to;
	enum clnt_stat rpc_stat;
	struct rpc_err rpcerr;
	rpcvers_t vers_high = vers_high_in;
	rpcvers_t vers_low = vers_low_in;

	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
	if (clnt == NULL) {
		return (NULL);
	}
	to.tv_sec = 10;
	to.tv_usec = 0;
	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
	if (rpc_stat == RPC_SUCCESS) {
		*vers_out = vers_high;
		return (clnt);
	}
	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
		unsigned int minvers, maxvers;

		clnt_geterr(clnt, &rpcerr);
		minvers = rpcerr.re_vers.low;
		maxvers = rpcerr.re_vers.high;
		if (maxvers < vers_high)
			vers_high = maxvers;
		else
			vers_high--;
		if (minvers > vers_low)
			vers_low = minvers;
		if (vers_low > vers_high) {
			goto error;
		}
		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
				(char *)NULL, (xdrproc_t)xdr_void,
				(char *)NULL, to);
		if (rpc_stat == RPC_SUCCESS) {
			*vers_out = vers_high;
			return (clnt);
		}
	}
	clnt_geterr(clnt, &rpcerr);

error:
	rpc_createerr.cf_stat = rpc_stat;
	rpc_createerr.cf_error = rpcerr;
	clnt_destroy(clnt);
	return (NULL);
}
Beispiel #2
0
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: gettunnelinfo
 * 
 ***************************************************************************************/
int CCHacontroller::gettunnelinfo( TGetTunnelRequest aArgs, THaTunnelInfo *rv )
{
	struct rpc_err rerr;

	// check the rv pointer
	if( rv == NULL ) {
		return ERR_INVALID_RV_POINTER;
	}

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	*rv = *gettunnelinfo_8( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
Beispiel #3
0
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: removelistoption
 * 
 ***************************************************************************************/
int CCHacontroller::removelistoption( TOptionDesc aArgs, TResult *rv )
{
	struct rpc_err rerr;

	// check the rv pointer
	if( rv == NULL ) {
		return ERR_INVALID_RV_POINTER;
	}

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	*rv = *removelistoption_8( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
int main(int argn, char *argc[])
{
	//Program parameters : argc[1] : HostName or Host IP
	//                                         argc[2] : Server Program Number
	//                                         other arguments depend on test case

	//run_mode can switch into stand alone program or program launch by shell script
	//1 : stand alone, debug mode, more screen information
	//0 : launch by shell script as test case, only one printf -> result status
	int run_mode = 0;
	int test_status = 1;	//Default test result set to FAILED
	int progNum = atoi(argc[2]);
	char proto[8] = "udp";
	CLIENT *clnt = NULL;
	struct rpc_err err;

	//First of all, create a client
	clnt = clnt_create(argc[1], progNum, VERSNUM, proto);

	if (run_mode == 1) {
		printf("CLIENT : %d\n", clnt);
	}
	//Call tested routine
	clnt_geterr(clnt, &err);

	//If we are here, macro call was successful
	test_status = !(err.re_status == RPC_SUCCESS);

	//This last printf gives the result status to the tests suite
	//normally should be 0: test has passed or 1: test has failed
	printf("%d\n", test_status);

	return test_status;
}
Beispiel #5
0
static ErrorObj*
ldm_clnt_nullproc(CLIENT* const clnt)
{
    struct timeval timeout;
    ErrorObj*       error;

    log_assert(NULL != clnt);

    timeout.tv_sec = 25;        /* RPC default */
    timeout.tv_usec = 0;

    if (clnt_call(clnt, NULLPROC, xdr_void, NULL, xdr_void,
            NULL, timeout) == 0) {
        error = NULL;
    }
    else {
        struct rpc_err rpcErr;

        clnt_geterr(clnt, &rpcErr);

        error = ERR_NEW(rpcErr.re_status, NULL, clnt_errmsg(clnt));
    }

    return error;
}
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: getppplog
 * 
 ***************************************************************************************/
int CCPppcontroller::getppplog( int aArgs, TVarData *rv )
{
	struct rpc_err rerr;

	// check the rv pointer
	if( rv == NULL ) {
		return ERR_INVALID_RV_POINTER;
	}

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	*rv = *getppplog_4( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
Beispiel #7
0
/*
 * Find the mapped port for program,version.
 * Calls the pmap service remotely to do the lookup.
 * Returns 0 if no map exists.
 */
u_short
pmap_getport(struct sockaddr_in *address, u_long program, u_long version,
    u_int protocol)
{
	u_short port = 0;
	int sock = -1;
	CLIENT *client;
	struct pmap parms;

	assert(address != NULL);

	address->sin_port = htons(PMAPPORT);
	client = clntudp_bufcreate(address, PMAPPROG,
	    PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
	if (client != NULL) {
		parms.pm_prog = program;
		parms.pm_vers = version;
		parms.pm_prot = protocol;
		parms.pm_port = 0;  /* not needed or used */
		if (CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
		    (xdrproc_t)xdr_pmap,
		    &parms, (xdrproc_t)xdr_u_short, &port, tottimeout) !=
		    RPC_SUCCESS){
			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
			clnt_geterr(client, &rpc_createerr.cf_error);
		} else if (port == 0) {
			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
		}
		CLNT_DESTROY(client);
	}
	address->sin_port = 0;
	return (port);
}
Beispiel #8
0
/*
 * Find the mapped port for program,version.
 * Calls the pmap service remotely to do the lookup.
 * Returns 0 if no map exists.
 */
u_short
pmap_getport(
    struct sockaddr_in *address,
    rpcprog_t program,
    rpcvers_t version,
    rpcprot_t protocol)
{
    unsigned short port = 0;
    int sock = -1;
    register CLIENT *client;
    struct pmap parms;

    address->sin_port = htons(PMAPPORT);
    client = clntudp_bufcreate(address, PMAPPROG,
                               PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
    if (client != (CLIENT *)NULL) {
        parms.pm_prog = program;
        parms.pm_vers = version;
        parms.pm_prot = protocol;
        parms.pm_port = 0;  /* not needed or used */
        if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
                      xdr_u_short, &port, tottimeout) != RPC_SUCCESS) {
            rpc_createerr.cf_stat = RPC_PMAPFAILURE;
            clnt_geterr(client, &rpc_createerr.cf_error);
        } else if (port == 0) {
            rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
        }
        CLNT_DESTROY(client);
    }
    (void)close(sock);
    address->sin_port = 0;
    return (port);
}
Beispiel #9
0
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: list_devices
 * 
 ***************************************************************************************/
int CCHacontroller::list_devices( TComponentList *rv )
{
	struct rpc_err rerr;
	int aArgs = 0;

	// check the rv pointer
	if( rv == NULL ) {
		return ERR_INVALID_RV_POINTER;
	}

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	*rv = *list_devices_8( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
Beispiel #10
0
/*
 * Returns the LDM proxy status for a given client failure. Logs the failure.
 *
 * Arguments:
 *      proxy           The LDM proxy data-structure.
 *      name            The name of the failed message or NULL.
 *      info            Metadata on the data-product that couldn't be sent or
 *                      NULL.
 * Returns:
 *      0               Success.
 *      LP_TIMEDOUT     The failure was due to an RPC timeout. "log_start()"
 *                      called iff "name" isn't NULL.
 *      LP_RPC_ERROR    RPC error. "log_start()" called iff "name" isn't NULL.
 */
static LdmProxyStatus
getStatus(
    LdmProxy* const     proxy,
    const char* const   name,
    prod_info* const    info)
{
    LdmProxyStatus      status;
    struct rpc_err      rpcErr;

    clnt_geterr(proxy->clnt, &rpcErr);

    if (0 == rpcErr.re_status) {
        status = 0;
    }
    else {
        if (NULL != name) {
            LOG_START3("%s failure to host \"%s\": %s", name, proxy->host, 
                    clnt_errmsg(proxy->clnt));

            if (NULL != info) {
                LOG_ADD1("Couldn't send product: %s", s_prod_info(NULL, 0,
                            info, ulogIsDebug()));
            }
        }

        status = RPC_TIMEDOUT == rpcErr.re_status
            ? LP_TIMEDOUT
            : LP_RPC_ERROR;
    }

    return status;
}
Beispiel #11
0
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: ss_startuprpcservice
 * 
 ***************************************************************************************/
int CCHacontroller::ss_startuprpcservice( TStartupInfo aArgs, int *rv )
{
	struct rpc_err rerr;

	// check the rv pointer
	if( rv == NULL ) {
		return ERR_INVALID_RV_POINTER;
	}

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	*rv = *ss_startuprpcservice_8( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
Beispiel #12
0
char * clnt_sperror(struct client *clnt, const char *msg) {
	/* FIXME should use snprintf instead sprintf */
	static char buff[ERROR_STR_MAX_SZ];
	struct rpc_err err;
	char *curr, *auth_msg;

	assert((clnt != NULL) && (msg != NULL));

	clnt_geterr(clnt, &err);

	curr = buff;
	curr += sprintf(curr, "%s: %s", msg, clnt_sperrno(err.status));
	switch (err.status) {
	default:
		curr += sprintf(curr, "; s1 = %u, s2 = %u", err.extra.mminfo.low,
				err.extra.mminfo.high);
		break;
	case RPC_SUCCESS:
	case RPC_CANTENCODEARGS:
	case RPC_CANTDECODERES:
	case RPC_TIMEDOUT:
	case RPC_PROGUNAVAIL:
	case RPC_PROCUNAVAIL:
	case RPC_CANTDECODEARGS:
	case RPC_SYSTEMERROR:
	case RPC_UNKNOWNHOST:
	case RPC_PMAPFAILURE:
	case RPC_PROGNOTREGISTERED:
	case RPC_FAILED:
	case RPC_UNKNOWNPROTO:
		break;
	case RPC_CANTSEND:
	case RPC_CANTRECV:
		curr += sprintf(curr, "; errno = %s", strerror(err.extra.error));
		break;
	case RPC_VERSMISMATCH:
		curr += sprintf(curr, "; low version = %u, high version = %u",
				err.extra.mminfo.low, err.extra.mminfo.high);
		break;
	case RPC_AUTHERROR:
		auth_msg = get_auth_msg(err.extra.reason);
		if (auth_msg != NULL) {
			curr += sprintf(curr, "; why = %s", auth_msg);
		}
		else {
			curr += sprintf(curr, "; why = (unknown authentication error - %d)",
					err.extra.reason);
		}
		break;
	case RPC_PROGVERSMISMATCH:
		curr += sprintf(curr, "; low version = %u, high version = %u",
				err.extra.mminfo.low, err.extra.mminfo.high);
		break;
	}

	assert(curr < buff + sizeof buff); // TODO remove this

	return buff;
}
Beispiel #13
0
char *CCPppcontroller::GetLastRPCError( int *aIntErr )
{
	struct rpc_err rpcerr;

	// check that the handle is valid
	if( cl == NULL ) {
		return NULL;
	}

	// pass the aIntErr
	if( aIntErr != NULL ) {
		clnt_geterr( cl, &rpcerr );
		*aIntErr = rpcerr.re_status;
	}

	// return the errorstring
	return clnt_sperror( cl, NULL );
}
Beispiel #14
0
/****************************************************************************************
 * 
 * PUBLIC FUNCTION: settimeout
 * 
 ***************************************************************************************/
int CCHacontroller::settimeout( TTimeoutRequest aArgs )
{
	struct rpc_err rerr;

	// check that we have a connection
	if( cl == NULL ) {
		return ERR_STUB_NOT_CONNECTED;
	}

	// do the call
	settimeout_8( &aArgs, cl );

	// check for rpc errors and return the result
	clnt_geterr( cl, &rerr );
	if( rerr.re_status != RPC_SUCCESS ) {
		iLastRPCError = rerr;
		return ERR_RPC_ERROR;
	}
	return ERR_NONE;
}
Beispiel #15
0
static int
nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
	       mntarg_t *mntarg, mntres_t *mntres)
{
	CLIENT *clnt;
	enum clnt_stat stat;
	int msock;

	if (!probe_bothports(mnt_server, nfs_server))
		goto out_bad;

	clnt = mnt_openclnt(mnt_server, &msock);
	if (!clnt)
		goto out_bad;
	/* make pointers in xdr_mountres3 NULL so
	 * that xdr_array allocates memory for us
	 */
	memset(mntres, 0, sizeof(*mntres));
	switch (mnt_server->pmap.pm_vers) {
	case 3:
		stat = nfs3_mount(clnt, mntarg, &mntres->nfsv3);
		break;
	case 2:
	case 1:
		stat = nfs2_mount(clnt, mntarg, &mntres->nfsv2);
		break;
	default:
		goto out_bad;
	}
	if (stat != RPC_SUCCESS) {
		clnt_geterr(clnt, &rpc_createerr.cf_error);
		rpc_createerr.cf_stat = stat;
	}
	mnt_closeclnt(clnt, msock);
	if (stat == RPC_SUCCESS)
		return 1;
 out_bad:
	return 0;
}
Beispiel #16
0
/*
 * If the server (fmd) is restarted, this will cause all future door calls to
 * fail.  Unfortunately, once the server comes back up, we have no way of
 * reestablishing the connection.  To get around this, if the error indicates
 * that the RPC call failed, we reopen the client handle and try again.  For
 * simplicity we only deal with the door case, as it's unclear whether the
 * remote case suffers from the same pathology.
 */
boolean_t
fmd_adm_retry(fmd_adm_t *ap, enum clnt_stat cs, uint_t *retries)
{
	CLIENT *c;
	struct rpc_err err;

	if (cs == RPC_SUCCESS || *retries == ap->adm_maxretries)
		return (B_FALSE);

	clnt_geterr(ap->adm_clnt, &err);
	if (err.re_status != RPC_CANTSEND)
		return (B_FALSE);

	if ((c = clnt_door_create(ap->adm_prog, FMD_ADM_VERSION_1,
	    _fmd_adm_bufsize)) == NULL)
		return (B_FALSE);

	(*retries)++;

	clnt_destroy(ap->adm_clnt);
	ap->adm_clnt = c;

	return (B_TRUE);
}
Beispiel #17
0
/*
 * Print reply error info
 *
 * This is derived from RPC 4.0 source.  It's here because at least one
 * implementation of the RPC function clnt_sperror() results in a segmentation
 * violation (SunOS 5.8).
 */
char*
clnt_errmsg(CLIENT* clnt)
{
    struct       rpc_err e;
    static char  buf[512];
    char        *str = buf;

    clnt_geterr(clnt, &e);

    (void) strncpy(str, clnt_sperrno(e.re_status), sizeof(buf));
    str[sizeof(buf)-1] = 0;
    str += strlen(str);

    switch (e.re_status) {
        case RPC_SUCCESS:
        case RPC_CANTENCODEARGS:
        case RPC_CANTDECODERES:
        case RPC_TIMEDOUT:     
        case RPC_PROGUNAVAIL:
        case RPC_PROCUNAVAIL:
        case RPC_CANTDECODEARGS:
        case RPC_SYSTEMERROR:
        case RPC_UNKNOWNHOST:
        case RPC_UNKNOWNPROTO:
        case RPC_PMAPFAILURE:
        case RPC_PROGNOTREGISTERED:
        case RPC_FAILED:
                break;

        case RPC_CANTSEND:
        case RPC_CANTRECV:
            (void) sprintf(str, "; errno = %s", strerror(e.re_errno)); 
            str += strlen(str);
            break;

        case RPC_VERSMISMATCH:
            (void) sprintf(str, "; low version = %lu, high version = %lu", 
                e.re_vers.low, e.re_vers.high);
            str += strlen(str);
            break;

        case RPC_AUTHERROR:
            (void) sprintf(str,"; why = ");
            str += strlen(str);
            (void) sprintf(str, "(authentication error %d)",
                (int) e.re_why);
            str += strlen(str);
            break;

        case RPC_PROGVERSMISMATCH:
            (void) sprintf(str, "; low version = %lu, high version = %lu", 
                e.re_vers.low, e.re_vers.high);
            str += strlen(str);
            break;

        default:        /* unknown */
            (void) sprintf(str, "; s1 = %lu, s2 = %lu", 
                    e.re_lb.s1, e.re_lb.s2);
            str += strlen(str);
            break;
    }

    (void) sprintf(str, "\n");

    return buf;
}
Beispiel #18
0
/*
 * Function: auth_gssapi_create
 *
 * Purpose: Create a GSS-API style authenticator, with all the
 * options, and return the handle.
 *
 * Effects: See design document, section XXX.
 */
AUTH *auth_gssapi_create(
     CLIENT *clnt,
     OM_uint32 *gssstat,
     OM_uint32 *minor_stat,
     gss_cred_id_t claimant_cred_handle,
     gss_name_t target_name,
     gss_OID mech_type,
     OM_uint32 req_flags,
     OM_uint32 time_req,
     gss_OID *actual_mech_type,
     OM_uint32 *ret_flags,
     OM_uint32 *time_rec)
{
     AUTH *auth, *save_auth;
     struct auth_gssapi_data *pdata;
     struct gss_channel_bindings_struct bindings, *bindp;
     struct sockaddr_in laddr, raddr;
     enum clnt_stat callstat;
     struct timeval timeout;
     int bindings_failed;
     rpcproc_t init_func;
     
     auth_gssapi_init_arg call_arg;
     auth_gssapi_init_res call_res;
     gss_buffer_desc *input_token, isn_buf;
     
     memset(&rpc_createerr, 0, sizeof(rpc_createerr));
     
     /* this timeout is only used if clnt_control(clnt, CLSET_TIMEOUT) */
     /* has not already been called.. therefore, we can just pick */
     /* something reasonable-sounding.. */
     timeout.tv_sec = 30;
     timeout.tv_usec = 0;
     
     auth = NULL;
     pdata = NULL;
     
     /* don't assume the caller will want to change clnt->cl_auth */
     save_auth = clnt->cl_auth;

     auth = (AUTH *) malloc(sizeof(*auth));
     pdata = (struct auth_gssapi_data *) malloc(sizeof(*pdata));
     if (auth == NULL || pdata == NULL) {
	  /* They needn't both have failed; clean up.  */
	  free(auth);
	  free(pdata);
	  auth = NULL;
	  pdata = NULL;
	  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
	  rpc_createerr.cf_error.re_errno = ENOMEM;
	  goto cleanup;
     }
     memset((char *) auth, 0, sizeof(*auth));
     memset((char *) pdata, 0, sizeof(*pdata));
     
     auth->ah_ops = &auth_gssapi_ops;
     auth->ah_private = (caddr_t) pdata;
     
     /* initial creds are auth_msg TRUE and no handle */
     marshall_new_creds(auth, TRUE, NULL);
     
     /* initial verifier is empty */
     auth->ah_verf.oa_flavor = AUTH_GSSAPI;
     auth->ah_verf.oa_base = NULL;
     auth->ah_verf.oa_length = 0;
     
     AUTH_PRIVATE(auth)->established = FALSE;
     AUTH_PRIVATE(auth)->clnt = clnt;
     AUTH_PRIVATE(auth)->def_cred = (claimant_cred_handle ==
				     GSS_C_NO_CREDENTIAL);
     
     clnt->cl_auth = auth;

     /* start by trying latest version */
     call_arg.version = 4;
     bindings_failed = 0;

try_new_version:
     /* set state for initial call to init_sec_context */
     input_token = GSS_C_NO_BUFFER;
     AUTH_PRIVATE(auth)->context = GSS_C_NO_CONTEXT;
     init_func = AUTH_GSSAPI_INIT;

#ifdef GSSAPI_KRB5
     /*
      * OV servers up to version 3 used the old mech id.  Beta 7
      * servers used version 3 with the new mech id; however, the beta
      * 7 gss-api accept_sec_context accepts either mech id.  Thus, if
      * any server rejects version 4, we fall back to version 3 with
      * the old mech id; for the OV server it will be right, and for
      * the beta 7 server it will be accepted.  Not ideal, but it
      * works.
      */
     if (call_arg.version < 4 && (mech_type == gss_mech_krb5 ||
				  mech_type == GSS_C_NULL_OID))
	  mech_type = (gss_OID) gss_mech_krb5_old;
#endif

     if (!bindings_failed && call_arg.version >= 3) {
	  if (clnt_control(clnt, CLGET_LOCAL_ADDR, &laddr) == FALSE) {
	       PRINTF(("gssapi_create: CLGET_LOCAL_ADDR failed"));
	       goto cleanup;
	  }
	  if (clnt_control(clnt, CLGET_SERVER_ADDR, &raddr) == FALSE) {
	       PRINTF(("gssapi_create: CLGET_SERVER_ADDR failed"));
	       goto cleanup;
	  }

	  memset(&bindings, 0, sizeof(bindings));
	  bindings.application_data.length = 0;
	  bindings.initiator_addrtype = GSS_C_AF_INET;
	  bindings.initiator_address.length = 4;
	  bindings.initiator_address.value = &laddr.sin_addr.s_addr;
	  
	  bindings.acceptor_addrtype = GSS_C_AF_INET;
	  bindings.acceptor_address.length = 4;
	  bindings.acceptor_address.value = &raddr.sin_addr.s_addr;
	  bindp = &bindings;
     } else {
	  bindp = NULL;
     }
     
     memset((char *) &call_res, 0, sizeof(call_res));
     
next_token:
     *gssstat = gss_init_sec_context(minor_stat,
				     claimant_cred_handle,
				     &AUTH_PRIVATE(auth)->context,
				     target_name,
				     mech_type,
				     req_flags,
				     time_req,
				     bindp,
				     input_token,
				     actual_mech_type,
				     &call_arg.token,
				     ret_flags,
				     time_rec);
     
     if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
	  AUTH_GSSAPI_DISPLAY_STATUS(("initializing context", *gssstat,
				      *minor_stat));
	  goto cleanup;
     }
     
     /* if we got a token, pass it on */
     if (call_arg.token.length != 0) {
	  
	  /*
	   * sanity check: if we received a signed isn in the last
	   * response then there *cannot* be another token to send
	   */
	  if (call_res.signed_isn.length != 0) {
	       PRINTF(("gssapi_create: unexpected token from init_sec\n"));
	       goto cleanup;
	  }
	  
	  PRINTF(("gssapi_create: calling GSSAPI_INIT (%d)\n", init_func));
	  
	  memset((char *) &call_res, 0, sizeof(call_res));
	  callstat = clnt_call(clnt, init_func,
			       xdr_authgssapi_init_arg, &call_arg,
			       xdr_authgssapi_init_res, &call_res,
			       timeout);
	  gss_release_buffer(minor_stat, &call_arg.token);
	  
	  if (callstat != RPC_SUCCESS) {
	       struct rpc_err err;

	       clnt_geterr(clnt, &err);
	       if (callstat == RPC_AUTHERROR &&
		   (err.re_why == AUTH_BADCRED || err.re_why == AUTH_FAILED)
		   && call_arg.version >= 1) {
		    L_PRINTF(1,
			     ("call_arg protocol version %d rejected, trying %d.\n",
			    call_arg.version, call_arg.version-1));
		    call_arg.version--;
		    goto try_new_version;
	       } else {
		    PRINTF(("gssapi_create: GSSAPI_INIT (%d) failed, stat %d\n",
			    init_func, callstat));
	       }
	       
	       goto cleanup;
	  } else if (call_res.version != call_arg.version &&
		     !(call_arg.version == 2 && call_res.version == 1)) {
	       /*
		* The Secure 1.1 servers always respond with version
		* 1.  Thus, if we just tried a version >=3, fall all
		* the way back to version 1 since that is all they
		* understand
		*/
	       if (call_arg.version > 2 && call_res.version == 1) {
		    L_PRINTF(1,
			     ("Talking to Secure 1.1 server, using version 1.\n"));
		    call_arg.version = 1;
		    goto try_new_version;
	       }

	       PRINTF(("gssapi_create: invalid call_res vers %d\n",
		       call_res.version));
	       goto cleanup;
	  } else if (call_res.gss_major != GSS_S_COMPLETE) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("in response from server",
					   call_res.gss_major,
					   call_res.gss_minor));
	       goto cleanup;
	  }
	  
	  PRINTF(("gssapi_create: GSSAPI_INIT (%d) succeeded\n", init_func));
	  init_func = AUTH_GSSAPI_CONTINUE_INIT;
	  
	  /* check for client_handle */
	  if (AUTH_PRIVATE(auth)->client_handle.length == 0) {
	       if (call_res.client_handle.length == 0) {
		    PRINTF(("gssapi_create: expected client_handle\n"));
		    goto cleanup;
	       } else {
		    PRINTF(("gssapi_create: got client_handle %d\n",
			    *((uint32_t *)call_res.client_handle.value)));
		    
		    GSS_DUP_BUFFER(AUTH_PRIVATE(auth)->client_handle,
				   call_res.client_handle);
		    
		    /* auth_msg is TRUE; there may be more tokens */
		    marshall_new_creds(auth, TRUE,
				       &AUTH_PRIVATE(auth)->client_handle); 
	       }
	  } else if (!GSS_BUFFERS_EQUAL(AUTH_PRIVATE(auth)->client_handle,
					call_res.client_handle)) {
	       PRINTF(("gssapi_create: got different client_handle\n"));
	       goto cleanup;
	  }
	  
	  /* check for token */
	  if (call_res.token.length==0 && *gssstat==GSS_S_CONTINUE_NEEDED) {
	       PRINTF(("gssapi_create: expected token\n"));
	       goto cleanup;
	  } else if (call_res.token.length != 0) {
	       if (*gssstat == GSS_S_COMPLETE) {
		    PRINTF(("gssapi_create: got unexpected token\n"));
		    goto cleanup;
	       } else {
		    /* assumes call_res is safe until init_sec_context */
		    input_token = &call_res.token;
		    PRINTF(("gssapi_create: got new token\n"));
	       }
	  }
     }
     
     /* check for isn */
     if (*gssstat == GSS_S_COMPLETE) {
	  if (call_res.signed_isn.length == 0) {
	       PRINTF(("gssapi_created: expected signed isn\n"));
	       goto cleanup;
	  } else {
	       PRINTF(("gssapi_create: processing signed isn\n"));
	       
	       /* don't check conf (integ only) or qop (accpet default) */
	       *gssstat = gss_unseal(minor_stat,
				     AUTH_PRIVATE(auth)->context,
				     &call_res.signed_isn,
				     &isn_buf, NULL, NULL);
	       
	       if (*gssstat != GSS_S_COMPLETE) {
		    AUTH_GSSAPI_DISPLAY_STATUS(("unsealing isn",
						*gssstat, *minor_stat)); 
		    goto cleanup;
	       } else if (isn_buf.length != sizeof(uint32_t)) {
		    PRINTF(("gssapi_create: gss_unseal gave %d bytes\n",
			    (int) isn_buf.length));
		    goto cleanup;
	       }
	       
	       AUTH_PRIVATE(auth)->seq_num = (uint32_t)
		    ntohl(*((uint32_t*)isn_buf.value)); 
	       *gssstat = gss_release_buffer(minor_stat, &isn_buf);
	       if (*gssstat != GSS_S_COMPLETE) {
		    AUTH_GSSAPI_DISPLAY_STATUS(("releasing unsealed isn",
						*gssstat, *minor_stat));
		    goto cleanup;
	       }
	       
	       PRINTF(("gssapi_create: isn is %d\n",
		       AUTH_PRIVATE(auth)->seq_num));
	       
	       /* we no longer need these results.. */
	       xdr_free(xdr_authgssapi_init_res, &call_res);
	  }
     } else if (call_res.signed_isn.length != 0) {
	  PRINTF(("gssapi_create: got signed isn, can't check yet\n"));
     }
     
     /* results were okay.. continue if necessary */
     if (*gssstat == GSS_S_CONTINUE_NEEDED) {
	  PRINTF(("gssapi_create: not done, continuing\n"));
	  goto next_token;
     }
     
     /*
      * Done!  Context is established, we have client_handle and isn.
      */
     AUTH_PRIVATE(auth)->established = TRUE;
     
     marshall_new_creds(auth, FALSE,
			&AUTH_PRIVATE(auth)->client_handle); 
     
     PRINTF(("gssapi_create: done. client_handle %#x, isn %d\n\n",
	     *((uint32_t *)AUTH_PRIVATE(auth)->client_handle.value),
	     AUTH_PRIVATE(auth)->seq_num));
     
     /* don't assume the caller will want to change clnt->cl_auth */
     clnt->cl_auth = save_auth;
     
     return auth;
     
     /******************************************************************/
     
cleanup:
     PRINTF(("gssapi_create: bailing\n\n"));

     if (auth) {
	 if (AUTH_PRIVATE(auth))
	     auth_gssapi_destroy(auth);
	 else
	     free(auth);
	 auth = NULL;
     }
     
     /* don't assume the caller will want to change clnt->cl_auth */
     clnt->cl_auth = save_auth;
     
     if (rpc_createerr.cf_stat == 0)
	  rpc_createerr.cf_stat = RPC_AUTHERROR;
     
     return auth;
}
Beispiel #19
0
/* get the best possible NFS version for a host and transport */
static CLIENT *
amu_clnt_create_best_vers(const char *hostname, u_long program, u_long *out_version, u_long low_version, u_long high_version, const char *nettype)
{
  CLIENT *clnt;
  enum clnt_stat rpc_stat;
  struct rpc_err rpcerr;
  struct timeval tv;
  u_long lo, hi;

  /* 3 seconds is more than enough for a LAN */
  tv.tv_sec = 3;
  tv.tv_usec = 0;

#ifdef HAVE_CLNT_CREATE_TIMED
  clnt = clnt_create_timed(hostname, program, high_version, nettype, &tv);
  if (!clnt) {
    plog(XLOG_INFO, "failed to create RPC client to \"%s\" after %d seconds",
	 hostname, (int) tv.tv_sec);
    return NULL;
  }
#else /* not HAVE_CLNT_CREATE_TIMED */
  /* Solaris 2.3 and earlier didn't have clnt_create_timed() */
  clnt = clnt_create(hostname, program, high_version, nettype);
  if (!clnt) {
    plog(XLOG_INFO, "failed to create RPC client to \"%s\"", hostname);
    return NULL;
  }
#endif /* not HAVE_CLNT_CREATE_TIMED */

  rpc_stat = clnt_call(clnt,
		       NULLPROC,
		       (XDRPROC_T_TYPE) xdr_void,
		       NULL,
		       (XDRPROC_T_TYPE) xdr_void,
		       NULL,
		       tv);
  if (rpc_stat == RPC_SUCCESS) {
    *out_version = high_version;
    return clnt;
  }
  while (low_version < high_version) {
    if (rpc_stat != RPC_PROGVERSMISMATCH)
      break;
    clnt_geterr(clnt, &rpcerr);
    lo = rpcerr.re_vers.low;
    hi = rpcerr.re_vers.high;
    if (hi < high_version)
      high_version = hi;
    else
      high_version--;
    if (lo > low_version)
      low_version = lo;
    if (low_version > high_version)
      goto out;

    CLNT_CONTROL(clnt, CLSET_VERS, (char *)&high_version);
    rpc_stat = clnt_call(clnt,
			 NULLPROC,
			 (XDRPROC_T_TYPE) xdr_void,
			 NULL,
			 (XDRPROC_T_TYPE) xdr_void,
			 NULL,
			 tv);
    if (rpc_stat == RPC_SUCCESS) {
      *out_version = high_version;
      return clnt;
    }
  }
  clnt_geterr(clnt, &rpcerr);

out:
  rpc_createerr.cf_stat = rpc_stat;
  rpc_createerr.cf_error = rpcerr;
  clnt_destroy(clnt);
  return NULL;
}
Beispiel #20
0
static int nfs_probe_statd(void)
{
	struct sockaddr_in addr = {
		.sin_family		= AF_INET,
		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
	};
	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);

	return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
				program, (rpcvers_t)1, IPPROTO_UDP);
}

/**
 * start_statd - attempt to start rpc.statd
 *
 * Returns 1 if statd is running; otherwise zero.
 */
int start_statd(void)
{
#ifdef START_STATD
	struct stat stb;
#endif

	if (nfs_probe_statd())
		return 1;

#ifdef START_STATD
	if (stat(START_STATD, &stb) == 0) {
		if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
			pid_t pid = fork();
			switch (pid) {
			case 0: /* child */
				execl(START_STATD, START_STATD, NULL);
				exit(1);
			case -1: /* error */
				nfs_error(_("fork failed: %s"),
							strerror(errno));
				break;
			default: /* parent */
				waitpid(pid, NULL,0);
				break;
			}
			if (nfs_probe_statd())
				return 1;
		}
	}
#endif

	return 0;
}

/**
 * nfs_advise_umount - ask the server to remove a share from it's rmtab
 * @sap: pointer to IP address of server to call
 * @salen: length of server address
 * @pmap: partially filled-in mountd RPC service tuple
 * @argp: directory path of share to "unmount"
 *
 * Returns one if the unmount call succeeded; zero if the unmount
 * failed for any reason;  rpccreateerr.cf_stat is set to reflect
 * the nature of the error.
 *
 * We use a fast timeout since this call is advisory only.
 */
int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
		      const struct pmap *pmap, const dirpath *argp)
{
	struct sockaddr_storage address;
	struct sockaddr *saddr = (struct sockaddr *)&address;
	struct pmap mnt_pmap = *pmap;
	struct timeval timeout = {
		.tv_sec		= MOUNT_TIMEOUT >> 3,
	};
	CLIENT *client;
	enum clnt_stat res = 0;

	if (nfs_probe_mntport(sap, salen, &mnt_pmap) == 0)
		return 0;

	memcpy(saddr, sap, salen);
	nfs_set_port(saddr, mnt_pmap.pm_port);

	client = nfs_get_rpcclient(saddr, salen, mnt_pmap.pm_prot,
					mnt_pmap.pm_prog, mnt_pmap.pm_vers,
					&timeout);
	if (client == NULL)
		return 0;

	client->cl_auth = authunix_create_default();

	res = CLNT_CALL(client, MOUNTPROC_UMNT,
			(xdrproc_t)xdr_dirpath, (caddr_t)argp,
			(xdrproc_t)xdr_void, NULL,
			timeout);

	auth_destroy(client->cl_auth);
	CLNT_DESTROY(client);

	if (res != RPC_SUCCESS)
		return 0;

	return 1;
}

/**
 * nfs_call_umount - ask the server to remove a share from it's rmtab
 * @mnt_server: address of RPC MNT program server
 * @argp: directory path of share to "unmount"
 *
 * Returns one if the unmount call succeeded; zero if the unmount
 * failed for any reason.
 *
 * Note that a side effect of calling this function is that rpccreateerr
 * is set.
 */
int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
{
	struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
	socklen_t salen = sizeof(mnt_server->saddr);
	struct pmap *pmap = &mnt_server->pmap;
	CLIENT *clnt;
	enum clnt_stat res = 0;
	int msock;

	if (!nfs_probe_mntport(sap, salen, pmap))
		return 0;
	clnt = mnt_openclnt(mnt_server, &msock);
	if (!clnt)
		return 0;
	res = clnt_call(clnt, MOUNTPROC_UMNT,
			(xdrproc_t)xdr_dirpath, (caddr_t)argp,
			(xdrproc_t)xdr_void, NULL,
			TIMEOUT);
	mnt_closeclnt(clnt, msock);

	if (res == RPC_SUCCESS)
		return 1;
	return 0;
}

/**
 * mnt_openclnt - get a handle for a remote mountd service
 * @mnt_server: address and pmap arguments of mountd service
 * @msock: returns a file descriptor of the underlying transport socket
 *
 * Returns an active handle for the remote's mountd service
 */
CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
{
	struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
	struct pmap *mnt_pmap = &mnt_server->pmap;
	CLIENT *clnt = NULL;

	mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
	*msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
				TRUE, FALSE);
	if (*msock == RPC_ANYSOCK) {
		if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
			/*
			 * Probably in-use by a TIME_WAIT connection,
			 * It is worth waiting a while and trying again.
			 */
			rpc_createerr.cf_stat = RPC_TIMEDOUT;
		return NULL;
	}

	switch (mnt_pmap->pm_prot) {
	case IPPROTO_UDP:
		clnt = clntudp_bufcreate(mnt_saddr,
					 mnt_pmap->pm_prog, mnt_pmap->pm_vers,
					 RETRY_TIMEOUT, msock,
					 MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
		break;
	case IPPROTO_TCP:
		clnt = clnttcp_create(mnt_saddr,
				      mnt_pmap->pm_prog, mnt_pmap->pm_vers,
				      msock,
				      MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
		break;
	}
	if (clnt) {
		/* try to mount hostname:dirname */
		clnt->cl_auth = authunix_create_default();
		return clnt;
	}
	return NULL;
}

/**
 * mnt_closeclnt - terminate a handle for a remote mountd service
 * @clnt: pointer to an active handle for a remote mountd service
 * @msock: file descriptor of the underlying transport socket
 *
 */
void mnt_closeclnt(CLIENT *clnt, int msock)
{
	auth_destroy(clnt->cl_auth);
	clnt_destroy(clnt);
	close(msock);
}

/**
 * clnt_ping - send an RPC ping to the remote RPC service endpoint
 * @saddr: server's address
 * @prog: target RPC program number
 * @vers: target RPC version number
 * @prot: target RPC protocol
 * @caddr: filled in with our network address
 *
 * Sigh... GETPORT queries don't actually check the version number.
 * In order to make sure that the server actually supports the service
 * we're requesting, we open an RPC client, and fire off a NULL
 * RPC call.
 *
 * caddr is the network address that the server will use to call us back.
 * On multi-homed clients, this address depends on which NIC we use to
 * route requests to the server.
 *
 * Returns one if successful, otherwise zero.
 */
int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
		const unsigned long vers, const unsigned int prot,
		struct sockaddr_in *caddr)
{
	CLIENT *clnt = NULL;
	int sock, stat;
	static char clnt_res;
	struct sockaddr dissolve;

	rpc_createerr.cf_stat = stat = 0;
	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
	if (sock == RPC_ANYSOCK) {
		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
			/*
			 * TCP timeout. Bubble up the error to see 
			 * how it should be handled.
			 */
			rpc_createerr.cf_stat = RPC_TIMEDOUT;
		}
		return 0;
	}

	if (caddr) {
		/* Get the address of our end of this connection */
		socklen_t len = sizeof(*caddr);
		if (getsockname(sock, caddr, &len) != 0)
			caddr->sin_family = 0;
	}

	switch(prot) {
	case IPPROTO_UDP:
		/* The socket is connected (so we could getsockname successfully),
		 * but some servers on multi-homed hosts reply from
		 * the wrong address, so if we stay connected, we lose the reply.
		 */
		dissolve.sa_family = AF_UNSPEC;
		connect(sock, &dissolve, sizeof(dissolve));

		clnt = clntudp_bufcreate(saddr, prog, vers,
					 RETRY_TIMEOUT, &sock,
					 RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
		break;
	case IPPROTO_TCP:
		clnt = clnttcp_create(saddr, prog, vers, &sock,
				      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
		break;
	}
	if (!clnt) {
		close(sock);
		return 0;
	}
	memset(&clnt_res, 0, sizeof(clnt_res));
	stat = clnt_call(clnt, NULLPROC,
			 (xdrproc_t)xdr_void, (caddr_t)NULL,
			 (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
			 TIMEOUT);
	if (stat) {
		clnt_geterr(clnt, &rpc_createerr.cf_error);
		rpc_createerr.cf_stat = stat;
	}
	clnt_destroy(clnt);
	close(sock);

	if (stat == RPC_SUCCESS)
		return 1;
	else
		return 0;
}
Beispiel #21
0
static int
yppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd)
{
	struct master_yppasswd yppwd;
	struct rpc_err rpcerr;
	struct netconfig *nc = NULL;
	void *localhandle = 0;
	CLIENT *clnt = NULL;
	int ret, *result;

	/* fill the master_yppasswd structure */
	memset(&yppwd, 0, sizeof yppwd);
	yppwd.newpw.pw_uid = pwd->pw_uid;
	yppwd.newpw.pw_gid = pwd->pw_gid;
	yppwd.newpw.pw_change = pwd->pw_change;
	yppwd.newpw.pw_expire = pwd->pw_expire;
	yppwd.newpw.pw_fields = pwd->pw_fields;
	yppwd.oldpass = strdup("");
	yppwd.domain = strdup(ypclnt->domain);
	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL) {
		ypclnt_error(ypclnt, __func__, strerror(errno));
		ret = -1;
		goto done;
	}

	/* connect to rpc.yppasswdd */
	localhandle = setnetconfig();
	while ((nc = getnetconfig(localhandle)) != NULL) {
		if (nc->nc_protofmly != NULL &&
		    strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
			break;
	}
	if (nc == NULL) {
		ypclnt_error(ypclnt, __func__,
		    "getnetconfig: %s", nc_sperror());
		ret = -1;
		goto done;
	}
	if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG,
	    MASTER_YPPASSWDVERS, nc)) == NULL) {
		ypclnt_error(ypclnt, __func__,
		    "failed to connect to rpc.yppasswdd: %s",
		    clnt_spcreateerror(ypclnt->server));
		ret = -1;
		goto done;
	}
	clnt->cl_auth = authunix_create_default();

	/* request the update */
	result = yppasswdproc_update_master_1(&yppwd, clnt);

	/* check for RPC errors */
	clnt_geterr(clnt, &rpcerr);
	if (rpcerr.re_status != RPC_SUCCESS) {
		ypclnt_error(ypclnt, __func__,
		    "NIS password update failed: %s",
		    clnt_sperror(clnt, ypclnt->server));
		ret = -1;
		goto done;
	}

	/* check the result of the update */
	if (result == NULL || *result != 0) {
		ypclnt_error(ypclnt, __func__,
		    "NIS password update failed");
		/* XXX how do we get more details? */
		ret = -1;
		goto done;
	}

	ypclnt_error(ypclnt, NULL, NULL);
	ret = 0;

 done:
	if (clnt != NULL) {
		auth_destroy(clnt->cl_auth);
		clnt_destroy(clnt);
	}
	endnetconfig(localhandle);
	free(yppwd.newpw.pw_name);
	if (yppwd.newpw.pw_passwd != NULL) {
		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
		free(yppwd.newpw.pw_passwd);
	}
	free(yppwd.newpw.pw_class);
	free(yppwd.newpw.pw_gecos);
	free(yppwd.newpw.pw_dir);
	free(yppwd.newpw.pw_shell);
	if (yppwd.oldpass != NULL) {
		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
		free(yppwd.oldpass);
	}
	return (ret);
}
Beispiel #22
0
static int
yppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
{
	struct yppasswd yppwd;
	struct rpc_err rpcerr;
	CLIENT *clnt = NULL;
	int ret, *result;

	/* fill the yppasswd structure */
	memset(&yppwd, 0, sizeof yppwd);
	yppwd.newpw.pw_uid = pwd->pw_uid;
	yppwd.newpw.pw_gid = pwd->pw_gid;
	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
		ypclnt_error(ypclnt, __func__, strerror(errno));
		ret = -1;
		goto done;
	}

	/* connect to rpc.yppasswdd */
	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
	if (clnt == NULL) {
		ypclnt_error(ypclnt, __func__,
		    "failed to connect to rpc.yppasswdd: %s",
		    clnt_spcreateerror(ypclnt->server));
		ret = -1;
		goto done;
	}
	clnt->cl_auth = authunix_create_default();

	/* request the update */
	result = yppasswdproc_update_1(&yppwd, clnt);

	/* check for RPC errors */
	clnt_geterr(clnt, &rpcerr);
	if (rpcerr.re_status != RPC_SUCCESS) {
		ypclnt_error(ypclnt, __func__,
		    "NIS password update failed: %s",
		    clnt_sperror(clnt, ypclnt->server));
		ret = -1;
		goto done;
	}

	/* check the result of the update */
	if (result == NULL || *result != 0) {
		ypclnt_error(ypclnt, __func__,
		    "NIS password update failed");
		/* XXX how do we get more details? */
		ret = -1;
		goto done;
	}

	ypclnt_error(ypclnt, NULL, NULL);
	ret = 0;

 done:
	if (clnt != NULL) {
		auth_destroy(clnt->cl_auth);
		clnt_destroy(clnt);
	}
	free(yppwd.newpw.pw_name);
	if (yppwd.newpw.pw_passwd != NULL) {
		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
		free(yppwd.newpw.pw_passwd);
	}
	free(yppwd.newpw.pw_gecos);
	free(yppwd.newpw.pw_dir);
	free(yppwd.newpw.pw_shell);
	if (yppwd.oldpass != NULL) {
		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
		free(yppwd.oldpass);
	}
	return (ret);
}