the DC being out of sync with the local machine.
DEBUG(4,("ads_resolve_dc: realm=%s\n", ads->config.realm));
- ads->auth.no_bind = 1;
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
#ifdef HAVE_ADS
/* a full ads_connect() is actually overkill, as we don't srictly need
char *password;
char *user_name;
char *kdc_server;
- int no_bind;
+ unsigned flags;
+ int time_offset;
} auth;
/* info derived from the servers config */
char *realm;
char *bind_path;
char *ldap_server_name;
+ time_t current_time;
} config;
} ADS_STRUCT;
/* DomainCntrollerAddressType */
#define ADS_INET_ADDRESS 0x00000001
#define ADS_NETBIOS_ADDRESS 0x00000002
+
+
+/* ads auth control flags */
+#define ADS_AUTH_DISABLE_KERBEROS 1
+#define ADS_AUTH_NO_BIND 2
simulate a kinit, putting the tgt in the default cache location
remus@snapserver.com
*/
-int kerberos_kinit_password(const char *principal, const char *password)
+int kerberos_kinit_password(const char *principal, const char *password, int time_offset)
{
krb5_context ctx;
krb5_error_code code = 0;
if ((code = krb5_init_context(&ctx)))
return code;
+
+ if (time_offset != 0) {
+ krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
+ }
if ((code = krb5_cc_default(ctx, &cc))) {
krb5_free_context(ctx);
int ret;
asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm);
- ret = kerberos_kinit_password(s, ads->auth.password);
+ ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset);
if (ret) {
DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
return 0;
}
-ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw)
+ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw,
+ int time_offset)
{
krb5_context context;
krb5_auth_context auth_context = NULL;
return ADS_ERROR_KRB5(ret);
}
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
ret = krb5_cc_default(context, &ccache);
if (ret) {
krb5_free_context(context);
ADS_STATUS kerberos_set_password(const char *kpasswd_server,
const char *auth_principal, const char *auth_password,
- const char *target_principal, const char *new_password)
+ const char *target_principal, const char *new_password,
+ int time_offset)
{
int ret;
- if ((ret = kerberos_kinit_password(auth_principal, auth_password))) {
+ if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset))) {
DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
return ADS_ERROR_KRB5(ret);
}
- return krb5_set_password(kpasswd_server, target_principal, new_password);
+ return krb5_set_password(kpasswd_server, target_principal, new_password, time_offset);
}
ads->ldap_port = port;
ads->ldap_ip = *interpret_addr2(srv);
free(srv);
+
return True;
}
ADS_STATUS ads_connect(ADS_STRUCT *ads)
{
int version = LDAP_VERSION3;
- int code;
ADS_STATUS status;
ads->last_attempt = time(NULL);
}
#endif
- if (ads->auth.no_bind) {
+ if (ads->auth.flags & ADS_AUTH_NO_BIND) {
return ADS_SUCCESS;
}
*/
asprintf(&principal, "%s$@%s", host, ads->auth.realm);
- status = krb5_set_password(ads->auth.kdc_server, principal, password);
+ status = krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset);
free(host);
free(principal);
return ADS_SUCCESS;
}
+/* parse a ADS timestring - typical string is
+ '20020917091222.0Z0' which means 09:12.22 17th September
+ 2002, timezone 0 */
+static time_t ads_parse_time(const char *str)
+{
+ struct tm tm;
+
+ ZERO_STRUCT(tm);
+
+ if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ return 0;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+
+ return timegm(&tm);
+}
+
/**
* Find the servers name and realm - this can be done before authentication
**/
ADS_STATUS ads_server_info(ADS_STRUCT *ads)
{
- const char *attrs[] = {"ldapServiceName", NULL};
+ const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
ADS_STATUS status;
void *res;
- char **values;
+ char *value;
char *p;
+ char *timestr;
+ TALLOC_CTX *ctx;
+
+ if (!(ctx = talloc_init())) {
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
if (!ADS_ERR_OK(status)) return status;
- values = ldap_get_values(ads->ld, res, "ldapServiceName");
- if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ value = ads_pull_string(ads, ctx, res, "ldapServiceName");
+ if (!value) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
+
+ timestr = ads_pull_string(ads, ctx, res, "currentTime");
+ if (!timestr) {
+ return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+ }
- p = strchr(values[0], ':');
+ ldap_msgfree(res);
+
+ p = strchr(value, ':');
if (!p) {
- ldap_value_free(values);
- ldap_msgfree(res);
+ talloc_destroy(ctx);
DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
return ADS_ERROR(LDAP_DECODING_ERROR);
}
ads->config.ldap_server_name = strdup(p+1);
p = strchr(ads->config.ldap_server_name, '$');
if (!p || p[1] != '@') {
- ldap_value_free(values);
- ldap_msgfree(res);
+ talloc_destroy(ctx);
SAFE_FREE(ads->config.ldap_server_name);
DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
return ADS_ERROR(LDAP_DECODING_ERROR);
DEBUG(3,("got ldap server name %s@%s\n",
ads->config.ldap_server_name, ads->config.realm));
+ ads->config.current_time = ads_parse_time(timestr);
+
+ if (ads->config.current_time != 0) {
+ ads->auth.time_offset = ads->config.current_time - time(NULL);
+ DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
+ }
+
+ talloc_destroy(ctx);
+
return ADS_SUCCESS;
}
struct berval cred, *scred;
int rc;
- blob = spnego_gen_negTokenTarg(principal);
+ blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
if (!blob.data) {
return ADS_ERROR(LDAP_OPERATIONS_ERROR);
*/
static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
{
- struct berval *scred;
+ struct berval *scred=NULL;
int rc, i;
ADS_STATUS status;
DATA_BLOB blob;
}
DEBUG(3,("got principal=%s\n", principal));
- if (got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
+ if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
+ got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
return ads_sasl_spnego_krb5_bind(ads, principal);
}
asprintf(&service_principal, "HOST/%s", host_principal);
ret = kerberos_set_password(ads->auth.kdc_server, host_principal, password,
- service_principal, new_password);
+ service_principal, new_password, ads->auth.time_offset);
if (!secrets_store_machine_password(new_password)) {
DEBUG(1,("Failed to save machine password\n"));
DEBUG(2,("Doing kerberos session setup\n"));
/* generate the encapsulated kerberos5 ticket */
- negTokenTarg = spnego_gen_negTokenTarg(principal);
+ negTokenTarg = spnego_gen_negTokenTarg(principal, 0);
if (!negTokenTarg.data) return False;
goto cleanup_creds;
}
+ /* cope with the ticket being in the future due to clock skew */
+ if ((unsigned)credsp->times.starttime > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset = (unsigned)credsp->times.starttime - t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(context, t + time_offset + 1, 0);
+ }
+
in_data.length = 0;
retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
&in_data, credsp, outbuf);
/*
get a kerberos5 ticket for the given service
*/
-DATA_BLOB krb5_get_ticket(char *principal)
+DATA_BLOB krb5_get_ticket(char *principal, time_t time_offset)
{
krb5_error_code retval;
krb5_data packet;
goto failed;
}
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
if ((retval = krb5_cc_default(context, &ccdef))) {
DEBUG(1,("krb5_cc_default failed (%s)\n",
error_message(retval)));
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
kerberos session setup
*/
-DATA_BLOB spnego_gen_negTokenTarg(const char *principal)
+DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset)
{
DATA_BLOB tkt, tkt_wrapped, targ;
const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
/* get a kerberos ticket for the service */
- tkt = krb5_get_ticket(principal);
+ tkt = krb5_get_ticket(principal, time_offset);
/* wrap that up in a nice GSS-API wrapping */
tkt_wrapped = spnego_gen_krb5_wrap(tkt);
}
/* we don't need to bind, just connect */
- ads->auth.no_bind = 1;
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
ads = ads_init(NULL, NULL, opt_host);
if (ads) {
- ads->auth.no_bind = 1;
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
}
ads_connect(ads);
ads = ads_init(NULL, NULL, opt_host);
if (ads) {
- ads->auth.no_bind = 1;
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
}
ads_connect(ads);
d_printf("Realm: %s\n", ads->config.realm);
d_printf("Bind Path: %s\n", ads->config.bind_path);
d_printf("LDAP port: %d\n", ads->ldap_port);
+ d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
return 0;
}
-static void usergrp_display(char *field, void **values, void *data_area)
+static BOOL usergrp_display(char *field, void **values, void *data_area)
{
char **disp_fields = (char **) data_area;
}
SAFE_FREE(disp_fields[0]);
SAFE_FREE(disp_fields[1]);
- return;
+ return True;
}
if (!values) /* must be new field, indicate string field */
- return;
+ return True;
if (StrCaseCmp(field, "sAMAccountName") == 0) {
disp_fields[0] = strdup((char *) values[0]);
}
if (StrCaseCmp(field, "description") == 0)
disp_fields[1] = strdup((char *) values[0]);
+ return True;
}
static int net_ads_user_usage(int argc, const char **argv)
/* try setting the password */
asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
- status = krb5_set_password(ads->auth.kdc_server, upn, argv[1]);
+ status = krb5_set_password(ads->auth.kdc_server, upn, argv[1], ads->auth.time_offset);
safe_free(upn);
if (ADS_ERR_OK(status)) {
d_printf("User %s added\n", argv[0]);
return -1;
}
- if (ads_kinit_password(ads)) {
+ rc = ads_domain_sid(ads, &dom_sid);
+ if (!ADS_ERR_OK(rc)) {
+ d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
return -1;
}
return -1;
}
- rc = ads_domain_sid(ads, &dom_sid);
- if (!ADS_ERR_OK(rc)) {
- d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
- return -1;
- }
-
if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
DEBUG(1,("Failed to save domain sid\n"));
return -1;
new_password = getpass(prompt);
ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
- auth_password, argv[0], new_password);
+ auth_password, argv[0], new_password, ads->auth.time_offset);
if (!ADS_ERR_OK(ret)) {
d_printf("Password change failed :-( ...\n");
ads_destroy(&ads);