diff options
author | Andrew Tridgell <tridge@samba.org> | 2002-09-17 12:12:50 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2002-09-17 12:12:50 +0000 |
commit | b33681fc0b8ef7b9fa91c154f7c3117afafa349e (patch) | |
tree | a83b6dc52ddcd4fdc873882b5a127a33044e55c5 | |
parent | 3fefef7a7238e63716a8003aa27a08627a61b927 (diff) | |
download | samba-b33681fc0b8ef7b9fa91c154f7c3117afafa349e.tar.gz samba-b33681fc0b8ef7b9fa91c154f7c3117afafa349e.tar.bz2 samba-b33681fc0b8ef7b9fa91c154f7c3117afafa349e.zip |
Add clock skew handling to our kerberos code. This allows us to cope with
the DC being out of sync with the local machine.
(This used to be commit 0d28d769472ea3b98ae4c8757093dfd4499f6dd1)
-rw-r--r-- | source3/auth/auth_domain.c | 2 | ||||
-rw-r--r-- | source3/include/ads.h | 9 | ||||
-rw-r--r-- | source3/libads/kerberos.c | 8 | ||||
-rw-r--r-- | source3/libads/krb5_setpw.c | 14 | ||||
-rw-r--r-- | source3/libads/ldap.c | 66 | ||||
-rw-r--r-- | source3/libads/sasl.c | 7 | ||||
-rw-r--r-- | source3/libads/util.c | 2 | ||||
-rw-r--r-- | source3/libsmb/cliconnect.c | 2 | ||||
-rw-r--r-- | source3/libsmb/clikrb5.c | 14 | ||||
-rw-r--r-- | source3/libsmb/clispnego.c | 4 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cm.c | 2 | ||||
-rw-r--r-- | source3/utils/net_ads.c | 26 |
12 files changed, 113 insertions, 43 deletions
diff --git a/source3/auth/auth_domain.c b/source3/auth/auth_domain.c index e8f11bb3d5..2e51a85281 100644 --- a/source3/auth/auth_domain.c +++ b/source3/auth/auth_domain.c @@ -48,7 +48,7 @@ static NTSTATUS ads_resolve_dc(fstring remote_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 diff --git a/source3/include/ads.h b/source3/include/ads.h index 6106eb6b40..875b895e49 100644 --- a/source3/include/ads.h +++ b/source3/include/ads.h @@ -24,7 +24,8 @@ typedef struct { char *password; char *user_name; char *kdc_server; - int no_bind; + unsigned flags; + int time_offset; } auth; /* info derived from the servers config */ @@ -32,6 +33,7 @@ typedef struct { char *realm; char *bind_path; char *ldap_server_name; + time_t current_time; } config; } ADS_STRUCT; @@ -249,3 +251,8 @@ typedef void **ADS_MODLIST; /* 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 diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index 9a486237c9..a80837cf4d 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -50,7 +50,7 @@ kerb_prompter(krb5_context ctx, void *data, 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; @@ -60,6 +60,10 @@ int kerberos_kinit_password(const char *principal, const char *password) 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); @@ -111,7 +115,7 @@ int ads_kinit_password(ADS_STRUCT *ads) 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", diff --git a/source3/libads/krb5_setpw.c b/source3/libads/krb5_setpw.c index ec79a8658f..a49b6cbe3b 100644 --- a/source3/libads/krb5_setpw.c +++ b/source3/libads/krb5_setpw.c @@ -248,7 +248,8 @@ static krb5_error_code parse_setpw_reply(krb5_context context, 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; @@ -268,6 +269,10 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char 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); @@ -452,16 +457,17 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char 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); } diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c index 2f70d3a285..385a9bd93f 100644 --- a/source3/libads/ldap.c +++ b/source3/libads/ldap.c @@ -63,6 +63,7 @@ static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port) ads->ldap_port = port; ads->ldap_ip = *interpret_addr2(srv); free(srv); + return True; } @@ -204,7 +205,6 @@ static BOOL ads_try_netbios(ADS_STRUCT *ads) ADS_STATUS ads_connect(ADS_STRUCT *ads) { int version = LDAP_VERSION3; - int code; ADS_STATUS status; ads->last_attempt = time(NULL); @@ -274,7 +274,7 @@ got_connection: } #endif - if (ads->auth.no_bind) { + if (ads->auth.flags & ADS_AUTH_NO_BIND) { return ADS_SUCCESS; } @@ -1416,7 +1416,7 @@ ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads, */ 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); @@ -1622,6 +1622,26 @@ ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn) 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 @@ -1632,22 +1652,36 @@ ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn) **/ 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); } @@ -1657,8 +1691,7 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads) 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); @@ -1675,6 +1708,15 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads) 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; } diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c index 12a5722319..c110c1d2cd 100644 --- a/source3/libads/sasl.c +++ b/source3/libads/sasl.c @@ -122,7 +122,7 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip 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); @@ -144,7 +144,7 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip */ 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; @@ -185,7 +185,8 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) } 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); } diff --git a/source3/libads/util.c b/source3/libads/util.c index b10b130a31..021f2d93e4 100644 --- a/source3/libads/util.c +++ b/source3/libads/util.c @@ -40,7 +40,7 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_princip 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")); diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 6c5c5e0b0e..298b1e52b6 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -431,7 +431,7 @@ static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, c 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; diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index 1fc400edb0..22bfdc0463 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -64,6 +64,14 @@ static krb5_error_code krb5_mk_req2(krb5_context context, 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); @@ -86,7 +94,7 @@ cleanup_princ: /* 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; @@ -108,6 +116,10 @@ DATA_BLOB krb5_get_ticket(char *principal) 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))); diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c index 04ec6ed39e..55f49c5987 100644 --- a/source3/libsmb/clispnego.c +++ b/source3/libsmb/clispnego.c @@ -321,13 +321,13 @@ BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket) 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); diff --git a/source3/nsswitch/winbindd_cm.c b/source3/nsswitch/winbindd_cm.c index 9ac392a6ba..0b9e38eb1f 100644 --- a/source3/nsswitch/winbindd_cm.c +++ b/source3/nsswitch/winbindd_cm.c @@ -109,7 +109,7 @@ static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring sr } /* 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)); diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c index 8c85bd82f9..af290ce83c 100644 --- a/source3/utils/net_ads.c +++ b/source3/utils/net_ads.c @@ -66,7 +66,7 @@ static int net_ads_lookup(int argc, const char **argv) ads = ads_init(NULL, NULL, opt_host); if (ads) { - ads->auth.no_bind = 1; + ads->auth.flags |= ADS_AUTH_NO_BIND; } ads_connect(ads); @@ -88,7 +88,7 @@ static int net_ads_info(int argc, const char **argv) ads = ads_init(NULL, NULL, opt_host); if (ads) { - ads->auth.no_bind = 1; + ads->auth.flags |= ADS_AUTH_NO_BIND; } ads_connect(ads); @@ -103,6 +103,7 @@ static int net_ads_info(int argc, const char **argv) 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; } @@ -199,7 +200,7 @@ static int net_ads_workgroup(int argc, const char **argv) -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; @@ -213,15 +214,16 @@ static void usergrp_display(char *field, void **values, void *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) @@ -270,7 +272,7 @@ static int ads_user_add(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]); @@ -653,7 +655,9 @@ int net_ads_join(int argc, const char **argv) 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; } @@ -663,12 +667,6 @@ int net_ads_join(int argc, const char **argv) 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; @@ -885,7 +883,7 @@ static int net_ads_password(int argc, const char **argv) 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); |