mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-22 23:23:30 -06:00
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:
parent
bea417828d
commit
9baa93da1c
2
ACI.txt
2
ACI.txt
@ -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
25
API.txt
@ -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)
|
||||
|
4
VERSION
4
VERSION
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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')
|
||||
|
@ -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
119
ipalib/plugins/otpconfig.py
Normal 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.')
|
Loading…
Reference in New Issue
Block a user