Пример #1
0
Файл: tls.c Проект: BHYCHIK/exim
int
auth_tls_server(auth_instance *ablock, uschar *data)
{
auth_tls_options_block * ob = (auth_tls_options_block *)ablock->options_block;

if (ob->server_param1)
  auth_vars[expand_nmax++] = expand_string(ob->server_param1);
if (ob->server_param2)
  auth_vars[expand_nmax++] = expand_string(ob->server_param2);
if (ob->server_param3)
  auth_vars[expand_nmax++] = expand_string(ob->server_param3);
return auth_check_serv_cond(ablock);
}
Пример #2
0
int
auth_cram_md5_server(auth_instance *ablock, uschar *data)
{
auth_cram_md5_options_block *ob =
  (auth_cram_md5_options_block *)(ablock->options_block);
uschar *challenge = string_sprintf("<%d.%ld@%s>", getpid(),
    (long int) time(NULL), primary_hostname);
uschar *clear, *secret;
uschar digest[16];
int i, rc, len;

/* If we are running in the test harness, always send the same challenge,
an example string taken from the RFC. */

if (running_in_test_harness)
  challenge = US"<*****@*****.**>";

/* No data should have been sent with the AUTH command */

if (*data != 0) return UNEXPECTED;

/* Send the challenge, read the return */

if ((rc = auth_get_data(&data, challenge, Ustrlen(challenge))) != OK) return rc;
if ((len = b64decode(data, &clear)) < 0) return BAD64;

/* The return consists of a user name, space-separated from the CRAM-MD5
digest, expressed in hex. Extract the user name and put it in $auth1 and $1.
The former is now the preferred variable; the latter is the original one. Then
check that the remaining length is 32. */

auth_vars[0] = expand_nstring[1] = clear;
while (*clear != 0 && !isspace(*clear)) clear++;
if (!isspace(*clear)) return FAIL;
*clear++ = 0;

expand_nlength[1] = clear - expand_nstring[1] - 1;
if (len - expand_nlength[1] - 1 != 32) return FAIL;
expand_nmax = 1;

/* Expand the server_secret string so that it can compute a value dependent on
the user name if necessary. */

debug_print_string(ablock->server_debug_string);    /* customized debugging */
secret = expand_string(ob->server_secret);

/* A forced fail implies failure of authentication - i.e. we have no secret for
the given name. */

if (secret == NULL)
  {
  if (expand_string_forcedfail) return FAIL;
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

/* Compute the CRAM-MD5 digest that we should have received from the client. */

compute_cram_md5(secret, challenge, digest);

HDEBUG(D_auth)
  {
  uschar buff[64];
  debug_printf("CRAM-MD5: user name = %s\n", auth_vars[0]);
  debug_printf("          challenge = %s\n", challenge);
  debug_printf("          received  = %s\n", clear);
  Ustrcpy(buff,"          digest    = ");
  for (i = 0; i < 16; i++) sprintf(CS buff+22+2*i, "%02x", digest[i]);
  debug_printf("%.54s\n", buff);
  }

/* We now have to compare the digest, which is 16 bytes in binary, with the
data received, which is expressed in lower case hex. We checked above that
there were 32 characters of data left. */

for (i = 0; i < 16; i++)
  {
  int a = *clear++;
  int b = *clear++;
  if (((((a >= 'a')? a - 'a' + 10 : a - '0') << 4) +
        ((b >= 'a')? b - 'a' + 10 : b - '0')) != digest[i]) return FAIL;
  }

/* Expand server_condition as an authorization check */
return auth_check_serv_cond(ablock);
}
Пример #3
0
int
auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
{
auth_cyrus_sasl_options_block *ob =
  (auth_cyrus_sasl_options_block *)(ablock->options_block);
uschar *output, *out2, *input, *clear, *hname;
uschar *debug = NULL;   /* Stops compiler complaining */
sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
sasl_conn_t *conn;
char * realm_expanded = NULL;
int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf;
unsigned int inlen, outlen;

input = data;
inlen = Ustrlen(data);

HDEBUG(D_auth) debug = string_copy(data);

hname = expand_string(ob->server_hostname);
if (hname && ob->server_realm)
  realm_expanded = CS expand_string(ob->server_realm);
if (!hname  ||  !realm_expanded  && ob->server_realm)
  {
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

if (inlen)
  {
  if ((clen = b64decode(input, &clear)) < 0)
    return BAD64;
  input = clear;
  inlen = clen;
  }

if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL library";
  return DEFER;
  }

rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
  NULL, NULL, 0, &conn);

HDEBUG(D_auth)
  debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
      ob->server_service, hname, realm_expanded);

if (rc != SASL_OK )
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
  sasl_done();
  return DEFER;
  }

if (tls_in.cipher)
  {
  if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
    {
    HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
        tls_in.bits, sasl_errstring(rc, NULL, NULL));
    auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
    sasl_done();
    return DEFER;
    }
  else
    HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
  }
else
  HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");

/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
with their iptostring() function, which just wraps
getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
inet_ntop which we wrap in our host_ntoa() function.

So the docs are too strict and we shouldn't worry about :: contractions. */

