forked from freeipa/freeipa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ipa_kdb_kdcpolicy.c
187 lines (160 loc) · 6.25 KB
/
ipa_kdb_kdcpolicy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
* Copyright (C) 2018,2020 FreeIPA Contributors see COPYING for license
*/
#include <errno.h>
#include <syslog.h>
#include <sys/random.h>
#include <krb5/kdcpolicy_plugin.h>
#include "ipa_krb5.h"
#include "ipa_kdb.h"
#define ONE_DAY_SECONDS (24 * 60 * 60)
#define JITTER_WINDOW_SECONDS (1 * 60 * 60)
static void
jitter(krb5_deltat baseline, krb5_deltat *lifetime_out)
{
krb5_deltat offset;
ssize_t ret;
if (baseline < JITTER_WINDOW_SECONDS) {
/* A negative value here would correspond to a never-valid ticket,
* which isn't the goal. */
*lifetime_out = baseline;
return;
}
do {
ret = getrandom(&offset, sizeof(offset), 0);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
krb5_klog_syslog(LOG_INFO, "IPA kdcpolicy: getrandom failed (errno %d); skipping jitter...",
errno);
return;
}
*lifetime_out = baseline - offset % JITTER_WINDOW_SECONDS;
}
static krb5_error_code
ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
const krb5_kdc_req *request,
const krb5_db_entry *client,
const krb5_db_entry *server,
const char *const *auth_indicators,
const char **status, krb5_deltat *lifetime_out,
krb5_deltat *renew_lifetime_out)
{
krb5_error_code kerr;
enum ipadb_user_auth ua;
struct ipadb_e_data *ied;
struct ipadb_e_pol_limits *pol_limits = NULL;
int valid_auth_indicators = 0, flags = 0;
krb5_db_entry *client_actual = NULL;
#ifdef KRB5_KDB_FLAG_ALIAS_OK
flags = KRB5_KDB_FLAG_ALIAS_OK;
#endif
*status = NULL;
*lifetime_out = 0;
*renew_lifetime_out = 0;
ied = (struct ipadb_e_data *)client->e_data;
if (ied == NULL || ied->magic != IPA_E_DATA_MAGIC) {
/* e-data is not availble, getting user auth from LDAP */
krb5_klog_syslog(LOG_INFO, "IPA kdcpolicy: client e_data not availble. Try fetching...");
kerr = ipadb_get_principal(context, request->client, flags,
&client_actual);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "IPA kdcpolicy: ipadb_find_principal failed.");
return kerr;
}
ied = (struct ipadb_e_data *)client_actual->e_data;
if (ied == NULL && ied->magic != IPA_E_DATA_MAGIC) {
krb5_klog_syslog(LOG_ERR, "IPA kdcpolicy: client e_data fetching failed.");
return EINVAL;
}
}
ua = ied->user_auth;
/* If no mechanisms are set, allow every auth method */
if (ua == IPADB_USER_AUTH_NONE) {
jitter(ONE_DAY_SECONDS, lifetime_out);
return 0;
}
/* For each auth indicator, see if it is allowed for that user */
for (int i = 0; auth_indicators[i] != NULL; i++) {
const char *auth_indicator = auth_indicators[i];
if (strcmp(auth_indicator, "otp") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_OTP)) {
*status = "OTP pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_OTP]);
} else if (strcmp(auth_indicator, "radius") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_RADIUS)) {
*status = "OTP pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_RADIUS]);
} else if (strcmp(auth_indicator, "pkinit") == 0) {
valid_auth_indicators++;
if (!(ua & IPADB_USER_AUTH_PKINIT)) {
*status = "PKINIT pre-authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_PKINIT]);
} else if (strcmp(auth_indicator, "hardened") == 0) {
valid_auth_indicators++;
/* Allow hardened even if only password pre-auth is allowed */
if (!(ua & (IPADB_USER_AUTH_HARDENED | IPADB_USER_AUTH_PASSWORD))) {
*status = "Password pre-authentication not not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_HARDENED]);
}
}
/* There is no auth indicator assigned for non-hardened password authentication
* so we assume password is used when no supported indicator exists */
if (!valid_auth_indicators) {
if (!(ua & IPADB_USER_AUTH_PASSWORD)) {
*status = "Non-hardened password authentication not allowed for this user.";
return KRB5KDC_ERR_POLICY;
}
}
/* If there were policy limits associated with the authentication indicators,
* apply them */
if (pol_limits != NULL) {
if (pol_limits->max_life != 0) {
jitter(pol_limits->max_life, lifetime_out);
} else {
jitter(ONE_DAY_SECONDS, lifetime_out);
}
if (pol_limits->max_renewable_life != 0) {
*renew_lifetime_out = pol_limits->max_renewable_life;
}
}
return 0;
}
static krb5_error_code
ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
const krb5_kdc_req *request,
const krb5_db_entry *server,
const krb5_ticket *ticket,
const char *const *auth_indicators,
const char **status, krb5_deltat *lifetime_out,
krb5_deltat *renew_lifetime_out)
{
*status = NULL;
*lifetime_out = 0;
*renew_lifetime_out = 0;
return 0;
}
krb5_error_code kdcpolicy_ipakdb_initvt(krb5_context context,
int maj_ver, int min_ver,
krb5_plugin_vtable vtable)
{
krb5_kdcpolicy_vtable vt;
if (maj_ver != 1)
return KRB5_PLUGIN_VER_NOTSUPP;
vt = (krb5_kdcpolicy_vtable)vtable;
vt->name = "ipakdb";
vt->init = NULL;
vt->fini = NULL;
vt->check_as = ipa_kdcpolicy_check_as;
vt->check_tgs = ipa_kdcpolicy_check_tgs;
return 0;
}