예제 #1
0
int capabilities(struct protstream *conn, sasl_conn_t *saslconn,
		 int starttls_done, int authenticated, sasl_ssf_t sasl_ssf)
{
    const char *sasllist;
    int mechcount;

    /* implementation */
    prot_printf(conn,
		"\"IMPLEMENTATION\" \"Cyrus timsieved%s %s\"\r\n",
		config_mupdate_server ? " (Murder)" : "", cyrus_version());
    
    /* SASL */
    if ((!authenticated || sasl_ssf) &&
	sasl_listmech(saslconn, NULL, 
		    "\"SASL\" \"", " ", "\"\r\n",
		    &sasllist,
		      NULL, &mechcount) == SASL_OK/* && mechcount > 0*/)
    {
      prot_printf(conn,"%s",sasllist);
    }
    
    /* Sieve capabilities */
    prot_printf(conn,"\"SIEVE\" \"%s\"\r\n",sieve_listextensions(interp));

    if (tls_enabled() && !starttls_done && !authenticated) {
	prot_printf(conn, "\"STARTTLS\"\r\n");
    }

    prot_printf(conn,"OK\r\n");

    return TIMSIEVE_OK;
}
예제 #2
0
/**
 * Generate a "TLS" child in the root if the node supports TLS connections.
 * This is a documented GTKG extension.
 */