/* Set properties for remote and local host-ip;port */
for (int i = 0; i < 2; ++i)
  {
  struct sockaddr_storage ss;
  int (*query)(int, struct sockaddr *, socklen_t *);
  int propnum, port;
  const uschar *label;
  uschar *address, *address_port;
  const char *s_err;
  socklen_t sslen;

  if (i)
    {
    query = &getpeername;
    propnum = SASL_IPREMOTEPORT;
    label = CUS"peer";
    }
  else
    {
    query = &getsockname;
    propnum = SASL_IPLOCALPORT;
    label = CUS"local";
    }

  sslen = sizeof(ss);
  if ((rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen)) < 0)
    {
    HDEBUG(D_auth)
      debug_printf("Failed to get %s address information: %s\n",
          label, strerror(errno));
    break;
    }

  address = host_ntoa(-1, &ss, NULL, &port);
  address_port = string_sprintf("%s;%d", address, port);

  if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
    {
    s_err = sasl_errdetail(conn);
    HDEBUG(D_auth)
      debug_printf("Failed to set %s SASL property: [%d] %s\n",
          label, rc, s_err ? s_err : "<unknown reason>");
    break;
    }
  HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
      label, address_port);
  }

for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
  {
  if (firsttime)
    {
    firsttime = 0;
    HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
    rc = sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
           (const char **)(&output), &outlen);
    }
  else
    {
    /* make sure that we have a null-terminated string */
    out2 = string_copyn(output, outlen);

    if ((rc = auth_get_data(&input, out2, outlen)) != OK)
      {
      /* we couldn't get the data, so free up the library before
       * returning whatever error we get */
      sasl_dispose(&conn);
      sasl_done();
      return rc;
      }
    inlen = Ustrlen(input);

    HDEBUG(D_auth) debug = string_copy(input);
    if (inlen)
      {
      if ((clen = b64decode(input, &clear)) < 0)
       {
       sasl_dispose(&conn);
       sasl_done();
       return BAD64;
       }
      input = clear;
      inlen = clen;
      }

    HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
    rc = sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
    }

  if (rc == SASL_BADPROT)
    {
    sasl_dispose(&conn);
    sasl_done();
    return UNEXPECTED;
    }
  if (rc == SASL_CONTINUE)
    continue;

  /* Get the username and copy it into $auth1 and $1. The former is now the
  preferred variable; the latter is the original variable. */

  if ((sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2))) != SASL_OK)
    {
    HDEBUG(D_auth)
      debug_printf("Cyrus SASL library will not tell us the username: %s\n",
	  sasl_errstring(rc, NULL, NULL));
    log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
       "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
       sasl_errstring(rc, NULL, NULL));
    sasl_dispose(&conn);
    sasl_done();
    return FAIL;
    }
  auth_vars[0] = expand_nstring[1] = string_copy(out2);
  expand_nlength[1] = Ustrlen(out2);
  expand_nmax = 1;

  switch (rc)
    {
    case SASL_FAIL: case SASL_BUFOVER: case SASL_BADMAC: case SASL_BADAUTH:
    case SASL_NOAUTHZ: case SASL_ENCRYPT: case SASL_EXPIRED:
    case SASL_DISABLED: case SASL_NOUSER:
      /* these are considered permanent failure codes */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
	 sasl_errstring(rc, NULL, NULL));
      sasl_dispose(&conn);
      sasl_done();
      return FAIL;

    case SASL_NOMECH:
      /* this is a temporary failure, because the mechanism is not
       * available for this user. If it wasn't available at all, we
       * shouldn't have got here in the first place...
       */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      auth_defer_msg =
	  string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
      sasl_dispose(&conn);
      sasl_done();
      return DEFER;

    case SASL_OK:
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
	    ob->server_mech, auth_vars[0]);

      if ((rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)))!= SASL_OK)
	{
	HDEBUG(D_auth)
	  debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
	      sasl_errstring(rc, NULL, NULL));
	log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	    "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
	    sasl_errstring(rc, NULL, NULL));
	sasl_dispose(&conn);
	sasl_done();
	return FAIL;
	}
      negotiated_ssf = *negotiated_ssf_ptr;
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
      if (negotiated_ssf > 0)
	{
	HDEBUG(D_auth)
	  debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
	log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
	    "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
	sasl_dispose(&conn);
	sasl_done();
	return FAIL;
	}

      /* close down the connection, freeing up library's memory */
      sasl_dispose(&conn);
      sasl_done();

      /* Expand server_condition as an authorization check */
      return auth_check_serv_cond(ablock);

    default:
      /* Anything else is a temporary failure, and we'll let SASL print out
       * the error string for us
       */
      HDEBUG(D_auth)
	debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
      auth_defer_msg =
	  string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
      sasl_dispose(&conn);
      sasl_done();
      return DEFER;
    }
  }
