OpenSolaris_b135/cmd/smbsrv/smbd/smbd_door_ops.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * SMBd door operations
 */
#include <stdlib.h>
#include <synch.h>
#include <strings.h>
#include <smbsrv/smb_common_door.h>
#include <smbsrv/smb_token.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/libsmbns.h>
#include "smbd.h"

static char *smb_dop_user_auth_logon(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_user_nonauth_logon(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_user_auth_logoff(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);

static char *smb_dop_lookup_sid(char *, size_t, door_desc_t *, uint_t,
    size_t *, int *);
static char *smb_dop_lookup_name(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_join(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_get_dcinfo(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);

static char *smb_dop_vss_get_count(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_vss_get_snapshots(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_vss_map_gmttoken(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);
static char *smb_dop_ads_find_host(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err);

/* SMB daemon's door operation table */
smb_dr_op_t smb_doorsrv_optab[] =
{
	smb_dop_user_auth_logon,	/* SMB_DR_USER_AUTH_LOGON */
	smb_dop_user_nonauth_logon,	/* SMB_DR_USER_NONAUTH_LOGON */
	smb_dop_user_auth_logoff,	/* SMB_DR_USER_AUTH_LOGOFF */
	smb_dop_lookup_sid,		/* SMB_DR_LOOKUP_SID */
	smb_dop_lookup_name,		/* SMB_DR_LOOKUP_NAME */
	smb_dop_join,			/* SMB_DR_JOIN */
	smb_dop_get_dcinfo,		/* SMB_DR_GET_DCINFO */
	smb_dop_vss_get_count,		/* SMB_DR_VSS_GET_COUNT */
	smb_dop_vss_get_snapshots,	/* SMB_DR_VSS_GET_SNAPSHOTS */
	smb_dop_vss_map_gmttoken,	/* SMB_DR_VSS_MAP_GMTTOKEN */
	smb_dop_ads_find_host		/* SMB_DR_ADS_FIND_HOST */
};

/*ARGSUSED*/
static char *
smb_dop_user_nonauth_logon(char *argp, size_t arg_size, door_desc_t *dp,
    uint_t n_desc, size_t *rbufsize, int *err)
{
	char *buf;
	uint32_t sid;

	if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &sid) != 0) {
		*rbufsize = 0;
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	smbd_user_nonauth_logon(sid);

	if ((buf = smb_dr_set_res_stat(SMB_DR_OP_SUCCESS, rbufsize)) == NULL) {
		*rbufsize = 0;
		*err = SMB_DR_OP_ERR_ENCODE;
		return (NULL);
	}

	*err = SMB_DR_OP_SUCCESS;
	return (buf);
}

/*ARGSUSED*/
static char *
smb_dop_user_auth_logoff(char *argp, size_t arg_size, door_desc_t *dp,
    uint_t n_desc, size_t *rbufsize, int *err)
{
	char *buf;
	uint32_t sid;

	if (smb_dr_decode_common(argp, arg_size, xdr_uint32_t, &sid) != 0) {
		*rbufsize = 0;
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	smbd_user_auth_logoff(sid);

	if ((buf = smb_dr_set_res_stat(SMB_DR_OP_SUCCESS, rbufsize)) == NULL) {
		*rbufsize = 0;
		*err = SMB_DR_OP_ERR_ENCODE;
		return (NULL);
	}

	*err = SMB_DR_OP_SUCCESS;
	return (buf);
}

/*
 * smb_dr_is_valid_opcode
 *
 * Validates the given door opcode.
 */
int
smb_dr_is_valid_opcode(int opcode)
{
	if (opcode < 0 ||
	    opcode > (sizeof (smb_doorsrv_optab) / sizeof (smb_dr_op_t)))
		return (-1);
	else
		return (0);
}

/*
 * Obtains an access token on successful user authentication.
 */
/*ARGSUSED*/
static char *
smb_dop_user_auth_logon(char *argp, size_t arg_size, door_desc_t *dp,
    uint_t n_desc, size_t *rbufsize, int *err)
{
	netr_client_t *clnt_info;
	smb_token_t *token;
	char *buf;

	*rbufsize = 0;
	*err = 0;
	clnt_info = smb_dr_decode_arg_get_token(argp, arg_size);
	if (clnt_info == NULL) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	token = smbd_user_auth_logon(clnt_info);

	netr_client_xfree(clnt_info);

	if (!token) {
		*err = SMB_DR_OP_ERR_EMPTYBUF;
		return (NULL);
	}

	if ((buf = smb_dr_encode_res_token(token, rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
	}

	smb_token_destroy(token);
	return (buf);
}

/*ARGSUSED*/
static char *
smb_dop_lookup_name(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	smb_domain_t	dinfo;
	smb_account_t	ainfo;
	lsa_account_t	acct;
	char		buf[MAXNAMELEN];
	char		*rbuf = NULL;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	if (smb_dr_decode_common(argp, arg_size, lsa_account_xdr, &acct) != 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	if (*acct.a_domain == '\0')
		(void) snprintf(buf, MAXNAMELEN, "%s", acct.a_name);
	else if (strchr(acct.a_domain, '.') != NULL)
		(void) snprintf(buf, MAXNAMELEN, "%s@%s", acct.a_name,
		    acct.a_domain);
	else
		(void) snprintf(buf, MAXNAMELEN, "%s\\%s", acct.a_domain,
		    acct.a_name);

	acct.a_status = lsa_lookup_name(buf, acct.a_sidtype, &ainfo);
	if (acct.a_status == NT_STATUS_SUCCESS) {
		acct.a_sidtype = ainfo.a_type;
		smb_sid_tostr(ainfo.a_sid, acct.a_sid);
		(void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN);

		if (smb_domain_lookup_name(ainfo.a_domain, &dinfo))
			(void) strlcpy(acct.a_domain, dinfo.di_fqname,
			    MAXNAMELEN);
		else
			(void) strlcpy(acct.a_domain, ainfo.a_domain,
			    MAXNAMELEN);
		smb_account_free(&ainfo);
	}

	if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &acct,
	    lsa_account_xdr, rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}

	return (rbuf);
}

/*ARGSUSED*/
static char *
smb_dop_lookup_sid(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	smb_domain_t	dinfo;
	smb_account_t	ainfo;
	lsa_account_t	acct;
	smb_sid_t	*sid;
	char		*rbuf = NULL;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	if (smb_dr_decode_common(argp, arg_size, lsa_account_xdr, &acct) != 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	sid = smb_sid_fromstr(acct.a_sid);
	acct.a_status = lsa_lookup_sid(sid, &ainfo);
	smb_sid_free(sid);

	if (acct.a_status == NT_STATUS_SUCCESS) {
		acct.a_sidtype = ainfo.a_type;
		smb_sid_tostr(ainfo.a_sid, acct.a_sid);
		(void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN);

		if (smb_domain_lookup_name(ainfo.a_domain, &dinfo))
			(void) strlcpy(acct.a_domain, dinfo.di_fqname,
			    MAXNAMELEN);
		else
			(void) strlcpy(acct.a_domain, ainfo.a_domain,
			    MAXNAMELEN);

		smb_account_free(&ainfo);
	}

	if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &acct,
	    lsa_account_xdr, rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}

	return (rbuf);
}

/*ARGSUSED*/
static char *
smb_dop_join(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	smb_joininfo_t jdi;
	uint32_t status;
	char *rbuf = NULL;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	if (smb_dr_decode_common(argp, arg_size, xdr_smb_dr_joininfo_t, &jdi)
	    != 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	status = smbd_join(&jdi);

	if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &status,
	    xdr_uint32_t, rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}

	return (rbuf);
}

/*ARGSUSED*/
static char *
smb_dop_get_dcinfo(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	char *rbuf = NULL;
	smb_domainex_t dxi;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	if (!smb_domain_getinfo(&dxi)) {
		*err = SMB_DR_OP_ERR_EMPTYBUF;
		return (NULL);
	}

	if ((rbuf = smb_dr_encode_string(SMB_DR_OP_SUCCESS, dxi.d_dc,
	    rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}
	return (rbuf);
}

/*
 * This routine returns the number of snapshots for a dataset
 */

/*ARGSUSED*/
static char *
smb_dop_vss_get_count(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	char *rbuf = NULL;
	uint32_t count;
	char *path;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	if ((path = smb_dr_decode_string(argp, arg_size)) == NULL) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	if (smbd_vss_get_count(path, &count) == 0) {
		if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &count,
		    xdr_uint32_t, rbufsize)) == NULL) {
			*err = SMB_DR_OP_ERR_ENCODE;
		}
	}

	xdr_free(xdr_string, (char *)&path);

	return (rbuf);
}

/*
 * This routine returns the count and list of snapshots.
 * The list is in the Microsoft @GMT token format.
 */
/*ARGSUSED*/
static char *
smb_dop_vss_get_snapshots(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	char *rbuf = NULL, **gmtp;
	smb_dr_get_gmttokens_t request;
	smb_dr_return_gmttokens_t reply;
	uint_t i;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;
	bzero(&request, sizeof (smb_dr_get_gmttokens_t));
	bzero(&reply, sizeof (smb_dr_return_gmttokens_t));

	if (smb_dr_decode_common(argp, arg_size,
	    xdr_smb_dr_get_gmttokens_t, &request) != 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	reply.rg_gmttokens.rg_gmttokens_val = malloc(request.gg_count *
	    sizeof (char *));
	bzero(reply.rg_gmttokens.rg_gmttokens_val, request.gg_count *
	    sizeof (char *));

	if (reply.rg_gmttokens.rg_gmttokens_val == NULL) {
		xdr_free(xdr_smb_dr_get_gmttokens_t, (char *)&request);
		return (NULL);
	}

	smbd_vss_get_snapshots(request.gg_path, request.gg_count,
	    &reply.rg_count,
	    &reply.rg_gmttokens.rg_gmttokens_len,
	    reply.rg_gmttokens.rg_gmttokens_val);

	if ((rbuf = smb_dr_encode_common(SMB_DR_OP_SUCCESS, &reply,
	    xdr_smb_dr_return_gmttokens_t, rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
	}

	for (i = 0, gmtp = reply.rg_gmttokens.rg_gmttokens_val;
	    (i < request.gg_count); i++) {
		if (*gmtp)
			free(*gmtp);
		gmtp++;
	}
	free(reply.rg_gmttokens.rg_gmttokens_val);
	xdr_free(xdr_smb_dr_get_gmttokens_t, (char *)&request);
	return (rbuf);
}

/*
 * This routine returns the snapshot name of the snapshot
 * that matches path of the pathname of the dataset and
 * the @GMT token.
 */

/*ARGSUSED*/
static char *
smb_dop_vss_map_gmttoken(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	char *rbuf = NULL;
	char *snapname;
	smb_dr_map_gmttoken_t request;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;
	bzero(&request, sizeof (smb_dr_map_gmttoken_t));

	if (smb_dr_decode_common(argp, arg_size, xdr_smb_dr_map_gmttoken_t,
	    &request) != 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		xdr_free(xdr_smb_dr_map_gmttoken_t, (char *)&request);
		return (NULL);
	}

	snapname = (char *)malloc(MAXPATHLEN);

	if (snapname == NULL) {
		xdr_free(xdr_smb_dr_map_gmttoken_t, (char *)&request);
		return (NULL);
	}

	if ((smbd_vss_map_gmttoken(request.mg_path, request.mg_gmttoken,
	    snapname) != 0)) {
		*snapname = '\0';
	}

	rbuf = smb_dr_encode_string(SMB_DR_OP_SUCCESS, snapname, rbufsize);

	if (rbuf == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}

	xdr_free(xdr_smb_dr_map_gmttoken_t, (char *)&request);
	free(snapname);
	return (rbuf);
}

/*ARGSUSED*/
static char *
smb_dop_ads_find_host(char *argp, size_t arg_size,
    door_desc_t *dp, uint_t n_desc, size_t *rbufsize, int *err)
{
	smb_ads_host_info_t *hinfo = NULL;
	char *hostname;
	char *fqdn = NULL;
	char *rbuf = NULL;

	*err = SMB_DR_OP_SUCCESS;
	*rbufsize = 0;

	/* Decode */
	if ((fqdn = smb_dr_decode_string(argp, arg_size)) == 0) {
		*err = SMB_DR_OP_ERR_DECODE;
		return (NULL);
	}

	if ((hinfo = smb_ads_find_host(fqdn, NULL)) == NULL)
		hostname = "";
	else
		hostname = hinfo->name;

	xdr_free(xdr_string, (char *)&fqdn);

	/* Encode the result and return */
	if ((rbuf = smb_dr_encode_string(SMB_DR_OP_SUCCESS, hostname,
	    rbufsize)) == NULL) {
		*err = SMB_DR_OP_ERR_ENCODE;
		*rbufsize = 0;
	}

	free(hinfo);
	return (rbuf);
}