Make token auth and sync windows configurable

This introduces two new CLI commands:
  * otpconfig-show
  * otpconfig-mod

https://fedorahosted.org/freeipa/ticket/4511

Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
This commit is contained in:
Nathaniel McCallum 2014-11-11 14:41:42 -05:00 committed by Petr Vobornik
parent bea417828d
commit 9baa93da1c
12 changed files with 361 additions and 153 deletions

View File

@ -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

25
API.txt
View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
uint32_t window = sync ? token->window.sync : token->window.auth;
if (token == NULL || code == NULL)
switch (token->type) {
case TYPE_TOTP:
return i * token->totp.step < window;
case TYPE_HOTP:
return i < window;
default:
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;
if (!bvtod(&tmp, &otp))
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;
/* Validate the negative step. */
if (validate(tokens[j], now, 0 - i, first_code, &second_code))
return true;
/* 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';
}
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);
}

View File

@ -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);

View File

@ -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')

View File

@ -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";)'

119
ipalib/plugins/otpconfig.py Normal file
View File

@ -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.')