Mercurial > dovecot > core-2.2
annotate src/auth/db-oauth2.c @ 22614:cf66220d281e
doveadm proxy: Don't crash if remote doesn't support log proxying
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Sat, 14 Oct 2017 12:54:18 +0300 |
parents | 036d9f649c8b |
children | cb108f786fb4 |
rev | line source |
---|---|
21579 | 1 /* Copyright (c) 2017 Dovecot authors, see the included COPYING file */ |
2 | |
3 #include "lib.h" | |
4 #include "array.h" | |
5 #include "str.h" | |
6 #include "var-expand.h" | |
7 #include "env-util.h" | |
8 #include "var-expand.h" | |
9 #include "settings.h" | |
10 #include "oauth2.h" | |
11 #include "http-client.h" | |
12 #include "iostream-ssl.h" | |
13 #include "auth-request.h" | |
14 #include "passdb.h" | |
15 #include "passdb-template.h" | |
16 #include "llist.h" | |
17 #include "db-oauth2.h" | |
18 | |
19 #include <stddef.h> | |
20 | |
21 struct passdb_oauth2_settings { | |
22 /* tokeninfo endpoint, format https://endpoint/somewhere?token= */ | |
23 const char *tokeninfo_url; | |
24 /* introspection endpoint, format https://endpoint/somewhere */ | |
25 const char *introspection_url; | |
26 /* expected scope, optional */ | |
27 const char *scope; | |
28 /* mode of introspection, one of get, get-auth, post | |
29 - get: append token to url | |
30 - get-auth: send token with header Authorization: Bearer token | |
31 - post: send token=<token> as POST request | |
32 */ | |
33 const char *introspection_mode; | |
34 /* normalization var-expand template for username, defaults to %Lu */ | |
35 const char *username_format; | |
36 /* name of username attribute to lookup, mandatory */ | |
37 const char *username_attribute; | |
38 /* name of account is active attribute, optional */ | |
39 const char *active_attribute; | |
40 /* expected active value for active attribute, optional */ | |
41 const char *active_value; | |
42 /* template to expand into passdb */ | |
43 const char *pass_attrs; | |
44 | |
45 /* TLS options */ | |
46 const char *tls_ca_cert_file; | |
47 const char *tls_ca_cert_dir; | |
48 const char *tls_cert_file; | |
49 const char *tls_key_file; | |
50 const char *tls_cipher_suite; | |
51 | |
52 /* HTTP rawlog directory */ | |
53 const char *rawlog_dir; | |
54 | |
55 /* HTTP client options */ | |
56 unsigned int timeout_msecs; | |
57 unsigned int max_idle_time_msecs; | |
58 unsigned int max_parallel_connections; | |
59 unsigned int max_pipelined_requests; | |
60 bool tls_allow_invalid_cert; | |
61 | |
62 bool debug; | |
63 /* Should introspection be done even if not necessary */ | |
64 bool force_introspection; | |
65 /* Should we send service and local/remote endpoints as X-Dovecot-Auth headers */ | |
66 bool send_auth_headers; | |
67 }; | |
68 | |
69 struct db_oauth2 { | |
70 struct db_oauth2 *prev,*next; | |
71 | |
72 pool_t pool; | |
73 | |
74 const char *config_path; | |
75 struct passdb_oauth2_settings set; | |
76 struct http_client *client; | |
77 struct passdb_template *tmpl; | |
78 struct oauth2_settings oauth2_set; | |
79 | |
80 struct db_oauth2_request *head; | |
81 | |
82 unsigned int refcount; | |
83 }; | |
84 | |
85 static struct db_oauth2 *db_oauth2_head = NULL; | |
86 | |
87 #undef DEF_STR | |
88 #undef DEF_BOOL | |
89 #undef DEF_INT | |
90 | |
91 #define DEF_STR(name) DEF_STRUCT_STR(name, passdb_oauth2_settings) | |
92 #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, passdb_oauth2_settings) | |
93 #define DEF_INT(name) DEF_STRUCT_INT(name, passdb_oauth2_settings) | |
94 | |
95 static struct setting_def setting_defs[] = { | |
96 DEF_STR(tokeninfo_url), | |
97 DEF_STR(introspection_url), | |
98 DEF_STR(scope), | |
99 DEF_BOOL(force_introspection), | |
100 DEF_STR(introspection_mode), | |
101 DEF_STR(username_format), | |
102 DEF_STR(username_attribute), | |
103 DEF_STR(pass_attrs), | |
104 DEF_STR(active_attribute), | |
105 DEF_STR(active_value), | |
106 DEF_INT(timeout_msecs), | |
107 DEF_INT(max_idle_time_msecs), | |
108 DEF_INT(max_parallel_connections), | |
109 DEF_INT(max_pipelined_requests), | |
110 DEF_BOOL(send_auth_headers), | |
111 | |
112 DEF_STR(tls_ca_cert_file), | |
113 DEF_STR(tls_ca_cert_dir), | |
114 DEF_STR(tls_cert_file), | |
115 DEF_STR(tls_key_file), | |
116 DEF_STR(tls_cipher_suite), | |
117 DEF_BOOL(tls_allow_invalid_cert), | |
118 | |
119 DEF_STR(rawlog_dir), | |
120 | |
121 DEF_BOOL(debug), | |
122 | |
123 { 0, NULL, 0 } | |
124 }; | |
125 | |
126 static struct passdb_oauth2_settings default_oauth2_settings = { | |
127 .tokeninfo_url = "", | |
128 .introspection_url = "", | |
129 .scope = "", | |
130 .force_introspection = FALSE, | |
131 .introspection_mode = "", | |
132 .username_format = "%Lu", | |
133 .username_attribute = "email", | |
134 .active_attribute = "", | |
135 .active_value = "", | |
136 .pass_attrs = "", | |
137 .rawlog_dir = "", | |
138 .timeout_msecs = 0, | |
139 .max_idle_time_msecs = 60000, | |
140 .max_parallel_connections = 1, | |
141 .max_pipelined_requests = 1, | |
142 .tls_ca_cert_file = NULL, | |
143 .tls_ca_cert_dir = NULL, | |
144 .tls_cert_file = NULL, | |
145 .tls_key_file = NULL, | |
146 .tls_cipher_suite = "HIGH:!SSLv2", | |
147 .tls_allow_invalid_cert = FALSE, | |
148 .send_auth_headers = FALSE, | |
149 .debug = FALSE, | |
150 }; | |
151 | |
152 static const char *parse_setting(const char *key, const char *value, | |
153 struct db_oauth2 *db) | |
154 { | |
155 return parse_setting_from_defs(db->pool, setting_defs, | |
156 &db->set, key, value); | |
157 } | |
158 | |
159 struct db_oauth2 *db_oauth2_init(const char *config_path) | |
160 { | |
161 struct db_oauth2 *db; | |
162 const char *error; | |
163 struct http_client_settings http_set; | |
164 | |
165 for(db = db_oauth2_head; db != NULL; db = db->next) { | |
166 if (strcmp(db->config_path, config_path) == 0) { | |
167 db->refcount++; | |
168 return db; | |
169 } | |
170 } | |
171 | |
172 pool_t pool = pool_alloconly_create("db_oauth2", 128); | |
173 db = p_new(pool, struct db_oauth2, 1); | |
174 db->pool = pool; | |
175 db->refcount = 1; | |
176 db->config_path = p_strdup(pool, config_path); | |
177 db->set = default_oauth2_settings; | |
178 | |
179 if (!settings_read_nosection(config_path, parse_setting, db, &error)) | |
180 i_fatal("oauth2 %s: %s", config_path, error); | |
181 | |
182 db->tmpl = passdb_template_build(pool, db->set.pass_attrs); | |
183 | |
184 i_zero(&http_set); | |
185 | |
186 http_set.ssl_ca_file = db->set.tls_ca_cert_file; | |
187 http_set.ssl_ca_dir = db->set.tls_ca_cert_dir; | |
188 if (db->set.tls_cert_file != NULL && *db->set.tls_cert_file != '\0') { | |
189 http_set.ssl_cert = db->set.tls_cert_file; | |
190 http_set.ssl_key = db->set.tls_key_file; | |
191 } | |
192 http_set.ssl_allow_invalid_cert = db->set.tls_allow_invalid_cert; | |
193 | |
194 http_set.dns_client_socket_path = "dns-client"; | |
195 http_set.user_agent = "dovecot-oauth2-passdb/" DOVECOT_VERSION; | |
196 | |
21648
66802cc05f4d
auth: Make sure tokeninfo or introspection URL is given
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21647
diff
changeset
|
197 if (*db->set.tokeninfo_url == '\0' && *db->set.introspection_url == '\0') |
66802cc05f4d
auth: Make sure tokeninfo or introspection URL is given
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21647
diff
changeset
|
198 i_fatal("oauth2: Tokeninfo or introspection URL must be given"); |
66802cc05f4d
auth: Make sure tokeninfo or introspection URL is given
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21647
diff
changeset
|
199 |
21579 | 200 if (*db->set.rawlog_dir != '\0') |
201 http_set.rawlog_dir = db->set.rawlog_dir; | |
202 | |
203 http_set.max_idle_time_msecs = db->set.max_idle_time_msecs; | |
204 http_set.max_parallel_connections = db->set.max_parallel_connections; | |
205 http_set.max_pipelined_requests = db->set.max_pipelined_requests; | |
206 http_set.no_auto_redirect = FALSE; | |
207 http_set.no_auto_retry = TRUE; | |
208 http_set.debug = db->set.debug; | |
209 | |
210 db->client = http_client_init(&http_set); | |
211 | |
212 i_zero(&db->oauth2_set); | |
213 db->oauth2_set.client = db->client; | |
214 db->oauth2_set.tokeninfo_url = db->set.tokeninfo_url, | |
215 db->oauth2_set.introspection_url = db->set.introspection_url; | |
216 db->oauth2_set.timeout_msecs = db->set.timeout_msecs; | |
217 db->oauth2_set.send_auth_headers = db->set.send_auth_headers; | |
218 | |
219 if (*db->set.introspection_mode == '\0' || | |
220 strcmp(db->set.introspection_mode, "auth") == 0) { | |
221 db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET_AUTH; | |
222 } else if (strcmp(db->set.introspection_mode, "get") == 0) { | |
223 db->oauth2_set.introspection_mode = INTROSPECTION_MODE_GET; | |
224 } else if (strcmp(db->set.introspection_mode, "post") == 0) { | |
225 db->oauth2_set.introspection_mode = INTROSPECTION_MODE_POST; | |
226 } else { | |
227 i_fatal("Invalid value '%s' for introspection mode, must be on auth, get or post", | |
228 db->set.introspection_mode); | |
229 } | |
230 | |
231 DLLIST_PREPEND(&db_oauth2_head, db); | |
232 | |
233 return db; | |
234 } | |
235 | |
236 void db_oauth2_ref(struct db_oauth2 *db) | |
237 { | |
238 i_assert(db->refcount > 0); | |
239 db->refcount++; | |
240 } | |
241 | |
242 void db_oauth2_unref(struct db_oauth2 **_db) | |
243 { | |
244 struct db_oauth2 *ptr, *db = *_db; | |
245 i_assert(db->refcount > 0); | |
246 | |
247 if (--db->refcount > 0) return; | |
248 | |
249 for(ptr = db_oauth2_head; ptr != NULL; ptr = db->next) { | |
250 if (ptr == db) { | |
251 DLLIST_REMOVE(&db_oauth2_head, ptr); | |
252 break; | |
253 } | |
254 } | |
255 | |
256 i_assert(ptr != NULL && ptr == db); | |
257 | |
258 /* make sure all requests are aborted */ | |
21728
fb3278b43cdd
auth: oauth2 - Fix aborting auth requests on deinit.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21717
diff
changeset
|
259 while (db->head != NULL) |
fb3278b43cdd
auth: oauth2 - Fix aborting auth requests on deinit.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21717
diff
changeset
|
260 oauth2_request_abort(&db->head->req); |
21579 | 261 |
262 http_client_deinit(&db->client); | |
263 | |
264 pool_unref(&db->pool); | |
265 } | |
266 | |
267 static bool | |
268 db_oauth2_have_all_fields(struct db_oauth2_request *req) | |
269 { | |
270 unsigned int n,i; | |
271 unsigned int size,idx; | |
272 const char *const *args = passdb_template_get_args(req->db->tmpl, &n); | |
273 | |
274 if (req->fields == NULL) | |
275 return FALSE; | |
276 | |
277 for(i=1;i<n;i+=2) { | |
278 const char *ptr = args[i]; | |
279 while(ptr != NULL) { | |
280 ptr = strchr(ptr, '%'); | |
281 if (ptr != NULL) { | |
282 const char *field; | |
283 ptr++; | |
284 var_get_key_range(ptr, &idx, &size); | |
285 ptr = ptr+idx; | |
286 field = t_strndup(ptr,size); | |
287 if (strncmp(field, "oauth2:", 8) == 0 && | |
288 !auth_fields_exists(req->fields, ptr+8)) | |
289 return FALSE; | |
290 ptr = ptr+size; | |
291 } | |
292 } | |
293 } | |
294 | |
295 if (!auth_fields_exists(req->fields, req->db->set.username_attribute)) | |
296 return FALSE; | |
297 if (*req->db->set.active_attribute != '\0' && !auth_fields_exists(req->fields, req->db->set.active_attribute)) | |
298 return FALSE; | |
299 | |
300 return TRUE; | |
301 } | |
302 | |
303 static const char *field_get_default(const char *data) | |
304 { | |
305 const char *p; | |
306 | |
307 p = strchr(data, ':'); | |
308 if (p == NULL) | |
309 return ""; | |
310 else { | |
311 /* default value given */ | |
312 return p+1; | |
313 } | |
314 } | |
315 | |
316 static const char * | |
317 db_oauth2_var_expand_func_oauth2(const char *data, void *context) | |
318 { | |
319 struct db_oauth2_request *ctx = context; | |
320 const char *field_name = t_strcut(data, ':'); | |
321 const char *value = NULL; | |
322 | |
323 if (ctx->fields != NULL) | |
324 value = auth_fields_find(ctx->fields, field_name); | |
325 return value != NULL ? value : field_get_default(data); | |
326 } | |
327 | |
328 static const char *escape_none(const char *value, const struct auth_request *req ATTR_UNUSED) | |
329 { | |
330 return value; | |
331 } | |
332 | |
333 static const struct var_expand_table * | |
334 db_oauth2_value_get_var_expand_table(struct auth_request *auth_request, | |
335 const char *oauth2_value) | |
336 { | |
337 struct var_expand_table *table; | |
338 unsigned int count = 1; | |
339 | |
340 table = auth_request_get_var_expand_table_full(auth_request, NULL, | |
341 &count); | |
342 table[0].key = '$'; | |
343 table[0].value = oauth2_value; | |
344 return table; | |
345 } | |
346 | |
21732
78b6f3032cc6
auth: oauth2 - Template expand failure should be an internal error
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21731
diff
changeset
|
347 static bool |
21731
58f7612b0658
auth: oauth2 - make db_oauth2_template_export() static
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21730
diff
changeset
|
348 db_oauth2_template_export(struct db_oauth2_request *req, |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
349 enum passdb_result *result_r ATTR_UNUSED, |
21731
58f7612b0658
auth: oauth2 - make db_oauth2_template_export() static
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21730
diff
changeset
|
350 const char **error_r ATTR_UNUSED) |
21579 | 351 { |
352 /* var=$ expands into var=${oauth2:var} */ | |
353 const struct var_expand_func_table funcs_table[] = { | |
354 { "oauth2", db_oauth2_var_expand_func_oauth2 }, | |
355 { NULL, NULL } | |
356 }; | |
357 string_t *dest; | |
358 const char *const *args, *value; | |
359 struct passdb_template *tmpl = req->db->tmpl; | |
360 unsigned int i, count; | |
361 | |
362 if (passdb_template_is_empty(tmpl)) | |
21732
78b6f3032cc6
auth: oauth2 - Template expand failure should be an internal error
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21731
diff
changeset
|
363 return TRUE; |
21579 | 364 |
365 dest = t_str_new(256); | |
366 args = passdb_template_get_args(tmpl, &count); | |
367 i_assert((count % 2) == 0); | |
368 for (i = 0; i < count; i += 2) { | |
369 if (args[i+1] == NULL) | |
370 value = ""; | |
371 else { | |
372 str_truncate(dest, 0); | |
373 const struct var_expand_table * | |
374 table = db_oauth2_value_get_var_expand_table(req->auth_request, | |
375 auth_fields_find(req->fields, args[i])); | |
376 var_expand_with_funcs(dest, args[i+1], table, funcs_table, | |
377 req); | |
378 value = str_c(dest); | |
379 } | |
380 | |
381 auth_request_set_field(req->auth_request, args[i], value, | |
382 STATIC_PASS_SCHEME); | |
383 } | |
21732
78b6f3032cc6
auth: oauth2 - Template expand failure should be an internal error
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21731
diff
changeset
|
384 return TRUE; |
21579 | 385 } |
386 | |
387 static void db_oauth2_fields_merge(struct db_oauth2_request *req, | |
388 ARRAY_TYPE(oauth2_field) *fields) | |
389 { | |
390 const struct oauth2_field *field; | |
391 | |
392 if (req->fields == NULL) | |
393 req->fields = auth_fields_init(req->pool); | |
394 | |
395 array_foreach(fields, field) { | |
396 auth_fields_add(req->fields, field->name, field->value, 0); | |
397 } | |
398 } | |
399 | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
400 static void db_oauth2_callback(struct db_oauth2_request *req, |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
401 enum passdb_result result, |
21579 | 402 const char *error) |
403 { | |
404 db_oauth2_lookup_callback_t *callback = req->callback; | |
405 req->callback = NULL; | |
406 | |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
407 i_assert(result == PASSDB_RESULT_OK || error != NULL); |
21579 | 408 |
21717
1085fea7435f
db-oauth2: Make sure request is removed only once
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21696
diff
changeset
|
409 if (callback != NULL) { |
1085fea7435f
db-oauth2: Make sure request is removed only once
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21696
diff
changeset
|
410 DLLIST_REMOVE(&req->db->head, req); |
21737
036d9f649c8b
auth: oauth2 - cleanup db_oauth2_lookup_callback_t
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21736
diff
changeset
|
411 callback(req, result, error, req->context); |
21717
1085fea7435f
db-oauth2: Make sure request is removed only once
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21696
diff
changeset
|
412 } |
21579 | 413 } |
414 | |
415 static bool | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
416 db_oauth2_validate_username(struct db_oauth2_request *req, |
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
417 enum passdb_result *result_r, const char **error_r) |
21579 | 418 { |
419 struct var_expand_table table[] = { | |
420 { 'u', NULL, "user" }, | |
421 { 'n', NULL, "username" }, | |
422 { 'd', NULL, "domain" }, | |
423 { '\0', NULL, NULL } | |
424 }; | |
425 const char *username_value = | |
426 auth_fields_find(req->fields, req->db->set.username_attribute); | |
427 | |
428 if (username_value == NULL) { | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
429 *result_r = PASSDB_RESULT_INTERNAL_FAILURE; |
21579 | 430 *error_r = "No username returned"; |
431 return FALSE; | |
432 } | |
433 | |
434 table[0].value = username_value; | |
435 table[1].value = t_strcut(username_value, '@'); | |
436 table[2].value = strchr(username_value, '@'); | |
437 if (table[2].value != NULL) | |
438 table[2].value++; | |
439 | |
440 string_t *username_req = t_str_new(32); | |
441 string_t *username_val = t_str_new(strlen(username_value)); | |
442 | |
443 auth_request_var_expand(username_req, req->db->set.username_format, req->auth_request, escape_none); | |
444 var_expand(username_val, req->db->set.username_format, table); | |
445 | |
446 if (!str_equals(username_req, username_val)) { | |
447 *error_r = t_strdup_printf("Username '%s' did not match '%s'", | |
448 str_c(username_req), str_c(username_val)); | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
449 *result_r = PASSDB_RESULT_USER_UNKNOWN; |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
450 return FALSE; |
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
451 } else { |
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
452 return TRUE; |
21579 | 453 } |
454 } | |
455 | |
456 static bool | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
457 db_oauth2_user_is_enabled(struct db_oauth2_request *req, |
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
458 enum passdb_result *result_r, const char **error_r) |
21579 | 459 { |
460 if (*req->db->set.active_attribute != '\0') { | |
461 const char *active_value = auth_fields_find(req->fields, req->db->set.active_attribute); | |
462 if (active_value == NULL || | |
463 (*req->db->set.active_value != '\0' && | |
464 strcmp(req->db->set.active_value, active_value) != 0)) { | |
465 *error_r = "User account is not active"; | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
466 *result_r = PASSDB_RESULT_USER_DISABLED; |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
467 return FALSE; |
21579 | 468 } |
469 } | |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
470 return TRUE; |
21579 | 471 } |
472 | |
473 static bool | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
474 db_oauth2_token_in_scope(struct db_oauth2_request *req, |
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
475 enum passdb_result *result_r, const char **error_r) |
21579 | 476 { |
477 if (*req->db->set.scope != '\0') { | |
478 bool found = FALSE; | |
479 const char *value = auth_fields_find(req->fields, "scope"); | |
480 if (value != NULL) { | |
481 const char **scopes = t_strsplit_spaces(value, " "); | |
482 found = str_array_find(scopes, req->db->set.scope); | |
483 } | |
484 if (!found) { | |
485 *error_r = t_strdup_printf("Token is not valid for scope '%s'", | |
486 req->db->set.scope); | |
21733
01ffe59436af
auth: oauth2 - remove db_oauth2_request.result
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21732
diff
changeset
|
487 *result_r = PASSDB_RESULT_USER_DISABLED; |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
488 return FALSE; |
21579 | 489 } |
490 } | |
21734
8b5f6e2ff4a6
auth: oauth2 - remove db_oauth2_request.failed
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21733
diff
changeset
|
491 return TRUE; |
21579 | 492 } |
493 | |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
494 static void db_oauth2_process_fields(struct db_oauth2_request *req, |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
495 enum passdb_result *result_r, |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
496 const char **error_r) |
21579 | 497 { |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
498 *error_r = NULL; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
499 |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
500 if (db_oauth2_validate_username(req, result_r, error_r) && |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
501 db_oauth2_user_is_enabled(req, result_r, error_r) && |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
502 db_oauth2_token_in_scope(req, result_r, error_r) && |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
503 db_oauth2_template_export(req, result_r, error_r)) { |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
504 *result_r = PASSDB_RESULT_OK; |
21579 | 505 } else { |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
506 i_assert(*result_r != PASSDB_RESULT_OK && *error_r != NULL); |
21579 | 507 } |
508 } | |
509 | |
510 static void | |
511 db_oauth2_introspect_continue(struct oauth2_introspection_result *result, | |
512 struct db_oauth2_request *req) | |
513 { | |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
514 enum passdb_result passdb_result; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
515 const char *error; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
516 |
21729
dfcfdbbe1fd1
auth: oauth2 - Make sure db_oauth2_request.req is set to NULL when it gets freed.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21728
diff
changeset
|
517 req->req = NULL; |
dfcfdbbe1fd1
auth: oauth2 - Make sure db_oauth2_request.req is set to NULL when it gets freed.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21728
diff
changeset
|
518 |
21579 | 519 if (!result->success) { |
520 /* fail here */ | |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
521 passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
522 error = result->error; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
523 } else { |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
524 db_oauth2_fields_merge(req, result->fields); |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
525 db_oauth2_process_fields(req, &passdb_result, &error); |
21579 | 526 } |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
527 db_oauth2_callback(req, passdb_result, error); |
21579 | 528 } |
529 | |
530 static void db_oauth2_lookup_introspect(struct db_oauth2_request *req) | |
531 { | |
532 struct oauth2_request_input input; | |
533 i_zero(&input); | |
534 | |
535 input.token = req->token; | |
536 input.local_ip = req->auth_request->local_ip; | |
537 input.local_port = req->auth_request->local_port; | |
538 input.remote_ip = req->auth_request->remote_ip; | |
539 input.remote_port = req->auth_request->remote_port; | |
540 input.real_local_ip = req->auth_request->real_local_ip; | |
541 input.real_local_port = req->auth_request->real_local_port; | |
542 input.real_remote_ip = req->auth_request->real_remote_ip; | |
543 input.real_remote_port = req->auth_request->real_remote_port; | |
544 input.service = req->auth_request->service; | |
545 | |
546 req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, | |
547 db_oauth2_introspect_continue, req); | |
548 } | |
549 | |
550 static void | |
551 db_oauth2_lookup_continue(struct oauth2_token_validation_result *result, | |
552 struct db_oauth2_request *req) | |
553 { | |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
554 enum passdb_result passdb_result; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
555 const char *error; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
556 |
21729
dfcfdbbe1fd1
auth: oauth2 - Make sure db_oauth2_request.req is set to NULL when it gets freed.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21728
diff
changeset
|
557 req->req = NULL; |
dfcfdbbe1fd1
auth: oauth2 - Make sure db_oauth2_request.req is set to NULL when it gets freed.
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21728
diff
changeset
|
558 |
21735
4119b7774573
auth: oauth2 - Clarify token validation success/valid error handling
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21734
diff
changeset
|
559 if (!result->success) { |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
560 passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
561 error = result->error; |
21735
4119b7774573
auth: oauth2 - Clarify token validation success/valid error handling
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21734
diff
changeset
|
562 } else if (!result->valid) { |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
563 passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
564 error = "Invalid token"; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
565 } else { |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
566 db_oauth2_fields_merge(req, result->fields); |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
567 if (*req->db->set.introspection_url != '\0' && |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
568 (req->db->set.force_introspection || |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
569 !db_oauth2_have_all_fields(req))) { |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
570 db_oauth2_lookup_introspect(req); |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
571 return; |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
572 } |
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
573 db_oauth2_process_fields(req, &passdb_result, &error); |
21579 | 574 } |
21736
7d3ad0620805
auth: oauth2 - make it easier to see where db_oauth2_callback() is called
Timo Sirainen <timo.sirainen@dovecot.fi>
parents:
21735
diff
changeset
|
575 db_oauth2_callback(req, passdb_result, error); |
21579 | 576 } |
577 | |
578 #undef db_oauth2_lookup | |
579 void db_oauth2_lookup(struct db_oauth2 *db, struct db_oauth2_request *req, | |
580 const char *token, struct auth_request *request, | |
581 db_oauth2_lookup_callback_t *callback, void *context) | |
582 { | |
583 struct oauth2_request_input input; | |
584 i_zero(&input); | |
585 | |
586 req->db = db; | |
587 req->token = p_strdup(req->pool, token); | |
588 req->callback = callback; | |
589 req->context = context; | |
590 req->auth_request = request; | |
591 | |
592 input.token = token; | |
593 input.local_ip = req->auth_request->local_ip; | |
594 input.local_port = req->auth_request->local_port; | |
595 input.remote_ip = req->auth_request->remote_ip; | |
596 input.remote_port = req->auth_request->remote_port; | |
597 input.real_local_ip = req->auth_request->real_local_ip; | |
598 input.real_local_port = req->auth_request->real_local_port; | |
599 input.real_remote_ip = req->auth_request->real_remote_ip; | |
600 input.real_remote_port = req->auth_request->real_remote_port; | |
601 input.service = req->auth_request->service; | |
602 | |
21647
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
603 if (*db->oauth2_set.tokeninfo_url == '\0') { |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
604 req->req = oauth2_introspection_start(&req->db->oauth2_set, &input, |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
605 db_oauth2_introspect_continue, req); |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
606 } else { |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
607 req->req = oauth2_token_validation_start(&db->oauth2_set, &input, |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
608 db_oauth2_lookup_continue, req); |
cbecd2e16979
auth: Make tokeninfo optional
Aki Tuomi <aki.tuomi@dovecot.fi>
parents:
21579
diff
changeset
|
609 } |
21579 | 610 DLLIST_PREPEND(&db->head, req); |
611 } |