#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <libintl.h>
#include <xti.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/byteorder.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <uuid/uuid.h>
#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include <netsmb/mchain.h>
#include <netsmb/netbios.h>
#include <netsmb/nb_lib.h>
#include <netsmb/smb_dev.h>
#include <cflib.h>
#include "charsets.h"
#include "private.h"
#include "smb_crypt.h"
static int
smb__ssnsetup(struct smb_ctx *ctx,
struct mbdata *mbc1, struct mbdata *mbc2);
int smb_ssnsetup_spnego(struct smb_ctx *, struct mbdata *);
const char *
smb_iod_state_name(enum smbiod_state st)
{
const char *n = "(?)";
switch (st) {
case SMBIOD_ST_UNINIT:
n = "UNINIT!";
break;
case SMBIOD_ST_IDLE:
n = "IDLE";
break;
case SMBIOD_ST_RECONNECT:
n = "RECONNECT";
break;
case SMBIOD_ST_RCFAILED:
n = "RCFAILED";
break;
case SMBIOD_ST_CONNECTED:
n = "CONNECTED";
break;
case SMBIOD_ST_NEGOTIATED:
n = "NEGOTIATED";
break;
case SMBIOD_ST_AUTHCONT:
n = "AUTHCONT";
break;
case SMBIOD_ST_AUTHFAIL:
n = "AUTHFAIL";
break;
case SMBIOD_ST_AUTHOK:
n = "AUTHOK";
break;
case SMBIOD_ST_VCACTIVE:
n = "VCACTIVE";
break;
case SMBIOD_ST_DEAD:
n = "DEAD";
break;
}
return (n);
}
int
smb_iod_connect(smb_ctx_t *ctx)
{
smbioc_ossn_t *ossn = &ctx->ct_ssn;
smbioc_ssn_work_t *work = &ctx->ct_work;
char *uuid_str;
int err;
struct mbdata blob;
char *nego_buf = NULL;
uint32_t nego_len;
memset(&blob, 0, sizeof (blob));
if (ctx->ct_srvname[0] == '\0') {
DPRINT("sername not set!");
return (EINVAL);
}
DPRINT("server: %s", ctx->ct_srvname);
if (smb_debug)
dump_ctx("smb_iod_connect", ctx);
if (ctx->ct_locname == NULL) {
err = smb_getlocalname(&ctx->ct_locname);
if (err) {
smb_error(dgettext(TEXT_DOMAIN,
"can't get local name"), err);
return (err);
}
}
uuid_str = cf_get_client_uuid();
if (uuid_str == NULL) {
err = EINVAL;
smb_error(dgettext(TEXT_DOMAIN,
"can't get local UUID"), err);
return (err);
}
(void) uuid_parse(uuid_str, ctx->ct_work.wk_cl_guid);
free(uuid_str);
uuid_str = NULL;
ctx->ct_flags |= SMBCF_RESOLVED;
DPRINT("Try ioctl connect...");
if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_CONNECT, work) < 0) {
err = errno;
smb_error(dgettext(TEXT_DOMAIN,
"%s: connect failed"),
err, ossn->ssn_srvname);
return (err);
}
DPRINT("Connect OK, new state=%s",
smb_iod_state_name(work->wk_out_state));
nego_len = 4096;
err = mb_init_sz(&blob, nego_len);
if (err)
goto out;
nego_buf = blob.mb_top->m_data;
work->wk_u_auth_rbuf.lp_ptr = nego_buf;
work->wk_u_auth_rlen = nego_len;
DPRINT("Try ioctl negotiate...");
if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_NEGOTIATE, work) < 0) {
err = errno;
smb_error(dgettext(TEXT_DOMAIN,
"%s: negotiate failed"),
err, ossn->ssn_srvname);
goto out;
}
DPRINT("Negotiate OK, new state=%s",
smb_iod_state_name(work->wk_out_state));
nego_len = work->wk_u_auth_rlen;
blob.mb_top->m_len = nego_len;
if (smb_debug) {
DPRINT("Sec. blob: %d", nego_len);
smb_hexdump(nego_buf, nego_len);
}
DPRINT("Do session setup...");
err = smb_ssnsetup_spnego(ctx, &blob);
if (err != 0) {
DPRINT("Session setup err=%d", err);
goto out;
}
DPRINT("Session setup OK");
out:
mb_done(&blob);
return (err);
}
int
smb_ssnsetup_spnego(struct smb_ctx *ctx, struct mbdata *hint_mb)
{
struct mbdata send_mb, recv_mb;
smbioc_ssn_work_t *work = &ctx->ct_work;
int err;
bzero(&send_mb, sizeof (send_mb));
bzero(&recv_mb, sizeof (recv_mb));
err = ssp_ctx_create_client(ctx, hint_mb);
if (err)
goto out;
err = ssp_ctx_next_token(ctx, NULL, &send_mb);
if (err) {
DPRINT("smb__ssnsetup, ssp next, err=%d", err);
goto out;
}
for (;;) {
err = smb__ssnsetup(ctx, &send_mb, &recv_mb);
DPRINT("smb__ssnsetup rc=%d, new state=%s", err,
smb_iod_state_name(work->wk_out_state));
if (err == 0) {
if (work->wk_out_state != SMBIOD_ST_AUTHOK) {
DPRINT("Wrong state (expected AUTHOK)");
}
break;
}
if (err != EINPROGRESS) {
if (work->wk_out_state != SMBIOD_ST_AUTHFAIL) {
DPRINT("Wrong state (expected AUTHFAIL)");
}
goto out;
}
if (work->wk_out_state != SMBIOD_ST_AUTHCONT) {
DPRINT("Wrong state (expected AUTHCONT)");
}
err = ssp_ctx_next_token(ctx, &recv_mb, &send_mb);
if (err) {
DPRINT("smb__ssnsetup, ssp next, err=%d", err);
goto out;
}
}
(void) ssp_ctx_next_token(ctx, &recv_mb, NULL);
out:
ssp_ctx_destroy(ctx);
return (err);
}
int smb_max_authtok_sz = 0x10000;
static int
smb__ssnsetup(struct smb_ctx *ctx,
struct mbdata *send_mb, struct mbdata *recv_mb)
{
smbioc_ossn_t *ossn = &ctx->ct_ssn;
smbioc_ssn_work_t *work = &ctx->ct_work;
mbuf_t *m;
int err;
err = mb_init_sz(recv_mb, smb_max_authtok_sz);
if (err != 0)
return (err);
m = recv_mb->mb_top;
work->wk_u_auth_rbuf.lp_ptr = m->m_data;
work->wk_u_auth_rlen = m->m_maxlen;
m = send_mb->mb_top;
work->wk_u_auth_wbuf.lp_ptr = m->m_data;
work->wk_u_auth_wlen = m->m_len;
DPRINT("Session setup ioctl...");
if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_IOD_SSNSETUP, work) < 0) {
err = errno;
if (err != 0 && err != EINPROGRESS) {
smb_error(dgettext(TEXT_DOMAIN,
"%s: session setup "),
err, ossn->ssn_srvname);
}
}
DPRINT("Session setup ret %d", err);
mb_done(send_mb);
m = recv_mb->mb_top;
m->m_len = work->wk_u_auth_rlen;
return (err);
}