/* NOTREACHED */
return 0;  /* Stop compiler complaints */
}
Пример #4
0
int
auth_dovecot_server(auth_instance * ablock, uschar * data)
{
auth_dovecot_options_block *ob =
       (auth_dovecot_options_block *) ablock->options_block;
struct sockaddr_un sa;
uschar buffer[DOVECOT_AUTH_MAXLINELEN];
uschar *args[DOVECOT_AUTH_MAXFIELDCOUNT];
uschar *auth_command;
uschar *auth_extra_data = US"";
uschar *p;
int nargs, tmp;
int crequid = 1, cont = 1, fd = -1, ret = DEFER;
BOOL found = FALSE, have_mech_line = FALSE;

HDEBUG(D_auth) debug_printf("dovecot authentication\n");

if (!data)
  {
  ret = FAIL;
  goto out;
  }

memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;

/* This was the original code here: it is nonsense because strncpy()
does not return an integer. I have converted this to use the function
that formats and checks length. PH */

/*
if (strncpy(sa.sun_path, ob->server_socket, sizeof(sa.sun_path)) < 0) {
}
*/

if (!string_format(US sa.sun_path, sizeof(sa.sun_path), "%s",
		  ob->server_socket))
  {
  auth_defer_msg = US"authentication socket path too long";
  return DEFER;
  }

auth_defer_msg = US"authentication socket connection error";

if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
  return DEFER;

if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
  goto out;

auth_defer_msg = US"authentication socket protocol error";

socket_buffer_left = 0;  /* Global, used to read more than a line but return by line */
while (cont)
  {
  if (dc_gets(buffer, sizeof(buffer), fd) == NULL)
    OUT("authentication socket read error or premature eof");
  p = buffer + Ustrlen(buffer) - 1;
  if (*p != '\n')
    OUT("authentication socket protocol line too long");

  *p = '\0';
  HDEBUG(D_auth) debug_printf("received: %s\n", buffer);

  nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));

  /* HDEBUG(D_auth) debug_strcut(args, nargs, sizeof(args) / sizeof(args[0])); */

  /* Code below rewritten by Kirill Miazine ([email protected]). Only check commands that
    Exim will need. Original code also failed if Dovecot server sent unknown
    command. E.g. COOKIE in version 1.1 of the protocol would cause troubles. */
  /* pdp: note that CUID is a per-connection identifier sent by the server,
    which increments at server discretion.
    By contrast, the "id" field of the protocol is a connection-specific request
    identifier, which needs to be unique per request from the client and is not
    connected to the CUID value, so we ignore CUID from server.  It's purely for
    diagnostics. */

  if (Ustrcmp(args[0], US"VERSION") == 0)
    {
    CHECK_COMMAND("VERSION", 2, 2);
    if (Uatoi(args[1]) != VERSION_MAJOR)
      OUT("authentication socket protocol version mismatch");
    }
  else if (Ustrcmp(args[0], US"MECH") == 0)
    {
    CHECK_COMMAND("MECH", 1, INT_MAX);
    have_mech_line = TRUE;
    if (strcmpic(US args[1], ablock->public_name) == 0)
      found = TRUE;
    }
  else if (Ustrcmp(args[0], US"SPID") == 0)
    {
    /* Unfortunately the auth protocol handshake wasn't designed well
    to differentiate between auth-client/userdb/master. auth-userdb
    and auth-master send VERSION + SPID lines only and nothing
    afterwards, while auth-client sends VERSION + MECH + SPID +
    CUID + more. The simplest way that we can determine if we've
    connected to the correct socket is to see if MECH line exists or
    not (alternatively we'd have to have a small timeout after SPID
    to see if CUID is sent or not). */

    if (!have_mech_line)
      OUT("authentication socket type mismatch"
	" (connected to auth-master instead of auth-client)");
    }
  else if (Ustrcmp(args[0], US"DONE") == 0)
    {
    CHECK_COMMAND("DONE", 0, 0);
    cont = 0;
    }
  }

if (!found)
  {
  auth_defer_msg = string_sprintf(
    "Dovecot did not advertise mechanism \"%s\" to us", ablock->public_name);
  goto out;
  }

/* Added by PH: data must not contain tab (as it is
b64 it shouldn't, but check for safety). */

if (Ustrchr(data, '\t') != NULL)
  {
  ret = FAIL;
  goto out;
  }

/* Added by PH: extra fields when TLS is in use or if the TCP/IP
connection is local. */

if (tls_in.cipher != NULL)
  auth_extra_data = string_sprintf("secured\t%s%s",
     tls_in.certificate_verified? "valid-client-cert" : "",
     tls_in.certificate_verified? "\t" : "");

else if (  interface_address != NULL
        && Ustrcmp(sender_host_address, interface_address) == 0)
  auth_extra_data = US"secured\t";


/****************************************************************************
The code below was the original code here. It didn't work. A reading of the
file auth-protocol.txt.gz that came with Dovecot 1.0_beta8 indicated that
this was not right. Maybe something changed. I changed it to move the
service indication into the AUTH command, and it seems to be better. PH

fprintf(f, "VERSION\t%d\t%d\r\nSERVICE\tSMTP\r\nCPID\t%d\r\n"
       "AUTH\t%d\t%s\trip=%s\tlip=%s\tresp=%s\r\n",
       VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
       ablock->public_name, sender_host_address, interface_address,
       data ? CS  data : "");

Subsequently, the command was modified to add "secured" and "valid-client-
cert" when relevant.
****************************************************************************/

auth_command = string_sprintf("VERSION\t%d\t%d\nCPID\t%d\n"
       "AUTH\t%d\t%s\tservice=smtp\t%srip=%s\tlip=%s\tnologin\tresp=%s\n",
       VERSION_MAJOR, VERSION_MINOR, getpid(), crequid,
       ablock->public_name, auth_extra_data, sender_host_address,
       interface_address, data);

if (write(fd, auth_command, Ustrlen(auth_command)) < 0)
  HDEBUG(D_auth) debug_printf("error sending auth_command: %s\n",
    strerror(errno));

HDEBUG(D_auth) debug_printf("sent: %s", auth_command);