static void
g2_build_add_tls(g2_tree_t *t)
{
	if (tls_enabled()) {
		g2_tree_t *c = g2_tree_alloc_empty("TLS");
		g2_tree_add_child(t, c);
	}
}
예제 #3
0
파일: lex.c 프로젝트: gnb/cyrus-imapd
int token_lookup (char *str, int len __attribute__((unused)))
{
    switch (*str) {
    case 'a':
	if (strcmp(str, "authenticate")==0) return AUTHENTICATE;
	break;

    case 'c':
	if (strcmp(str, "capability")==0) return CAPABILITY;
	break;

    case 'd':
	if (strcmp(str, "deletescript")==0) return DELETESCRIPT;
	break;

    case 'g':
	if (strcmp(str, "getscript")==0) return GETSCRIPT;
	break;

    case 'h':
	if (strcmp(str, "havespace")==0) return HAVESPACE;
	break;

    case 'l':
	if (strcmp(str, "listscripts")==0) return LISTSCRIPTS;
	if (strcmp(str, "logout")==0) return LOGOUT;
	break;

    case 'n':
	if (strcmp(str, "noop")==0) return NOOP;
	break;

    case 'p':
	if (strcmp(str, "putscript")==0) return PUTSCRIPT;
	break;

    case 's':
	if (strcmp(str, "setactive")==0) return SETACTIVE;
	if (strcmp(str, "starttls")==0 && tls_enabled())
	    return STARTTLS;
	break;

    case 'u':
	if (strcmp(str, "unauthenticate")==0) return UNAUTHENTICATE;
	break;
    }

    /* error, nothing matched */
    return -1;
}
예제 #4
0
/* Returns TRUE if we are done */
int parser(struct protstream *sieved_out, struct protstream *sieved_in)
{
  int token = EOL;
  const char *error_msg = "Generic Error";

  struct buf mechanism_name = BUF_INITIALIZER;
  struct buf initial_challenge = BUF_INITIALIZER;
  struct buf sieve_name = BUF_INITIALIZER;
  struct buf sieve_data = BUF_INITIALIZER;
  unsigned long num;
  int ret = FALSE;

  /* get one token from the lexer */
  while(token == EOL) 
      token = timlex(NULL, NULL, sieved_in);

  if (!authenticated && (token > 255) && (token!=AUTHENTICATE) &&
      (token!=LOGOUT) && (token!=CAPABILITY) &&
      (token!=NOOP) && (token!=CHECKSCRIPT) &&
      (!tls_enabled() || (token!=STARTTLS)))
  {
    error_msg = "Authenticate first";
    if (token!=EOL)
      lex_setrecovering();

    goto error;
  }

  if (verify_only && (token > 255) && (token!=CHECKSCRIPT)
    && (token!=PUTSCRIPT) && (token!=LOGOUT))
  {
    error_msg = "Script verification only";
    if (token!=EOL)
      lex_setrecovering();

    goto error;
  }

  switch (token)
  {
  case EOF:
      /* timlex() will return EOF when the remote disconnects badly */
      syslog(LOG_WARNING, "Lost connection to client -- exiting");
      prot_printf(sieved_out, "BYE \"Shutdown TCP timeout\"\r\n");
      ret = TRUE;
      goto done;
      break;

  case AUTHENTICATE:
    if (sieved_tls_required) {
      error_msg = "AUTHENTICATE only available under a layer";
      goto error;
    }
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after AUTHENTICATE";
      goto error;
    }

    if (timlex(&mechanism_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify mechanism name";
      goto error;
    }

    token = timlex(NULL, NULL, sieved_in);

    if (token != EOL)
    {
      /* optional client first challenge */
      if (token!=SPACE)
      {
	error_msg = "Expected SPACE";
	goto error;
      }

      if (timlex(&initial_challenge, NULL, sieved_in)!=STRING)
      {
	error_msg = "Expected string";
	goto error;
      }

      token = timlex(NULL, NULL, sieved_in);      
    }

    if (token != EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if (authenticated)
	prot_printf(sieved_out, "NO \"Already authenticated\"\r\n");
    else if (cmd_authenticate(sieved_out, sieved_in, mechanism_name.s,
			      &initial_challenge, &error_msg)==FALSE)
    {
	error_msg = "Authentication Error";
	goto error;
    }

#if 0 /* XXX - not implemented in sieveshell*/
    /* referral_host is non-null only once we are authenticated */
    if(referral_host)
	goto do_referral;
#endif
    break;

  case CAPABILITY:
      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }

      if(referral_host)
	  goto do_referral;

      capabilities(sieved_out, sieved_saslconn, starttls_done, authenticated,
		   sasl_ssf);
      break;

  case CHECKSCRIPT:
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
          error_msg = "SPACE must occur after CHECKSCRIPT";
          goto error;
      }

      if (timlex(&sieve_data, NULL, sieved_in)!=STRING)
      {
          error_msg = "Expected script content as second parameter";
          goto error;
      }

      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
        error_msg = "Expected EOL";
        goto error;
      }

      /* f stands for "f"aked name, it could be any valid script name */
      buf_reset(&sieve_name);
      buf_appendcstr(&sieve_name, "f");
      putscript(sieved_out, &sieve_name, &sieve_data, /* verify_only */ 1);
      break;

  case HAVESPACE:
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
	  error_msg = "SPACE must occur after HAVESPACE";
	  goto error;
      }
      
      if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
      {
	  error_msg = "Did not specify script name";
	  goto error;
      }
      
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
	  error_msg = "Expected SPACE after SCRIPTNAME";
	  goto error;
      }
      
      if (timlex(NULL, &num, sieved_in)!=NUMBER)
      {
	  error_msg = "Expected Number";
	  goto error;
      }

      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }

      if(referral_host)
	  goto do_referral;

      cmd_havespace(sieved_out, &sieve_name, num);

      break;

  case LOGOUT:
      token = timlex(NULL, NULL, sieved_in);
      
      /* timlex() will return LOGOUT when the remote disconnects badly */
      if (token!=EOL && token!=EOF && token!=LOGOUT)
      {
	  error_msg = "Garbage after logout command";
	  goto error;
      }

      /* no referral for logout */

      cmd_logout(sieved_out, sieved_in);
      
      ret = TRUE;
      goto done;
      break;

  case GETSCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after GETSCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    getscript(sieved_out, &sieve_name);
    
    break;


  case PUTSCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after PUTSCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "Expected SPACE";
      goto error;
    }

    if (timlex(&sieve_data, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify legal script data length";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    putscript(sieved_out, &sieve_name, &sieve_data, verify_only);
    
    break;

  case SETACTIVE:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after SETACTIVE";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    setactive(sieved_out, &sieve_name);
    
    break;

  case DELETESCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after DELETESCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    deletescript(sieved_out, &sieve_name);
    
    break;

  case LISTSCRIPTS:

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;
    
    listscripts(sieved_out);
    
    break;

  case STARTTLS:

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    /* XXX  discard any input pipelined after STARTTLS */
    prot_flush(sieved_in);

    if(referral_host)
	goto do_referral;

    cmd_starttls(sieved_out, sieved_in);
    
    break;

  case NOOP:

    token = timlex(NULL, NULL, sieved_in);
    if (token != EOL)
    {
      /* optional string parameter */
      if (token!=SPACE)
      {
	error_msg = "Expected SPACE";
	goto error;
      }

      if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
      {
	error_msg = "Expected string";
	goto error;
      }

      token = timlex(NULL, NULL, sieved_in);      
    }

    if (token != EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if (sieve_name.len) {
      prot_printf(sieved_out, "OK (TAG ");
      prot_printliteral(sieved_out, sieve_name.s, sieve_name.len);
      prot_printf(sieved_out, ") \"Done\"\r\n");
    } else
      prot_printf(sieved_out, "OK \"Done\"\r\n");
    break;

  case UNAUTHENTICATE:
      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }
      cmd_unauthenticate(sieved_out, sieved_in);
      break;

  default:
    error_msg="Expected a command. Got something else.";
    goto error;
    break;

  }

 done: 
  /* free memory */
  buf_free(&mechanism_name);
  buf_free(&initial_challenge);
  buf_free(&sieve_name);
  buf_free(&sieve_data);
 
  prot_flush(sieved_out);

  return ret;

 error:

  /* free memory */
  buf_free(&mechanism_name);
  buf_free(&initial_challenge);
  buf_free(&sieve_name);
  buf_free(&sieve_data);

  prot_printf(sieved_out, "NO \"%s\"\r\n",error_msg);
  prot_flush(sieved_out);

  return FALSE;

 do_referral:
  {
      char buf[4096];
      char *c;

      /* Truncate the hostname if necessary */
      strlcpy(buf, referral_host, sizeof(buf));
      c = strchr(buf, '!');
      if(c) *c = '\0';
      
      prot_printf(sieved_out, "BYE (REFERRAL \"sieve://%s\") \"Try Remote.\"\r\n",
		  buf);
      ret = TRUE;
      goto done;
  }

}
예제 #5
0
/*
  perform a sasl bind using the given credentials
*/
_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
			struct cli_credentials *creds,
			struct loadparm_context *lp_ctx)
{
	NTSTATUS status;
	TALLOC_CTX *tmp_ctx = NULL;

