
/*
 * Copyright (c) 2003-2018
 * Distributed Systems Software.  All rights reserved.
 */

/*
 *
 * It does not appear to be necessary to provide a workgroup or domain
 * parameter, but your mileage may vary.
 * This program and the DACS libdsm library have tested successfully against
 * accounts on Windows 10 and Windows Server 2012.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <iconv.h>

#include "libdsm.h"
#include "smb_types.h"

#ifndef lint
static MAYBE_UNUSED const char copyright[] =
"Copyright (c) 2003-2018\n\
Distributed Systems Software.  All rights reserved.";
static MAYBE_UNUSED const char revid[] =
  "$Id: smb_auth.c 2986 2018-01-26 00:26:30Z brachman $";
#endif

static inline MAYBE_UNUSED int
streq(const char *p, const char *q)
{

    return(strcmp(p, q) == 0);
}

static int
resolve_name(char *server_name, struct in_addr *sa)
{
  int gai_code;
  struct addrinfo ai_hints, *ai;
  struct in_addr sin_addr;
  struct sockaddr_in *sai;

  memset(&ai_hints, 0, sizeof(ai_hints));
  ai_hints.ai_family = AF_INET;
  ai_hints.ai_socktype = SOCK_STREAM;
  ai_hints.ai_protocol = IPPROTO_TCP;
  ai_hints.ai_flags = AI_CANONNAME;

  ai = NULL;
  gai_code = getaddrinfo(server_name, NULL, &ai_hints, &ai);
  if (gai_code != 0)
    return(-1);

  sai = (struct sockaddr_in *) ai->ai_addr;
  sin_addr = sai->sin_addr;
  if (sa != NULL)
	*sa = sin_addr;
  freeaddrinfo(ai);

  return(0);
}

/*
 * Microsoft Windows NTLM authentication using the SMB protocol.
 * USERNAME, PASSWORD, and SMB_SERVER (a hostname or IP address) are required,
 * but the others may be NULL.
 * SMB_PORT may specify a particular server port to use, typically
 * 445 (microsoft-ds) or 139 (netbios-ssn), but if it is 0 we will try a
 * sequence of default ports until one works.
 * It seems that SMB_DOMAIN and SMB_WORKGROUP are not needed and may be NULL.
 * The DACS parameter AUX is unused.
 *
 * We assume the caller destroys the plaintext password as soon as we
 * return.
 *
 * Return 0 if authentication succeeds, -1 otherwise.
 * If successful, set IS_GUEST to non-zero if this is a guest account,
 * zero otherwise.
 * If unsuccessful, return any informational message via ERRMSG.
 */
int
local_ntlm_auth_libdsm(char *username, char *password, MAYBE_UNUSED char *aux,
					   int smb_port, char *smb_server, char *smb_domain,
					   char *smb_workgroup, int *is_guest, char **errmsg)
{
  uint32_t rc;	
  struct sockaddr_in addr;
  smb_session *session;

  if (username == NULL || password == NULL || smb_server == NULL) {
	if (errmsg != NULL)
	  *errmsg = "Missing username, password, or smb_server argument";
	return(-1);
  }

  if ((session = smb_session_new()) == NULL) {
	if (errmsg != NULL)
	  *errmsg = "Cannot initialize session";
	return(-1);
  }

  if (resolve_name(smb_server, &addr.sin_addr) == -1) {
	if (errmsg != NULL)
	  *errmsg = "Cannot resolve server name";
	smb_session_destroy(session);
	return(-1);
  }

  if (smb_session_connect(session, smb_workgroup, addr.sin_addr.s_addr,
						  smb_port, SMB_TRANSPORT_TCP)) {
	if (errmsg != NULL)
	  *errmsg = "Cannot connect to server";
	smb_session_destroy(session);
	return(-1);
  }

  smb_session_set_creds(session, smb_domain, username, password);

  if (smb_session_login(session) == DSM_SUCCESS)
	rc = NT_STATUS_SUCCESS;
  else {
	/*
	 * If authentication failed, check if a reason has been set.
	 */
	if ((rc = smb_session_get_nt_status(session)) == NT_STATUS_SUCCESS)
	  rc = NT_STATUS_LOGON_FAILURE;
  }

  if (rc == NT_STATUS_SUCCESS) {
	if (is_guest != NULL)
	  *is_guest = (session->guest != 0);
  }
  else {
	/*
	 * XXX we could try to return rc as a better error indication if it
	 * turns out that would be helpful to the caller.
	 */
	if (errmsg != NULL)
	  *errmsg = "Authentication failed";
  }

  smb_session_destroy(session);

  return((rc == NT_STATUS_SUCCESS) ? 0 : -1);
}

char *
libdsm_version_string(void)
{
  static char buf[512];
  extern int _libiconv_version;

  snprintf(buf, sizeof(buf),
		   "libdsm-DACS-%d.%d.%d (libtasn1-%s, libiconv-%d.%d)",
		   LIBDSM_VERSION_CURRENT, LIBDSM_VERSION_REVISION, LIBDSM_VERSION_AGE,
		   ASN1_VERSION,
		   _libiconv_version >> 8, _libiconv_version & 0xff);
  return(buf);
}