while (1)
  {
  uschar *temp;
  uschar *auth_id_pre = NULL;
  int i;

  if (dc_gets(buffer, sizeof(buffer), fd) == NULL)
    {
    auth_defer_msg = US"authentication socket read error or premature eof";
    goto out;
    }

  buffer[Ustrlen(buffer) - 1] = 0;
  HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
  nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));

  if (Uatoi(args[1]) != crequid)
    OUT("authentication socket connection id mismatch");

  switch (toupper(*args[0]))
    {
    case 'C':
      CHECK_COMMAND("CONT", 1, 2);

      if ((tmp = auth_get_no64_data(&data, US args[2])) != OK)
	{
	ret = tmp;
	goto out;
	}

      /* Added by PH: data must not contain tab (as it is
      b64 it shouldn't, but check for safety). */

      if (Ustrchr(data, '\t') != NULL)
        {
	ret = FAIL;
	goto out;
	}

      temp = string_sprintf("CONT\t%d\t%s\n", crequid, data);
      if (write(fd, temp, Ustrlen(temp)) < 0)
	OUT("authentication socket write error");
      break;

    case 'F':
      CHECK_COMMAND("FAIL", 1, -1);

      for (i=2; (i<nargs) && (auth_id_pre == NULL); i++)
	{
	if ( Ustrncmp(args[i], US"user="******"OK", 2, -1);

      /* Search for the "user=$USER" string in the args array
      and return the proper value.  */

      for (i=2; (i<nargs) && (auth_id_pre == NULL); i++)
	{
	if ( Ustrncmp(args[i], US"user="******"authentication socket protocol error, username missing");

      ret = OK;
      /* fallthrough */

    default:
      goto out;
    }
  }

out:
/* close the socket used by dovecot */
if (fd >= 0)
  close(fd);

/* Expand server_condition as an authorization check */
return ret == OK ? auth_check_serv_cond(ablock) : ret;
}
Пример #5
0
int
auth_spa_server(auth_instance *ablock, uschar *data)
{
auth_spa_options_block *ob = (auth_spa_options_block *)(ablock->options_block);
uint8x lmRespData[24];
uint8x ntRespData[24];
SPAAuthRequest request;
SPAAuthChallenge challenge;
SPAAuthResponse  response;
SPAAuthResponse  *responseptr = &response;
uschar msgbuf[2048];
uschar *clearpass;

/* send a 334, MS Exchange style, and grab the client's request,
unless we already have it via an initial response. */

if ((*data == '\0') &&
    (auth_get_no64_data(&data, US"NTLM supported") != OK))
  {
  /* something borked */
  return FAIL;
  }

if (spa_base64_to_bits((char *)(&request), sizeof(request), (const char *)(data)) < 0)
  {
  DEBUG(D_auth) debug_printf("auth_spa_server(): bad base64 data in "
  "request: %s\n", data);
  return FAIL;
  }

/* create a challenge and send it back */

spa_build_auth_challenge(&request,&challenge);
spa_bits_to_base64 (msgbuf, (unsigned char*)&challenge,
    spa_request_length(&challenge));

if (auth_get_no64_data(&data, msgbuf) != OK)
  {
  /* something borked */
  return FAIL;
  }

/* dump client response */
if (spa_base64_to_bits((char *)(&response), sizeof(response), (const char *)(data)) < 0)
  {
  DEBUG(D_auth) debug_printf("auth_spa_server(): bad base64 data in "
  "response: %s\n", data);
  return FAIL;
  }

/***************************************************************
PH 07-Aug-2003: The original code here was this:

Ustrcpy(msgbuf, unicodeToString(((char*)responseptr) +
  IVAL(&responseptr->uUser.offset,0),
  SVAL(&responseptr->uUser.len,0)/2) );

However, if the response data is too long, unicodeToString bombs out on
an assertion failure. It uses a 1024 fixed buffer. Bombing out is not a good
idea. It's too messy to try to rework that function to return an error because
it is called from a number of other places in the auth-spa.c module. Instead,
since it is a very small function, I reproduce its code here, with a size check
that causes failure if the size of msgbuf is exceeded. ****/

  {
  int i;
  char *p = ((char*)responseptr) + IVAL(&responseptr->uUser.offset,0);
  int len = SVAL(&responseptr->uUser.len,0)/2;

  if (len + 1 >= sizeof(msgbuf)) return FAIL;
  for (i = 0; i < len; ++i)
    {
    msgbuf[i] = *p & 0x7f;
    p += 2;
    }
  msgbuf[i] = 0;
  }

/***************************************************************/

/* Put the username in $auth1 and $1. The former is now the preferred variable;
the latter is the original variable. These have to be out of stack memory, and
need to be available once known even if not authenticated, for error messages
(server_set_id, which only makes it to authenticated_id if we return OK) */

auth_vars[0] = expand_nstring[1] = string_copy(msgbuf);
expand_nlength[1] = Ustrlen(msgbuf);
expand_nmax = 1;

debug_print_string(ablock->server_debug_string);    /* customized debug */

/* look up password */

clearpass = expand_string(ob->spa_serverpassword);
if (clearpass == NULL)
  {
  if (expand_string_forcedfail)
    {
    DEBUG(D_auth) debug_printf("auth_spa_server(): forced failure while "
      "expanding spa_serverpassword\n");
    return FAIL;
    }
  else
    {
    DEBUG(D_auth) debug_printf("auth_spa_server(): error while expanding "
      "spa_serverpassword: %s\n", expand_string_message);
    return DEFER;
    }
  }

/* create local hash copy */

spa_smb_encrypt (clearpass, challenge.challengeData, lmRespData);
spa_smb_nt_encrypt (clearpass, challenge.challengeData, ntRespData);

/* compare NT hash (LM may not be available) */

if (memcmp(ntRespData,
      ((unsigned char*)responseptr)+IVAL(&responseptr->ntResponse.offset,0),
      24) == 0)
  /* success. we have a winner. */
  {
  return auth_check_serv_cond(ablock);
  }

  /* Expand server_condition as an authorization check (PH) */

return FAIL;
}
Пример #6
0
int
auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
{
auth_cyrus_sasl_options_block *ob =
  (auth_cyrus_sasl_options_block *)(ablock->options_block);
uschar *output, *out2, *input, *clear, *hname;
uschar *debug = NULL;   /* Stops compiler complaining */
sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
sasl_conn_t *conn;
int rc, firsttime=1, clen;
unsigned int inlen, outlen;

input=data;
inlen=Ustrlen(data);

HDEBUG(D_auth) debug=string_copy(data);

hname=expand_string(ob->server_hostname);
if(hname == NULL)
  {
  auth_defer_msg = expand_string_message;
  return DEFER;
  }

if(inlen)
  {
  clen=auth_b64decode(input, &clear);
  if(clen < 0)
    {
    return BAD64;
    }
  input=clear;
  inlen=clen;
  }

rc=sasl_server_init(cbs, "exim");
if (rc != SASL_OK)
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL library";
  return DEFER;
  }

rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
  NULL, NULL, 0, &conn);

if( rc != SASL_OK )
  {
  auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
  sasl_done();
  return DEFER;
  }

rc=SASL_CONTINUE;

while(rc==SASL_CONTINUE)
  {
  if(firsttime)
    {
    firsttime=0;
    HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
    rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
           (const char **)(&output), &outlen);
    }
  else
    {
    /* make sure that we have a null-terminated string */
    out2=store_get(outlen+1);
    memcpy(out2,output,outlen);
    out2[outlen]='\0';
    if((rc=auth_get_data(&input, out2, outlen))!=OK)
      {
      /* we couldn't get the data, so free up the library before
       * returning whatever error we get */
      sasl_dispose(&conn);
      sasl_done();
      return rc;
      }
    inlen=Ustrlen(input);

    HDEBUG(D_auth) debug=string_copy(input);
    if(inlen)
      {
      clen=auth_b64decode(input, &clear);
      if(clen < 0)
       {
        sasl_dispose(&conn);
        sasl_done();
       return BAD64;
       }
      input=clear;
      inlen=clen;
      }

    HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
    rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
    }
  if(rc==SASL_BADPROT)
    {
    sasl_dispose(&conn);
    sasl_done();
    return UNEXPECTED;
    }
  else if( rc==SASL_FAIL     || rc==SASL_BUFOVER
       || rc==SASL_BADMAC   || rc==SASL_BADAUTH
       || rc==SASL_NOAUTHZ  || rc==SASL_ENCRYPT
       || rc==SASL_EXPIRED  || rc==SASL_DISABLED
       || rc==SASL_NOUSER   )
    {
    /* these are considered permanent failure codes */
    HDEBUG(D_auth)
      debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
    log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
       "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
       sasl_errstring(rc, NULL, NULL));
    sasl_dispose(&conn);
    sasl_done();
    return FAIL;
    }
  else if(rc==SASL_NOMECH)
    {
    /* this is a temporary failure, because the mechanism is not
     * available for this user. If it wasn't available at all, we
     * shouldn't have got here in the first place...
     */
    HDEBUG(D_auth)
      debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
    auth_defer_msg =
        string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
    sasl_dispose(&conn);
    sasl_done();
    return DEFER;
    }
  else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
    {
    /* Anything else is a temporary failure, and we'll let SASL print out
     * the error string for us
     */
    HDEBUG(D_auth)
      debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
    auth_defer_msg =
        string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
    sasl_dispose(&conn);
    sasl_done();
    return DEFER;
    }
  else if(rc==SASL_OK)
    {
    /* Get the username and copy it into $auth1 and $1. The former is now the
    preferred variable; the latter is the original variable. */
    rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
    auth_vars[0] = expand_nstring[1] = string_copy(out2);
    expand_nlength[1] = Ustrlen(expand_nstring[1]);
    expand_nmax = 1;

    HDEBUG(D_auth)
      debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
    /* close down the connection, freeing up library's memory */
    sasl_dispose(&conn);
    sasl_done();

    /* Expand server_condition as an authorization check */
    return auth_check_serv_cond(ablock);
    }
  }