	DATA_BLOB input = data_blob(NULL, 0);
	DATA_BLOB output = data_blob(NULL, 0);

	struct ldap_message **sasl_mechs_msgs;
	struct ldap_SearchResEntry *search;
	int count, i;

	const char **sasl_names;
	uint32_t old_gensec_features;
	static const char *supported_sasl_mech_attrs[] = {
		"supportedSASLMechanisms", 
		NULL 
	};
	unsigned int logon_retries = 0;

	status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs,
			      false, NULL, NULL, &sasl_mechs_msgs);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
			  nt_errstr(status)));
		goto failed;
	}

	count = ildap_count_entries(conn, sasl_mechs_msgs);
	if (count != 1) {
		DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
			  count));
		goto failed;
	}

	tmp_ctx = talloc_new(conn);
	if (tmp_ctx == NULL) goto failed;

	search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
	if (search->num_attributes != 1) {
		DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
			  search->num_attributes));
		goto failed;
	}

	sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
	if (!sasl_names) {
		DEBUG(1, ("talloc_arry(char *, %d) failed\n",
			  count));
		goto failed;
	}

	for (i=0; i<search->attributes[0].num_values; i++) {
		sasl_names[i] = (const char *)search->attributes[0].values[i].data;
	}
	sasl_names[i] = NULL;

	gensec_init();

