Сменить пароль, если для сброса пароля при следующем входе в систему установлен Active Directory в linux с использованием библиотеки Openldap C - PullRequest
0 голосов
/ 27 февраля 2020

Я пытаюсь сбросить пароль для пользователя активного каталога, для которого установлен «сброс пароля при следующем входе в систему», используя открытый код ldap c, который похож на код ldapmodify. Но ldap_bind завершается неудачно с неверными учетными данными, с данными 773 (код ошибки истек срок действия пароля).

ldapmodify -v -x -ZZ -H ldap://<ldap> -D "cn=fi_user,cn=users,dc=qa01,dc=eng,dc=user,dc=com" -w oldpassword -f ~/old_to_new.ldif 
ldap_initialize( ldap://<ldap>/??base )
ldap_bind: Invalid credentials (49)
        additional info: 80090308: LdapErr: DSID-0C09042F, comment: AcceptSecurityContext error, data 773, v2580

Возможно ли сбросить пароль для этого пользователя Active Directory (задан сброс пароля при следующем входе в систему) с использованием протокола ldap в C в linux?

    vector<string> Split(string& s, string delim) {
      vector<string> ret;
      auto start = 0U;
      auto end = s.find(delim);
      while (end != std::string::npos) {
        ret.push_back(s.substr(start, end - start));
        start = end + delim.length();
        end = s.find(delim, start);
      }
      ret.push_back(s.substr(start));
      return ret;
    }

    void SetLDAPModPassword(LDAPMod* ldap_mod, string& password) {
      std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
      std::u16string utf16_curr_pass = convert.from_bytes("\"" + password + "\"");

      struct berval** ber_arr = new struct berval*[2];
      ber_arr[1] = NULL;
      ber_arr[0] = new struct berval;
      ber_arr[0]->bv_val = new char[utf16_curr_pass.size() * 2];
      memcpy(ber_arr[0]->bv_val,
             utf16_curr_pass.data(),
             utf16_curr_pass.size() * 2);
      ber_arr[0]->bv_len = utf16_curr_pass.size() * 2;
      ldap_mod->mod_vals.modv_bvals = ber_arr;
    }


    string SetLdapOptions(LDAP* ldap_handle) {
      stringstream ss;
      int ret = ldap_set_option(ldap_handle, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_REFERRALS to LDAP_OPT_OFF failed: "
           << ldap_err2string(ret);
        return ss.str();
      }

      const int ldap_version = LDAP_VERSION3;
      ret = ldap_set_option(ldap_handle, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_PROTOCOL_VERSION to " << ldap_version
           << " failed: " << ldap_err2string(ret);
        return ss.str();
      }

      const int cert_flag = LDAP_OPT_X_TLS_NEVER;
      ret = ldap_set_option(nullptr, LDAP_OPT_X_TLS_REQUIRE_CERT, &cert_flag);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_X_TLS_REQUIRE_CERT to "
              "LDAP_OPT_X_TLS_NEVER failed: "
           << ldap_err2string(ret);
        return ss.str();
      }

      ret = ldap_start_tls_s(ldap_handle, NULL, NULL );
      if (ret != LDAP_SUCCESS) {
        ss << "ldap_start_tls_s failed: " << ldap_err2string(ret);
        return ss.str();
      }

      int timelimit = 15;
      struct timeval timeout = {.tv_sec = timelimit, .tv_usec = 0};

      ret = ldap_set_option(ldap_handle, LDAP_OPT_NETWORK_TIMEOUT, &timeout);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_NETWORK_TIMEOUT to "
              "LDAP_OPT_X_TLS_NEVER failed: "
           << ldap_err2string(ret);
        return ss.str();
      }

      ret = ldap_set_option(ldap_handle, LDAP_OPT_TIMELIMIT, &timelimit);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_TIMELIMIT to " << timelimit
           << " failed: " << ldap_err2string(ret);
        return ss.str();
      }

      ret = ldap_set_option(ldap_handle, LDAP_OPT_TIMEOUT, &timeout);
      if (ret != LDAP_OPT_SUCCESS) {
        ss << "ldap_set_option LDAP_OPT_TIMEOUT to "
           << timeout.tv_sec << " failed: " << ldap_err2string(ret);
      }

      return ss.str();
    }

    string ChangeActiveDirectoryPassword(string domain_controller,
                                         string domain,
                                         string username,
                                         string curr_password,
                                         string new_password) {
      int ret;
      LDAP* ldap_handle = nullptr;

      char passwd_attr[16] = "unicodePwd";
      std::stringstream ss;

      ss << "ldap://" << domain_controller << ":389";

      LOG(INFO) << ss.str();

      ret = ldap_initialize(&ldap_handle, ss.str().c_str());
      ss.str(std::string());
      if (ret != LDAP_SUCCESS) {
        ss << "ldap_initialize failed: " << ldap_err2string(ret);
        return ss.str();
      }


      string ret_err = SetLdapOptions(ldap_handle);
      if (!ret_err.empty()) {
        return ret_err;
      }

      stringstream domain_stream;
      domain_stream << "CN=" << FLAGS_username << ",CN=Users,";

      vector<string> split_string_vec = Split(domain, ".");
      int ii = 0;
      for (auto subdomain : split_string_vec) {
        if (ii != 0) {
          domain_stream << ",";
        }
        domain_stream << string("DC=") << subdomain;
        ii++;
      }

      string domain_string = domain_stream.str();

      struct berval passwd_berval;
      passwd_berval.bv_len = curr_password.size();
      passwd_berval.bv_val = new char[curr_password.size() + 1];
      strcpy(passwd_berval.bv_val, curr_password.c_str());

      LOG(INFO) << domain_string;
      struct berval* servercredp;
      ret = ldap_sasl_bind_s(ldap_handle,
                             domain_string.c_str(),
                             LDAP_SASL_SIMPLE,
                             &passwd_berval,
                             NULL,
                             NULL,
                             &servercredp);
      delete[] passwd_berval.bv_val;

      if (ret != LDAP_SUCCESS) {
        ss << "ldap_sasl_bind_s failed: " << ldap_err2string(ret);
        //return ss.str();
      }

LDAPMod delete_old_pass;
  memset(&delete_old_pass, 0, sizeof(LDAPMod));
  char unicode_str[16] = "unicodePwd";;
  delete_old_pass.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
  delete_old_pass.mod_type = unicode_str;

  std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
  std::u16string utf16_curr_pass =
      convert.from_bytes("\"" + FLAGS_curr_passwd + "\"");
  struct berval** del_ber_arr = new struct berval*[2];
  del_ber_arr[1] = NULL;
  del_ber_arr[0] = new struct berval;
  del_ber_arr[0]->bv_val = new char[utf16_curr_pass.size() * 2];
  memcpy(del_ber_arr[0]->bv_val,
         utf16_curr_pass.data(),
         utf16_curr_pass.size() * 2);
  del_ber_arr[0]->bv_len = utf16_curr_pass.size() * 2;
  delete_old_pass.mod_vals.modv_bvals = del_ber_arr;

  LDAPMod add_new_pass;
  LDAPMod *add_new_pass_pointer = &add_new_pass;
  memset(&add_new_pass, 0, sizeof(LDAPMod));
  add_new_pass.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
  add_new_pass.mod_type = unicode_str;

  std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert1;
  std::u16string utf16_new_pass =
      convert1.from_bytes("\"" + FLAGS_new_passwd + "\"");
  struct berval** add_ber_arr = new struct berval*[2];
  add_ber_arr[1] = NULL;
  add_ber_arr[0] = new struct berval;
  add_ber_arr[0]->bv_val = new char[utf16_new_pass.size() * 2];
  memcpy(add_ber_arr[0]->bv_val,
         utf16_new_pass.data(),
         utf16_new_pass.size() * 2);
  add_ber_arr[0]->bv_len = utf16_new_pass.size() * 2;
  add_new_pass.mod_vals.modv_bvals = add_ber_arr;

  LDAPMod *mods[3];
  mods[0] = &delete_old_pass;
  mods[1] = &add_new_pass;
  mods[2] = NULL;

  ret = ldap_modify_ext_s(
                ldap_handle,
                domain.c_str(),
                mods,
                NULL,
                NULL);

  CHECK_EQ(ret, LDAP_SUCCESS)
      << "ldap_modify_ext_s() failed." << ldap_err2string(ret) << " dn "
      << domain << " pass " << curr_passwd.bv_val;

  ldap_destroy(ldap_handle);
  LOG(INFO) << "LDAP password changed.";
}