/* NOTREACHED */
return 0;  /* Stop compiler complaints */
}
Пример #7
0
int
auth_heimdal_gssapi_server(auth_instance *ablock, uschar *initial_data)
{
  gss_name_t gclient = GSS_C_NO_NAME;
  gss_name_t gserver = GSS_C_NO_NAME;
  gss_cred_id_t gcred = GSS_C_NO_CREDENTIAL;
  gss_ctx_id_t gcontext = GSS_C_NO_CONTEXT;
  uschar *ex_server_str;
  gss_buffer_desc gbufdesc = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc gbufdesc_in = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc gbufdesc_out = GSS_C_EMPTY_BUFFER;
  gss_OID mech_type;
  OM_uint32 maj_stat, min_stat;
  int step, error_out, i;
  uschar *tmp1, *tmp2, *from_client;
  auth_heimdal_gssapi_options_block *ob =
    (auth_heimdal_gssapi_options_block *)(ablock->options_block);
  BOOL handled_empty_ir;
  uschar *store_reset_point;
  uschar *keytab;
  uschar sasl_config[4];
  uschar requested_qop;

  store_reset_point = store_get(0);

  HDEBUG(D_auth)
    debug_printf("heimdal: initialising auth context for %s\n", ablock->name);

  /* Construct our gss_name_t gserver describing ourselves */
  tmp1 = expand_string(ob->server_service);
  tmp2 = expand_string(ob->server_hostname);
  ex_server_str = string_sprintf("%s@%s", tmp1, tmp2);
  gbufdesc.value = (void *) ex_server_str;
  gbufdesc.length = Ustrlen(ex_server_str);
  maj_stat = gss_import_name(&min_stat,
      &gbufdesc, GSS_C_NT_HOSTBASED_SERVICE, &gserver);
  if (GSS_ERROR(maj_stat))
    return exim_gssapi_error_defer(store_reset_point, maj_stat, min_stat,
        "gss_import_name(%s)", CS gbufdesc.value);

  /* Use a specific keytab, if specified */
  if (ob->server_keytab) {
    keytab = expand_string(ob->server_keytab);
    maj_stat = gsskrb5_register_acceptor_identity(CCS keytab);
    if (GSS_ERROR(maj_stat))
      return exim_gssapi_error_defer(store_reset_point, maj_stat, min_stat,
          "registering keytab \"%s\"", keytab);
    HDEBUG(D_auth)
      debug_printf("heimdal: using keytab \"%s\"\n", keytab);
  }

  /* Acquire our credentials */
  maj_stat = gss_acquire_cred(&min_stat,
      gserver,             /* desired name */
      0,                   /* time */
      GSS_C_NULL_OID_SET,  /* desired mechs */
      GSS_C_ACCEPT,        /* cred usage */
      &gcred,              /* handle */
      NULL                 /* actual mechs */,
      NULL                 /* time rec */);
  if (GSS_ERROR(maj_stat))
    return exim_gssapi_error_defer(store_reset_point, maj_stat, min_stat,
        "gss_acquire_cred(%s)", ex_server_str);

  maj_stat = gss_release_name(&min_stat, &gserver);

  HDEBUG(D_auth) debug_printf("heimdal: have server credentials.\n");

  /* Loop talking to client */
  step = 0;
  from_client = initial_data;
  handled_empty_ir = FALSE;
  error_out = OK;

  /* buffer sizes: auth_get_data() uses big_buffer, which we grow per
  GSSAPI RFC in _init, if needed, to meet the SHOULD size of 64KB.
  (big_buffer starts life at the MUST size of 16KB). */

  /* step values
  0: getting initial data from client to feed into GSSAPI
  1: iterating for as long as GSS_S_CONTINUE_NEEDED
  2: GSS_S_COMPLETE, SASL wrapping for authz and qop to send to client
  3: unpick final auth message from client
  4: break/finish (non-step)
  */
  while (step < 4) {
    switch (step) {
      case 0:
        if (!from_client || *from_client == '\0') {
          if (handled_empty_ir) {
            HDEBUG(D_auth) debug_printf("gssapi: repeated empty input, grr.\n");
            error_out = BAD64;
            goto ERROR_OUT;
          } else {
            HDEBUG(D_auth) debug_printf("gssapi: missing initial response, nudging.\n");
            error_out = auth_get_data(&from_client, US"", 0);
            if (error_out != OK)
              goto ERROR_OUT;
            handled_empty_ir = TRUE;
            continue;
          }
        }
        /* We should now have the opening data from the client, base64-encoded. */
        step += 1;
        HDEBUG(D_auth) debug_printf("heimdal: have initial client data\n");
        break;

      case 1:
        gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value);
        if (gclient) {
          maj_stat = gss_release_name(&min_stat, &gclient);
          gclient = GSS_C_NO_NAME;
        }
        maj_stat = gss_accept_sec_context(&min_stat,
            &gcontext,          /* context handle */
            gcred,              /* acceptor cred handle */
            &gbufdesc_in,       /* input from client */
            GSS_C_NO_CHANNEL_BINDINGS,  /* XXX fixme: use the channel bindings from GnuTLS */
            &gclient,           /* client identifier */
            &mech_type,         /* mechanism in use */
            &gbufdesc_out,      /* output to send to client */
            NULL,               /* return flags */
            NULL,               /* time rec */
            NULL                /* delegated cred_handle */
            );
        if (GSS_ERROR(maj_stat)) {
          exim_gssapi_error_defer(NULL, maj_stat, min_stat,
              "gss_accept_sec_context()");
          error_out = FAIL;
          goto ERROR_OUT;
        }
        if (&gbufdesc_out.length != 0) {
          error_out = auth_get_data(&from_client,
              gbufdesc_out.value, gbufdesc_out.length);
          if (error_out != OK)
            goto ERROR_OUT;

          gss_release_buffer(&min_stat, &gbufdesc_out);
          EmptyBuf(gbufdesc_out);
        }
        if (maj_stat == GSS_S_COMPLETE) {
          step += 1;
          HDEBUG(D_auth) debug_printf("heimdal: GSS complete\n");
        } else {
          HDEBUG(D_auth) debug_printf("heimdal: need more data\n");
        }
        break;

      case 2:
        memset(sasl_config, 0xFF, 4);
        /* draft-ietf-sasl-gssapi-06.txt defines bitmasks for first octet
        0x01 No security layer
        0x02 Integrity protection
        0x04 Confidentiality protection

        The remaining three octets are the maximum buffer size for wrapped
        content. */
        sasl_config[0] = 0x01;  /* Exim does not wrap/unwrap SASL layers after auth */
        gbufdesc.value = (void *) sasl_config;
        gbufdesc.length = 4;
        maj_stat = gss_wrap(&min_stat,
            gcontext,
            0,                    /* conf_req_flag: integrity only */
            GSS_C_QOP_DEFAULT,    /* qop requested */
            &gbufdesc,            /* message to protect */
            NULL,                 /* conf_state: no confidentiality applied */
            &gbufdesc_out         /* output buffer */
            );
        if (GSS_ERROR(maj_stat)) {
          exim_gssapi_error_defer(NULL, maj_stat, min_stat,
              "gss_wrap(SASL state after auth)");
          error_out = FAIL;
          goto ERROR_OUT;
        }

        HDEBUG(D_auth) debug_printf("heimdal SASL: requesting QOP with no security layers\n");

        error_out = auth_get_data(&from_client,
            gbufdesc_out.value, gbufdesc_out.length);
        if (error_out != OK)
          goto ERROR_OUT;

        gss_release_buffer(&min_stat, &gbufdesc_out);
        EmptyBuf(gbufdesc_out);
        step += 1;
        break;

      case 3:
        gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value);
        maj_stat = gss_unwrap(&min_stat,
            gcontext,
            &gbufdesc_in,       /* data from client */
            &gbufdesc_out,      /* results */
            NULL,               /* conf state */
            NULL                /* qop state */
            );
        if (GSS_ERROR(maj_stat)) {
          exim_gssapi_error_defer(NULL, maj_stat, min_stat,
              "gss_unwrap(final SASL message from client)");
          error_out = FAIL;
          goto ERROR_OUT;
        }
        if (gbufdesc_out.length < 4) {
          HDEBUG(D_auth)
            debug_printf("gssapi: final message too short; "
                "need flags, buf sizes and optional authzid\n");
          error_out = FAIL;
          goto ERROR_OUT;
        }

        requested_qop = (CS gbufdesc_out.value)[0];
        if ((requested_qop & 0x01) == 0) {
          HDEBUG(D_auth)
            debug_printf("gssapi: client requested security layers (%x)\n",
                (unsigned int) requested_qop);
          error_out = FAIL;
          goto ERROR_OUT;
        }

        for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
        expand_nmax = 0;

        /* Identifiers:
        The SASL provided identifier is an unverified authzid.
        GSSAPI provides us with a verified identifier, but it might be empty
        for some clients.
        */

        /* $auth2 is authzid requested at SASL layer */
        if (gbufdesc_out.length > 4) {
          expand_nlength[2] = gbufdesc_out.length - 4;
          auth_vars[1] = expand_nstring[2] =
            string_copyn((US gbufdesc_out.value) + 4, expand_nlength[2]);
          expand_nmax = 2;
        }

        gss_release_buffer(&min_stat, &gbufdesc_out);
        EmptyBuf(gbufdesc_out);

        /* $auth1 is GSSAPI display name */
        maj_stat = gss_display_name(&min_stat,
            gclient,
            &gbufdesc_out,
            &mech_type);
        if (GSS_ERROR(maj_stat)) {
          auth_vars[1] = expand_nstring[2] = NULL;
          expand_nmax = 0;
          exim_gssapi_error_defer(NULL, maj_stat, min_stat,
              "gss_display_name(client identifier)");
          error_out = FAIL;
          goto ERROR_OUT;
        }

        expand_nlength[1] = gbufdesc_out.length;
        auth_vars[0] = expand_nstring[1] =
          string_copyn(gbufdesc_out.value, gbufdesc_out.length);

        if (expand_nmax == 0) { /* should be: authzid was empty */
          expand_nmax = 2;
          expand_nlength[2] = expand_nlength[1];
          auth_vars[1] = expand_nstring[2] = string_copyn(expand_nstring[1], expand_nlength[1]);
          HDEBUG(D_auth)
            debug_printf("heimdal SASL: empty authzid, set to dup of GSSAPI display name\n");
        }

        HDEBUG(D_auth)
          debug_printf("heimdal SASL: happy with client request\n"
             "  auth1 (verified GSSAPI display-name): \"%s\"\n"
             "  auth2 (unverified SASL requested authzid): \"%s\"\n",
             auth_vars[0], auth_vars[1]);

        step += 1;
        break;

    } /* switch */
  } /* while step */