try_logon_again:
	/*
	  we loop back here on a logon failure, and re-create the
	  gensec session. The logon_retries counter ensures we don't
	  loop forever.
	 */

	status = gensec_client_start(conn, &conn->gensec,
				     lpcfg_gensec_settings(conn, lp_ctx));
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
		goto failed;
	}

	/* require Kerberos SIGN/SEAL only if we don't use SSL
	 * Windows seem not to like double encryption */
	old_gensec_features = cli_credentials_get_gensec_features(creds);
	if (tls_enabled(conn->sock)) {
		cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL));
	}

	/* this call also sets the gensec_want_features */
	status = gensec_set_credentials(conn->gensec, creds);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("Failed to set GENSEC creds: %s\n", 
			  nt_errstr(status)));
		goto failed;
	}

	/* reset the original gensec_features (on the credentials
	 * context, so we don't tatoo it ) */
	cli_credentials_set_gensec_features(creds, old_gensec_features);

	if (conn->host) {
		status = gensec_set_target_hostname(conn->gensec, conn->host);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
				  nt_errstr(status)));
			goto failed;
		}
	}

	status = gensec_set_target_service(conn->gensec, "ldap");
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("Failed to set GENSEC target service: %s\n", 
			  nt_errstr(status)));
		goto failed;
	}

	status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
			  count, nt_errstr(status)));
		goto failed;
	}

	while (1) {
		NTSTATUS gensec_status;
		struct ldap_message *response;
		struct ldap_message *msg;
		struct ldap_request *req;
		int result = LDAP_OTHER;
	
		status = gensec_update(conn->gensec, tmp_ctx,
				       conn->event.event_ctx,
				       input,
				       &output);
		/* The status value here, from GENSEC is vital to the security
		 * of the system.  Even if the other end accepts, if GENSEC
		 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
		 * feeding it blobs, or else the remote host/attacker might
		 * avoid mutal authentication requirements.
		 *
		 * Likewise, you must not feed GENSEC too much (after the OK),
		 * it doesn't like that either
		 */

		gensec_status = status;

		if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
		    !NT_STATUS_IS_OK(status)) {
			break;
		}
		if (NT_STATUS_IS_OK(status) && output.length == 0) {
			break;
		}

		/* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
		msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
		if (msg == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto failed;
		}

		req = ldap_request_send(conn, msg);
		if (req == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto failed;
		}
		talloc_reparent(conn, tmp_ctx, req);

		status = ldap_result_n(req, 0, &response);
		if (!NT_STATUS_IS_OK(status)) {
			goto failed;
		}
		
		if (response->type != LDAP_TAG_BindResponse) {
			status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
			goto failed;
		}

		result = response->r.BindResponse.response.resultcode;

		if (result == LDAP_INVALID_CREDENTIALS) {
			/*
			  try a second time on invalid credentials, to
			  give the user a chance to re-enter the
			  password and to handle the case where our
			  kerberos ticket is invalid as the server
			  password has changed
			*/
			const char *principal;

			principal = gensec_get_target_principal(conn->gensec);
			if (principal == NULL) {
				const char *hostname = gensec_get_target_hostname(conn->gensec);
				const char *service  = gensec_get_target_service(conn->gensec);
				if (hostname != NULL && service != NULL) {
					principal = talloc_asprintf(tmp_ctx, "%s/%s", service, hostname);
				}
			}

			if (cli_credentials_failed_kerberos_login(creds, principal, &logon_retries) ||
			    cli_credentials_wrong_password(creds)) {
				/*
				  destroy our gensec session and loop
				  back up to the top to retry,
				  offering the user a chance to enter
				  new credentials, or get a new ticket
				  if using kerberos
				 */
				talloc_free(conn->gensec);
				conn->gensec = NULL;
				goto try_logon_again;
			}
		}

		if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
			status = ldap_check_response(conn, 
						     &response->r.BindResponse.response);
			break;
		}

		/* This is where we check if GENSEC wanted to be fed more data */
		if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
			break;
		}
		if (response->r.BindResponse.SASL.secblob) {
			input = *response->r.BindResponse.SASL.secblob;
		} else {
			input = data_blob(NULL, 0);
		}
	}

	talloc_free(tmp_ctx);

	if (NT_STATUS_IS_OK(status)) {
		struct socket_context *sasl_socket;
		status = gensec_socket_init(conn->gensec, 
					    conn,
					    conn->sock,
					    conn->event.event_ctx, 
					    ldap_read_io_handler,
					    conn,
					    &sasl_socket);
		if (!NT_STATUS_IS_OK(status)) goto failed;

		conn->sock = sasl_socket;
		packet_set_socket(conn->packet, conn->sock);

		conn->bind.type = LDAP_BIND_SASL;
		conn->bind.creds = creds;
	}

	return status;

failed:
	talloc_free(tmp_ctx);
	talloc_free(conn->gensec);
	conn->gensec = NULL;
	return status;
}