1 Ответ

1 голос
/ 27 февраля 2020

Обратите внимание, что «изменение» пароля и «сброс» пароля считаются двумя разными вещами.

  • Изменение пароля происходит, когда пользователь вводит старый пароль и новый пароль
  • Сброс пароля происходит, когда пользователь вводит новый пароль, не зная старого пароля. Пользователь должен иметь разрешение «Сбросить пароль» для целевой учетной записи.

Процедура для обоих описана в документации для атрибута unicodePwd.

Я не знаю библиотеку OpenLDAP C, поэтому не могу дать вам код, но могу описать процесс.

Сброс пароля

Сначала вы должны выполнить привязку, используя учетную запись пользователя с «Сбросить пароль» на целевой учетной записи. Затем:

Если запрос Modify содержит одну операцию замены, содержащую значение Vrep для unicodePwd, сервер считает запрос административным сбросом пароля, то есть изменение пароля без знания старый пароль. Сервер декодирует Vrep, используя процедуру декодирования пароля, описанную далее в этом разделе, и использует его в качестве нового пароля.

Поэтому необходимо отправить операцию замены LDAP для атрибута unicodePwd с новым паролем. в необходимом формате. Требуемый формат:

D C требует, чтобы значение пароля было указано в строке Unicode в кодировке UTF-16, содержащей пароль, заключенный в кавычки, который был закодирован в виде BER как Строка октета в соответствии с синтаксисом Object (Replica-Link).

Смена пароля

Сначала необходимо выполнить привязку с использованием любой учетной записи которые могут аутентифицироваться в домене. Используемая вами учетная запись не требует специальных разрешений. Это просто необходимо иметь возможность аутентификации.

Тогда вы можете изменить пароль. Документация гласит:

Если запрос Modify содержит операцию удаления, содержащую значение Vdel для unicodePwd, за которой следует операция добавления, содержащая значение Vadd для unicodePwd, сервер считает запрос запросом на изменение. пароль. Сервер декодирует Vadd и Vdel, используя процедуру декодирования пароля, описанную далее в этом разделе. Vdel - старый пароль, а Vadd - новый пароль.

Для пояснения необходимо отправить запрос LDAP для изменения атрибута unicodePwd, который содержит две операции в одном запросе:

  1. Операция удаления, которая содержит значение старого пароля. Знание старого пароля - это то, что дает вам право сменить пароль.
  2. Операция добавления, которая содержит значение нового пароля.

Формат строк пароля тот же как при сбросе пароля.

...