ERROR_OUT:
  maj_stat = gss_release_cred(&min_stat, &gcred);
  if (gclient) {
    gss_release_name(&min_stat, &gclient);
    gclient = GSS_C_NO_NAME;
  }
  if (gbufdesc_out.length) {
    gss_release_buffer(&min_stat, &gbufdesc_out);
    EmptyBuf(gbufdesc_out);
  }
  if (gcontext != GSS_C_NO_CONTEXT) {
    gss_delete_sec_context(&min_stat, &gcontext, GSS_C_NO_BUFFER);
  }

  store_reset(store_reset_point);

  if (error_out != OK)
    return error_out;

  /* Auth succeeded, check server_condition */
  return auth_check_serv_cond(ablock);
}
Пример #8
0
int auth_dovecot_server(auth_instance *ablock, uschar *data)
{
       auth_dovecot_options_block *ob =
               (auth_dovecot_options_block *)(ablock->options_block);
       struct sockaddr_un sa;
       uschar buffer[4096];
       uschar *args[8];
       uschar *auth_command;
       uschar *auth_extra_data = US"";
       int nargs, tmp;
       int cuid = 0, cont = 1, found = 0, fd, ret = DEFER;

       HDEBUG(D_auth) debug_printf("dovecot authentication\n");

       memset(&sa, 0, sizeof(sa));
       sa.sun_family = AF_UNIX;

       /* This was the original code here: it is nonsense because strncpy()
       does not return an integer. I have converted this to use the function
       that formats and checks length. PH */

       /*
       if (strncpy(sa.sun_path, ob->server_socket, sizeof(sa.sun_path)) < 0) {
       */

       if (!string_format(US sa.sun_path, sizeof(sa.sun_path), "%s",
                          ob->server_socket)) {
               auth_defer_msg = US"authentication socket path too long";
               return DEFER;
       }

       auth_defer_msg = US"authentication socket connection error";

       fd = socket(PF_UNIX, SOCK_STREAM, 0);
       if (fd < 0)
               return DEFER;

       if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
               goto out;

       auth_defer_msg = US"authentication socket protocol error";

       sbp = 0;  /* Socket buffer pointer */
       while (cont) {
               if (dc_gets(buffer, sizeof(buffer), fd) == NULL)
                       OUT("authentication socket read error or premature eof");

               buffer[Ustrlen(buffer) - 1] = 0;
               HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
               nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));

               /* Code below rewritten by Kirill Miazine ([email protected]). Only check commands that
                  Exim will need. Original code also failed if Dovecot server sent unknown
                  command. E.g. COOKIE in version 1.1 of the protocol would cause troubles. */
               if (Ustrcmp(args[0], US"CUID") == 0) {
                       CHECK_COMMAND("CUID", 1, 1);
                       cuid = Uatoi(args[1]);
               } else if (Ustrcmp(args[0], US"VERSION") == 0) {
                       CHECK_COMMAND("VERSION", 2, 2);
                       if (Uatoi(args[1]) != VERSION_MAJOR)
                               OUT("authentication socket protocol version mismatch");
               } else if (Ustrcmp(args[0], US"MECH") == 0) {
                       CHECK_COMMAND("MECH", 1, INT_MAX);
                       if (strcmpic(US args[1], ablock->public_name) == 0)
                               found = 1;
               } else if (Ustrcmp(args[0], US"DONE") == 0) {
                       CHECK_COMMAND("DONE", 0, 0);
                       cont = 0;
               }
       }

       if (!found)
               goto out;

       /* Added by PH: data must not contain tab (as it is
       b64 it shouldn't, but check for safety). */

       if (Ustrchr(data, '\t') != NULL) {
               ret = FAIL;
               goto out;
       }

       /* Added by PH: extra fields when TLS is in use or if the TCP/IP
       connection is local. */

       if (tls_cipher != NULL)
               auth_extra_data = string_sprintf("secured\t%s%s",
                   tls_certificate_verified? "valid-client-cert" : "",
                   tls_certificate_verified? "\t" : "");
       else if (interface_address != NULL &&
                Ustrcmp(sender_host_address, interface_address) == 0)
               auth_extra_data = US"secured\t";


