diff options
-rw-r--r-- | ACI.txt | 2 | ||||
-rw-r--r-- | API.txt | 25 | ||||
-rw-r--r-- | VERSION | 4 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 77 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c | 4 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/libotp/otp_config.c | 89 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/libotp/otp_config.h | 17 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/libotp/otp_token.c | 137 | ||||
-rw-r--r-- | daemons/ipa-slapi-plugins/libotp/otp_token.h | 26 | ||||
-rw-r--r-- | install/share/70ipaotp.ldif | 5 | ||||
-rw-r--r-- | install/updates/40-otp.update | 9 | ||||
-rw-r--r-- | ipalib/plugins/otpconfig.py | 119 |
12 files changed, 361 insertions, 153 deletions
@@ -154,6 +154,8 @@ dn: cn=ng,cn=alt,dc=ipa,dc=example aci: (targetattr = "cn || createtimestamp || description || entryusn || hostcategory || ipaenabledflag || ipauniqueid || modifytimestamp || nisdomainname || objectclass || usercategory")(targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Read Netgroups";allow (compare,read,search) userdn = "ldap:///all";) dn: cn=ng,cn=alt,dc=ipa,dc=example aci: (targetfilter = "(objectclass=ipanisnetgroup)")(version 3.0;acl "permission:System: Remove Netgroups";allow (delete) groupdn = "ldap:///cn=System: Remove Netgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";) +dn: cn=otp,cn=etc,dc=ipa,dc=example +aci: (targetattr = "cn || ipatokenhotpauthwindow || ipatokenhotpsyncwindow || ipatokentotpauthwindow || ipatokentotpsyncwindow")(targetfilter = "(objectclass=ipatokenotpconfig)")(version 3.0;acl "permission:System: Read OTP Configuration";allow (compare,read,search) userdn = "ldap:///all";) dn: cn=permissions,cn=pbac,dc=ipa,dc=example aci: (targetattr = "member")(targetfilter = "(objectclass=ipapermission)")(version 3.0;acl "permission:System: Modify Privilege Membership";allow (write) groupdn = "ldap:///cn=System: Modify Privilege Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";) dn: dc=ipa,dc=example @@ -2599,6 +2599,31 @@ option: Str('version?', exclude='webui') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: PrimaryKey('value', None, None) +command: otpconfig_mod +args: 0,11,3 +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Int('ipatokenhotpauthwindow', attribute=True, autofill=False, cli_name='hotp_auth_window', minvalue=1, multivalue=False, required=False) +option: Int('ipatokenhotpsyncwindow', attribute=True, autofill=False, cli_name='hotp_sync_window', minvalue=1, multivalue=False, required=False) +option: Int('ipatokentotpauthwindow', attribute=True, autofill=False, cli_name='totp_auth_window', minvalue=5, multivalue=False, required=False) +option: Int('ipatokentotpsyncwindow', attribute=True, autofill=False, cli_name='totp_sync_window', minvalue=5, multivalue=False, required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: Str('version?', exclude='webui') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: PrimaryKey('value', None, None) +command: otpconfig_show +args: 0,4,3 +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: PrimaryKey('value', None, None) command: otptoken_add args: 1,23,3 arg: Str('ipatokenuniqueid', attribute=True, cli_name='id', multivalue=False, primary_key=True, required=False) @@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=110 -# Last change: pvoborni - allow to retrieve keytab by hosts +IPA_API_VERSION_MINOR=111 +# Last change: npmccallum - configurable token windows diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c index 96c55f39b..84eff1701 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c @@ -68,8 +68,6 @@ #define IPAPWD_OP_ADD 1 #define IPAPWD_OP_MOD 2 -#define OTP_VALIDATE_STEPS 3 - extern Slapi_PluginDesc ipapwd_plugin_desc; extern void *ipapwd_plugin_id; extern const char *ipa_realm_tree; @@ -1113,8 +1111,8 @@ done: } /* - * Authenticates creds against OTP tokens. Returns true when authentication - * completed successfully against a token OR when a user has no active tokens. + * This function handles the bind functionality for OTP. The return value + * indicates if the OTP portion of authentication was successful. * * WARNING: This function DOES NOT authenticate the first factor. Only the OTP * code is validated! You still need to validate the first factor. @@ -1123,53 +1121,6 @@ done: * value at the end. This leaves only the password in creds for later * validation. */ -static bool ipapwd_do_otp_auth(const char *dn, Slapi_Entry *bind_entry, - struct berval *creds) -{ - struct otp_token **tokens = NULL; - bool success = false; - - /* Find all of the user's active tokens. */ - tokens = otp_token_find(otp_config, dn, NULL, true, NULL); - if (tokens == NULL) { - slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, - "%s: can't find tokens for '%s'.\n", __func__, dn); - return false; - } - - /* If the user has no active tokens, succeed. */ - success = tokens[0] == NULL; - - /* Loop through each token. */ - for (int i = 0; tokens[i] && !success; i++) { - /* Attempt authentication. */ - success = otp_token_validate_berval(tokens[i], OTP_VALIDATE_STEPS, - creds, true); - - /* Truncate the password to remove the OTP code at the end. */ - if (success) { - creds->bv_len -= otp_token_get_digits(tokens[i]); - creds->bv_val[creds->bv_len] = '\0'; - } - - slapi_log_error(SLAPI_LOG_PLUGIN, IPAPWD_PLUGIN_NAME, - "%s: token authentication %s " - "(user: '%s', token: '%s\').\n", __func__, - success ? "succeeded" : "failed", dn, - slapi_sdn_get_ndn(otp_token_get_sdn(tokens[i]))); - } - - otp_token_free_array(tokens); - return success; -} - -/* - * This function handles the bind functionality for OTP. The return value - * indicates if the OTP portion of authentication was successful. - * - * NOTE: This function may modify creds. See explanation in the comment for - * ipapwd_do_otp_auth() above. - */ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, struct berval *creds) { @@ -1189,10 +1140,32 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, */ if (auth_types & OTP_CONFIG_AUTH_TYPE_OTP) { + struct otp_token **tokens = NULL; + LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME, "Attempting OTP authentication for '%s'.\n", bind_dn); - if (ipapwd_do_otp_auth(bind_dn, entry, creds)) + + /* Find all of the user's active tokens. */ + tokens = otp_token_find(otp_config, bind_dn, NULL, true, NULL); + if (tokens == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, IPAPWD_PLUGIN_NAME, + "%s: can't find tokens for '%s'.\n", + __func__, bind_dn); + return false; + } + + /* If the user has no active tokens, succeed. */ + if (tokens[0] == NULL) { + otp_token_free_array(tokens); + return true; + } + + if (otp_token_validate_berval(tokens, creds, NULL)) { + otp_token_free_array(tokens); return true; + } + + otp_token_free_array(tokens); } return auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD; diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c index 0aef43802..3a31529f7 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/syncreq.c @@ -40,8 +40,6 @@ #include "../libotp/otp_token.h" #include "syncreq.h" -#define OTP_SYNC_MAX_STEPS 25 - bool sync_request_present(Slapi_PBlock *pb) { LDAPControl **controls = NULL; @@ -92,7 +90,7 @@ bool sync_request_handle(const struct otp_config *cfg, Slapi_PBlock *pb, if (ber_scanf(ber, "}") != LBER_ERROR) { tokens = otp_token_find(cfg, user_dn, token_dn, true, NULL); if (tokens != NULL) { - success = otp_token_sync_berval(tokens, OTP_SYNC_MAX_STEPS, first, second); + success = otp_token_validate_berval(tokens, first, second); otp_token_free_array(tokens); } } diff --git a/daemons/ipa-slapi-plugins/libotp/otp_config.c b/daemons/ipa-slapi-plugins/libotp/otp_config.c index 1b7c1e658..ac2cfc72a 100644 --- a/daemons/ipa-slapi-plugins/libotp/otp_config.c +++ b/daemons/ipa-slapi-plugins/libotp/otp_config.c @@ -105,6 +105,17 @@ static uint32_t entry_to_authtypes(Slapi_Entry *e, const char *attr) return types; } +static uint32_t entry_to_window(Slapi_Entry *e, const char *attr) +{ + long long val; + + if (e == NULL) + return 0; + + val = slapi_entry_attr_get_longlong(e, attr); + return val > 0 ? val : 0; +} + static const struct spec authtypes = { entry_to_authtypes, "cn=ipaConfig,cn=etc,%s", @@ -112,6 +123,34 @@ static const struct spec authtypes = { OTP_CONFIG_AUTH_TYPE_PASSWORD }; +static const struct spec totp_auth_window = { + entry_to_window, + "cn=otp,cn=etc,%s", + "ipatokenTOTPauthWindow", + 300 +}; + +static const struct spec totp_sync_window = { + entry_to_window, + "cn=otp,cn=etc,%s", + "ipatokenTOTPsyncWindow", + 86400 +}; + +static const struct spec hotp_auth_window = { + entry_to_window, + "cn=otp,cn=etc,%s", + "ipatokenHOTPauthWindow", + 10 +}; + +static const struct spec hotp_sync_window = { + entry_to_window, + "cn=otp,cn=etc,%s", + "ipatokenHOTPsyncWindow", + 100 +}; + static Slapi_DN *make_sdn(const char *prefix, const Slapi_DN *suffix) { char *dn = slapi_ch_smprintf(prefix, slapi_sdn_get_dn(suffix)); @@ -126,10 +165,14 @@ static uint32_t find_value(const struct otp_config *cfg, sdn = make_sdn(spec->prefix, suffix); for (struct record *rec = cfg->records; rec != NULL; rec = rec->next) { - if (rec->spec == spec) { - value = PR_ATOMIC_ADD(&rec->value, 0); - break; - } + if (rec->spec != spec) + continue; + + if (slapi_sdn_compare(sdn, rec->sdn) != 0) + continue; + + value = PR_ATOMIC_ADD(&rec->value, 0); + break; } slapi_sdn_free(&sdn); @@ -162,6 +205,10 @@ struct otp_config *otp_config_init(Slapi_ComponentId *plugin_id) { static const struct spec *specs[] = { &authtypes, + &totp_auth_window, + &totp_sync_window, + &hotp_auth_window, + &hotp_sync_window, NULL }; @@ -272,3 +319,37 @@ uint32_t otp_config_auth_types(const struct otp_config *cfg, return OTP_CONFIG_AUTH_TYPE_PASSWORD; } + +struct otp_config_window +otp_config_window(const struct otp_config *cfg, Slapi_Entry *token_entry) +{ + const struct spec *auth = NULL, *sync = NULL; + struct otp_config_window wndw = { 0, 0 }; + const Slapi_DN *sfx; + char **clses; + + sfx = slapi_get_suffix_by_dn(slapi_entry_get_sdn_const(token_entry)); + + clses = slapi_entry_attr_get_charray(token_entry, SLAPI_ATTR_OBJECTCLASS); + for (size_t i = 0; clses != NULL && clses[i] != NULL; i++) { + if (strcasecmp(clses[i], "ipatokenTOTP") == 0) { + auth = &totp_auth_window; + sync = &totp_sync_window; + break; + } + + if (strcasecmp(clses[i], "ipatokenHOTP") == 0) { + auth = &hotp_auth_window; + sync = &hotp_sync_window; + break; + } + } + slapi_ch_array_free(clses); + + if (auth == NULL || sync == NULL) + return wndw; + + wndw.auth = find_value(cfg, sfx, auth); + wndw.sync = find_value(cfg, sfx, sync); + return wndw; +} diff --git a/daemons/ipa-slapi-plugins/libotp/otp_config.h b/daemons/ipa-slapi-plugins/libotp/otp_config.h index bfd514bd5..b7f14fafa 100644 --- a/daemons/ipa-slapi-plugins/libotp/otp_config.h +++ b/daemons/ipa-slapi-plugins/libotp/otp_config.h @@ -49,6 +49,11 @@ struct otp_config; +struct otp_config_window { + uint32_t auth; + uint32_t sync; +}; + struct otp_config *otp_config_init(Slapi_ComponentId *plugin_id); void otp_config_fini(struct otp_config **cfg); @@ -63,3 +68,15 @@ Slapi_ComponentId *otp_config_plugin_id(const struct otp_config *cfg); */ uint32_t otp_config_auth_types(const struct otp_config *cfg, Slapi_Entry *user_entry); + +/* Gets the window sizes for a token. + * + * The entry should be queried for the following attributes: + * objectClass + * ipatokenTOTPauthWindow + * ipatokenTOTPsyncWindow + * ipatokenHOTPauthWindow + * ipatokenHOTPsyncWindow + */ +struct otp_config_window otp_config_window(const struct otp_config *cfg, + Slapi_Entry *token_entry); diff --git a/daemons/ipa-slapi-plugins/libotp/otp_token.c b/daemons/ipa-slapi-plugins/libotp/otp_token.c index eef072685..bc6acc42c 100644 --- a/daemons/ipa-slapi-plugins/libotp/otp_token.c +++ b/daemons/ipa-slapi-plugins/libotp/otp_token.c @@ -38,6 +38,7 @@ * END COPYRIGHT BLOCK **/ #include "otp_token.h" +#include "otp_config.h" #include "hotp.h" #include <time.h> @@ -63,10 +64,11 @@ struct otp_token { Slapi_DN *sdn; struct hotp_token token; enum type type; + struct otp_config_window window; union { struct { uint64_t watermark; - unsigned int step; + int step; /* Seconds. */ int offset; } totp; struct { @@ -247,6 +249,7 @@ static struct otp_token *otp_token_new(const struct otp_config *cfg, if (token == NULL) return NULL; token->cfg = cfg; + token->window = otp_config_window(cfg, entry); /* Get the token type. */ vals = slapi_entry_attr_get_charray(entry, "objectClass"); @@ -300,7 +303,7 @@ static struct otp_token *otp_token_new(const struct otp_config *cfg, /* Get step. */ token->totp.step = slapi_entry_attr_get_uint(entry, T("timeStep")); - if (token->totp.step == 0) + if (token->totp.step < 5) token->totp.step = IPA_OTP_DEFAULT_TOKEN_STEP; break; case TYPE_HOTP: @@ -431,42 +434,11 @@ struct otp_token **otp_token_find(const struct otp_config *cfg, return find(cfg, user_dn, token_dn, actfilt, filter); } -int otp_token_get_digits(struct otp_token *token) -{ - return token == NULL ? 0 : token->token.digits; -} - const Slapi_DN *otp_token_get_sdn(struct otp_token *token) { return token->sdn; } -static bool otp_token_validate(struct otp_token *token, size_t steps, - uint32_t code) -{ - time_t now = 0; - - if (token == NULL) - return false; - - /* We only need the local time for time-based tokens. */ - if (token->type == TYPE_TOTP && time(&now) == (time_t) -1) - return false; - - for (int i = 0; i <= steps; i++) { - /* Validate the positive step. */ - if (validate(token, now, i, code, NULL)) - return true; - - /* Validate the negative step. */ - if (validate(token, now, 0 - i, code, NULL)) - return true; - } - - return false; -} - - /* * Convert code berval to decimal. * @@ -474,45 +446,40 @@ static bool otp_token_validate(struct otp_token *token, size_t steps, * 1. If we have leading zeros, atol() fails. * 2. Neither support limiting conversion by length. */ -static bool bvtod(const struct berval *code, uint32_t *out) +static bool bvtod(const struct berval *code, int digits, uint32_t *out) { *out = 0; - for (ber_len_t i = 0; i < code->bv_len; i++) { + if (code == NULL || digits <= 0 || code->bv_len < digits) + return false; + + for (ber_len_t i = code->bv_len - digits; i < code->bv_len; i++) { if (code->bv_val[i] < '0' || code->bv_val[i] > '9') return false; *out *= 10; *out += code->bv_val[i] - '0'; } - return code->bv_len != 0; + return true; } -bool otp_token_validate_berval(struct otp_token *token, size_t steps, - const struct berval *code, bool tail) +static bool step_is_valid(struct otp_token *token, bool sync, uint32_t i) { - struct berval tmp; - uint32_t otp; - - if (token == NULL || code == NULL) - return false; - tmp = *code; - - if (tmp.bv_len < token->token.digits) - return false; - - if (tail) - tmp.bv_val = &tmp.bv_val[tmp.bv_len - token->token.digits]; - tmp.bv_len = token->token.digits; + uint32_t window = sync ? token->window.sync : token->window.auth; - if (!bvtod(&tmp, &otp)) + switch (token->type) { + case TYPE_TOTP: + return i * token->totp.step < window; + case TYPE_HOTP: + return i < window; + default: return false; - - return otp_token_validate(token, steps, otp); + } } -static bool otp_token_sync(struct otp_token * const *tokens, size_t steps, - uint32_t first_code, uint32_t second_code) +bool otp_token_validate_berval(struct otp_token * const *tokens, + struct berval *first_code, + struct berval *second_code) { time_t now = 0; @@ -522,33 +489,45 @@ static bool otp_token_sync(struct otp_token * const *tokens, size_t steps, if (time(&now) == (time_t) -1) return false; - for (int i = 0; i <= steps; i++) { + for (uint32_t i = 0, cnt = 1; cnt != 0; i++) { + cnt = 0; for (int j = 0; tokens[j] != NULL; j++) { - /* Validate the positive step. */ - if (validate(tokens[j], now, i, first_code, &second_code)) - return true; + uint32_t *secondp = NULL; + uint32_t second; + uint32_t first; + + /* Don't validate beyond the specified window. */ + if (!step_is_valid(tokens[j], second_code != NULL, i)) + continue; + cnt++; + + /* Parse the first code. */ + if (!bvtod(first_code, tokens[j]->token.digits, &first)) + continue; + + /* Parse the second code. */ + if (second_code != NULL) { + secondp = &second; + if (!bvtod(second_code, tokens[j]->token.digits, secondp)) + continue; + } + + /* Validate the positive/negative steps. */ + if (!validate(tokens[j], now, i, first, secondp) && + !validate(tokens[j], now, 0 - i, first, secondp)) + continue; + + /* Codes validated; strip. */ + first_code->bv_len -= tokens[j]->token.digits; + first_code->bv_val[first_code->bv_len] = '\0'; + if (second_code != NULL) { + second_code->bv_len -= tokens[j]->token.digits; + second_code->bv_val[second_code->bv_len] = '\0'; + } - /* Validate the negative step. */ - if (validate(tokens[j], now, 0 - i, first_code, &second_code)) - return true; + return true; } } return false; } - -bool otp_token_sync_berval(struct otp_token * const *tokens, size_t steps, - const struct berval *first_code, - const struct berval *second_code) -{ - uint32_t second = 0; - uint32_t first = 0; - - if (!bvtod(first_code, &first)) - return false; - - if (!bvtod(second_code, &second)) - return false; - - return otp_token_sync(tokens, steps, first, second); -} diff --git a/daemons/ipa-slapi-plugins/libotp/otp_token.h b/daemons/ipa-slapi-plugins/libotp/otp_token.h index 4b159077d..4cef9a63b 100644 --- a/daemons/ipa-slapi-plugins/libotp/otp_token.h +++ b/daemons/ipa-slapi-plugins/libotp/otp_token.h @@ -70,19 +70,19 @@ struct otp_token **otp_token_find(const struct otp_config *cfg, const char *user_dn, const char *token_dn, bool active, const char *filter); -/* Get the length of the token code. */ -int otp_token_get_digits(struct otp_token *token); - /* Get the SDN of the token. */ const Slapi_DN *otp_token_get_sdn(struct otp_token *token); -/* Validate the token code within a range of steps. If tail is true, - * it will be assumed that the token is specified at the end of the string. */ -bool otp_token_validate_berval(struct otp_token *token, size_t steps, - const struct berval *code, bool tail); - -/* Synchronize the token within a range of steps. */ -bool otp_token_sync_berval(struct otp_token * const *tokens, size_t steps, - const struct berval *first_code, - const struct berval *second_code); - +/* Perform OTP authentication. + * + * If only the first code is specified, validation will be performed and the + * validated token will be stripped. + * + * If both codes are specified, synchronization will be performed and the + * validated tokens will be stripped. + * + * Returns true if and only if all specified tokens were validated. + */ +bool otp_token_validate_berval(struct otp_token * const *tokens, + struct berval *first_code, + struct berval *second_code); diff --git a/install/share/70ipaotp.ldif b/install/share/70ipaotp.ldif index bc9555668..b35ab6235 100644 --- a/install/share/70ipaotp.ldif +++ b/install/share/70ipaotp.ldif @@ -24,8 +24,13 @@ attributeTypes: (2.16.840.1.113730.3.8.16.1.19 NAME 'ipatokenRadiusRetries' DESC attributeTypes: (2.16.840.1.113730.3.8.16.1.20 NAME 'ipatokenUserMapAttribute' DESC 'Attribute to map from the user entry for RADIUS server authentication' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA OTP') attributeTypes: (2.16.840.1.113730.3.8.16.1.21 NAME 'ipatokenHOTPcounter' DESC 'HOTP counter' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') attributeTypes: (2.16.840.1.113730.3.8.16.1.22 NAME 'ipatokenTOTPwatermark' DESC 'TOTP watermark' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') +attributeTypes: (2.16.840.1.113730.3.8.16.1.23 NAME 'ipatokenTOTPauthWindow' DESC 'TOTP Auth Window (maximum authentication variance in seconds)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') +attributeTypes: (2.16.840.1.113730.3.8.16.1.24 NAME 'ipatokenTOTPsyncWindow' DESC 'TOTP Sync Window (maximum synchronization variance in seconds)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') +attributeTypes: (2.16.840.1.113730.3.8.16.1.25 NAME 'ipatokenHOTPauthWindow' DESC 'HOTP Auth Window (maximum authentication skip-ahead)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') +attributeTypes: (2.16.840.1.113730.3.8.16.1.26 NAME 'ipatokenHOTPsyncWindow' DESC 'HOTP Sync Window (maximum synchronization skip-ahead)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA OTP') objectClasses: (2.16.840.1.113730.3.8.16.2.1 NAME 'ipaToken' SUP top ABSTRACT DESC 'Abstract token class for tokens' MUST (ipatokenUniqueID) MAY (description $ managedBy $ ipatokenOwner $ ipatokenDisabled $ ipatokenNotBefore $ ipatokenNotAfter $ ipatokenVendor $ ipatokenModel $ ipatokenSerial) X-ORIGIN 'IPA OTP') objectClasses: (2.16.840.1.113730.3.8.16.2.2 NAME 'ipatokenTOTP' SUP ipaToken STRUCTURAL DESC 'TOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenTOTPclockOffset $ ipatokenTOTPtimeStep) MAY (ipatokenTOTPwatermark) X-ORIGIN 'IPA OTP') objectClasses: (2.16.840.1.113730.3.8.16.2.3 NAME 'ipatokenRadiusProxyUser' SUP top AUXILIARY DESC 'Radius Proxy User' MAY (ipatokenRadiusConfigLink $ ipatokenRadiusUserName) X-ORIGIN 'IPA OTP') objectClasses: (2.16.840.1.113730.3.8.16.2.4 NAME 'ipatokenRadiusConfiguration' SUP top STRUCTURAL DESC 'Proxy Radius Configuration' MUST (cn $ ipatokenRadiusServer $ ipatokenRadiusSecret) MAY (description $ ipatokenRadiusTimeout $ ipatokenRadiusRetries $ ipatokenUserMapAttribute) X-ORIGIN 'IPA OTP') objectClasses: (2.16.840.1.113730.3.8.16.2.5 NAME 'ipatokenHOTP' SUP ipaToken STRUCTURAL DESC 'HOTP Token Type' MUST (ipatokenOTPkey $ ipatokenOTPalgorithm $ ipatokenOTPdigits $ ipatokenHOTPcounter) X-ORIGIN 'IPA OTP') +objectClasses: (2.16.840.1.113730.3.8.16.2.6 NAME 'ipatokenOTPConfig' SUP top STRUCTURAL DESC 'OTP Global Configuration' MUST (cn) MAY (ipatokenTOTPauthWindow $ ipatokenTOTPsyncWindow $ ipatokenHOTPauthWindow $ ipatokenHOTPsyncWindow) X-ORIGIN 'IPA OTP') diff --git a/install/updates/40-otp.update b/install/updates/40-otp.update index 83808b718..7cdff44ba 100644 --- a/install/updates/40-otp.update +++ b/install/updates/40-otp.update @@ -3,6 +3,15 @@ default: objectClass: nsContainer default: objectClass: top default: cn: otp +dn: cn=otp,cn=etc,$SUFFIX +default: objectClass: ipatokenOTPConfig +default: objectClass: top +default: cn: otp +default: ipatokenTOTPauthWindow: 300 +default: ipatokenTOTPsyncWindow: 86400 +default: ipatokenHOTPauthWindow: 10 +default: ipatokenHOTPsyncWindow: 100 + dn: $SUFFIX remove: aci:'(target = "ldap:///ipatokenuniqueid=*,cn=otp,$SUFFIX")(targetfilter = "(objectClass=ipaToken)")(version 3.0; acl "Users can create and delete tokens"; allow (add, delete) userattr = "ipatokenOwner#SELFDN";)' remove: aci:'(targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || ipatokenUniqueID || description || ipatokenOwner || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial")(version 3.0; acl "Users can read basic token info"; allow (read, search, compare) userattr = "ipatokenOwner#USERDN";)' diff --git a/ipalib/plugins/otpconfig.py b/ipalib/plugins/otpconfig.py new file mode 100644 index 000000000..440440dc9 --- /dev/null +++ b/ipalib/plugins/otpconfig.py @@ -0,0 +1,119 @@ +# Authors: +# Nathaniel McCallum <npmccallum@redhat.com> +# +# Copyright (C) 2014 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from ipalib import _, api, Int +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import DN, LDAPObject, LDAPUpdate, LDAPRetrieve + +__doc__ = _(""" +OTP configuration + +Manage the default values that IPA uses for OTP tokens. + +EXAMPLES: + + Show basic OTP configuration: + ipa otpconfig-show + + Show all OTP configuration options: + ipa otpconfig-show --all + + Change maximum TOTP authentication window to 10 minutes: + ipa otpconfig-mod --totp-auth-window=600 + + Change maximum TOTP synchronization window to 12 hours: + ipa otpconfig-mod --totp-sync-window=43200 + + Change maximum HOTP authentication window to 5: + ipa hotpconfig-mod --hotp-auth-window=5 + + Change maximum HOTP synchronization window to 50: + ipa hotpconfig-mod --hotp-sync-window=50 +""") + +register = Registry() + + +@register() +class otpconfig(LDAPObject): + object_name = _('OTP configuration options') + default_attributes = [ + 'ipatokentotpauthwindow', + 'ipatokentotpsyncwindow', + 'ipatokenhotpauthwindow', + 'ipatokenhotpsyncwindow', + ] + + container_dn = DN(('cn', 'otp'), ('cn', 'etc')) + permission_filter_objectclasses = ['ipatokenotpconfig'] + managed_permissions = { + 'System: Read OTP Configuration': { + 'replaces_global_anonymous_aci': True, + 'ipapermbindruletype': 'all', + 'ipapermright': {'read', 'search', 'compare'}, + 'ipapermdefaultattr': { + 'ipatokentotpauthwindow', 'ipatokentotpsyncwindow', + 'ipatokenhotpauthwindow', 'ipatokenhotpsyncwindow', + 'cn', + }, + }, + } + + label = _('OTP Configuration') + label_singular = _('OTP Configuration') + + takes_params = ( + Int('ipatokentotpauthwindow', + cli_name='totp_auth_window', + label=_('TOTP authentication Window'), + doc=_('TOTP authentication time variance (seconds)'), + minvalue=5, + ), + Int('ipatokentotpsyncwindow', + cli_name='totp_sync_window', + label=_('Synchronization Window'), + doc=_('TOTP synchronization time variance (seconds)'), + minvalue=5, + ), + Int('ipatokenhotpauthwindow', + cli_name='hotp_auth_window', + label=_('HOTP Authentication Window'), + doc=_('HOTP authentication skip-ahead'), + minvalue=1, + ), + Int('ipatokenhotpsyncwindow', + cli_name='hotp_sync_window', + label=_('HOTP Synchronization Window'), + doc=_('HOTP synchronization skip-ahead'), + minvalue=1, + ), + ) + + def get_dn(self, *keys, **kwargs): + return self.container_dn + api.env.basedn + + +@register() +class otpconfig_mod(LDAPUpdate): + __doc__ = _('Modify OTP configuration options.') + + +@register() +class otpconfig_show(LDAPRetrieve): + __doc__ = _('Show the current OTP configuration.') |