/****************************************************************************
   The code below was the original code here. It didn't work. A reading of the
   file auth-protocol.txt.gz that came with Dovecot 1.0_beta8 indicated that
   this was not right. Maybe something changed. I changed it to move the
   service indication into the AUTH command, and it seems to be better. PH

       fprintf(f, "VERSION\t%d\t%d\r\nSERVICE\tSMTP\r\nCPID\t%d\r\n"
               "AUTH\t%d\t%s\trip=%s\tlip=%s\tresp=%s\r\n",
               VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
               ablock->public_name, sender_host_address, interface_address,
               data ? (char *) data : "");

   Subsequently, the command was modified to add "secured" and "valid-client-
   cert" when relevant.

   The auth protocol is documented here:
        http://wiki.dovecot.org/Authentication_Protocol
****************************************************************************/

       auth_command = string_sprintf("VERSION\t%d\t%d\nCPID\t%d\n"
               "AUTH\t%d\t%s\tservice=smtp\t%srip=%s\tlip=%s\tnologin\tresp=%s\n",
               VERSION_MAJOR, VERSION_MINOR, getpid(), cuid,
               ablock->public_name, auth_extra_data, sender_host_address,
               interface_address, data ? (char *) data : "");

       if (write(fd, auth_command, Ustrlen(auth_command)) < 0)
              HDEBUG(D_auth) debug_printf("error sending auth_command: %s\n",
                strerror(errno));

       HDEBUG(D_auth) debug_printf("sent: %s", auth_command);

       while (1) {
               uschar *temp;
               uschar *auth_id_pre = NULL;
               int i;

               if (dc_gets(buffer, sizeof(buffer), fd) == NULL) {
                       auth_defer_msg = US"authentication socket read error or premature eof";
                       goto out;
               }

               buffer[Ustrlen(buffer) - 1] = 0;
               HDEBUG(D_auth) debug_printf("received: %s\n", buffer);
               nargs = strcut(buffer, args, sizeof(args) / sizeof(args[0]));

               if (Uatoi(args[1]) != cuid)
                       OUT("authentication socket connection id mismatch");

               switch (toupper(*args[0])) {
               case 'C':
                       CHECK_COMMAND("CONT", 1, 2);

                       tmp = auth_get_no64_data(&data, US args[2]);
                       if (tmp != OK) {
                               ret = tmp;
                               goto out;
                       }

                       /* Added by PH: data must not contain tab (as it is
                       b64 it shouldn't, but check for safety). */

                       if (Ustrchr(data, '\t') != NULL) {
                               ret = FAIL;
                               goto out;
                       }

                       temp = string_sprintf("CONT\t%d\t%s\n", cuid, data);
                       if (write(fd, temp, Ustrlen(temp)) < 0)
                               OUT("authentication socket write error");
                       break;

               case 'F':
                       CHECK_COMMAND("FAIL", 1, -1);

                       for (i=2; (i<nargs) && (auth_id_pre == NULL); i++)
                       {
                               if ( Ustrncmp(args[i], US"user="******"OK", 2, -1);

                       /*
                        * Search for the "user=$USER" string in the args array
                        * and return the proper value.
                        */
                       for (i=2; (i<nargs) && (auth_id_pre == NULL); i++)
                       {
                               if ( Ustrncmp(args[i], US"user="******"authentication socket protocol error, username missing");

                       ret = OK;
                       /* fallthrough */

               default:
                       goto out;
               }
       }

out:
       /* close the socket used by dovecot */
       if (fd >= 0)
              close(fd);

       /* Expand server_condition as an authorization check */
       return (ret == OK)? auth_check_serv_cond(ablock) : ret;
}