1 /*
2 * Command handling for the JSON user information service interface.
3 *
4 * The user information service calls support using either XML (the older
5 * protocol) or JSON (the preferred protocol). This file contains all of the
6 * command creation and parsing logic for making JSON calls.
7 *
8 * Written by Russ Allbery <eagle@eyrie.org>
9 * Copyright 2011, 2012, 2013, 2014
10 * The Board of Trustees of the Leland Stanford Junior University
11 *
12 * See LICENSE for licensing terms.
13 */
14
15 #include <config.h>
16 #include <portable/apr.h>
17 #include <portable/system.h>
18
19 #include <limits.h>
20 #ifdef HAVE_JANSSON
21 # include <jansson.h>
22 #endif
23 #include <time.h>
24
25 #include <util/macros.h>
26
27 #include <lib/internal.h>
28 #include <webauth/basic.h>
29 #include <webauth/factors.h>
30 #include <webauth/webkdc.h>
31
32 /*
33 * Parsing macros that include error checking. Each of these macros assume
34 * that the s variable is available for a status and that the correct thing to
35 * do on any failure is to return the status while taking no further action.
36 */
37 #define PARSE_DEVICES(ctx, json, key, result) \
38 do { \
39 s = json_parse_devices((ctx), (json), (key), (result)); \
40 if (s != WA_ERR_NONE) \
41 return s; \
42 } while (0)
43 #define PARSE_FACTORS(ctx, json, key, result, exp) \
44 do { \
45 s = json_parse_factors((ctx), (json), (key), (result), (exp)); \
46 if (s != WA_ERR_NONE) \
47 return s; \
48 } while (0)
49 #define PARSE_HISTORY(ctx, json, key, result) \
50 do { \
51 s = json_parse_history((ctx), (json), (key), (result)); \
52 if (s != WA_ERR_NONE) \
53 return s; \
54 } while (0)
55 #define PARSE_INTEGER(ctx, json, key, result) \
56 do { \
57 unsigned long tmp; \
58 s = json_parse_integer((ctx), (json), (key), &tmp); \
59 if (s != WA_ERR_NONE) \
60 return s; \
61 *(result) = tmp; \
62 } while (0)
63 #define PARSE_STRING(ctx, json, key, result) \
64 do { \
65 s = json_parse_string((ctx), (json), (key), (result)); \
66 if (s != WA_ERR_NONE) \
67 return s; \
68 } while (0)
69
70 /*
71 * Macros to add data to a JSON data structure. Used to generate the JSON
72 * structure for making user information service calls. Each macro assumes
73 * that there is a fail label that should be called on any error.
74 */
75 #define ADD_BOOLEAN(json, key, value) \
76 do { \
77 json_t *tmp; \
78 tmp = json_boolean(value); \
79 if (json_object_set_new((json), (key), tmp) < 0) \
80 goto fail; \
81 } while (0);
82 #define ADD_INTEGER(json, key, value) \
83 do { \
84 json_t *tmp; \
85 tmp = json_integer(value); \
86 if (tmp == NULL) \
87 goto fail; \
88 if (json_object_set_new((json), (key), tmp) < 0) { \
89 json_decref(tmp); \
90 goto fail; \
91 } \
92 } while (0);
93 #define ADD_STRING(json, key, value) \
94 do { \
95 json_t *tmp; \
96 tmp = json_string(value); \
97 if (tmp == NULL) \
98 goto fail; \
99 if (json_object_set_new((json), (key), tmp) < 0) { \
100 json_decref(tmp); \
101 goto fail; \
102 } \
103 } while (0);
104
105
106 /*
107 * Stub out the calls to return an error if not built with JSON support.
108 */
109 #ifndef HAVE_JANSSON
110
111 int
wai_user_info_json(struct webauth_context * ctx,const char * user UNUSED,const char * ip UNUSED,int random_mf UNUSED,const char * url UNUSED,const char * factors UNUSED,struct webauth_user_info ** info UNUSED)112 wai_user_info_json(struct webauth_context *ctx, const char *user UNUSED,
113 const char *ip UNUSED, int random_mf UNUSED,
114 const char *url UNUSED, const char *factors UNUSED,
115 struct webauth_user_info **info UNUSED)
116 {
117 return wai_error_set(ctx, WA_ERR_UNIMPLEMENTED,
118 "not built with JSON support");
119 }
120
121 int
wai_user_validate_json(struct webauth_context * ctx,const char * user UNUSED,const char * ip UNUSED,const char * code UNUSED,const char * type UNUSED,const char * device UNUSED,const char * state UNUSED,struct webauth_user_validate ** validate UNUSED)122 wai_user_validate_json(struct webauth_context *ctx, const char *user UNUSED,
123 const char *ip UNUSED, const char *code UNUSED,
124 const char *type UNUSED, const char *device UNUSED,
125 const char *state UNUSED,
126 struct webauth_user_validate **validate UNUSED)
127 {
128 return wai_error_set(ctx, WA_ERR_UNIMPLEMENTED,
129 "not built with JSON support");
130 }
131
132 #else /* HAVE_JANSSON */
133
134 /*
135 * Given a WebAuth context, a JSON object, a key, and storage for an unsigned
136 * long, retrieve the value of that key as an integer and store it in the
137 * provided location. If the key is not present or is null, set result to 0
138 * and return success. If the key is present but the value is not an integer
139 * or is too long for an unsigned long, return an error code.
140 */
141 static int
json_parse_integer(struct webauth_context * ctx,json_t * json,const char * key,unsigned long * result)142 json_parse_integer(struct webauth_context *ctx, json_t *json, const char *key,
143 unsigned long *result)
144 {
145 json_t *value;
146 json_int_t integer;
147 int s = WA_ERR_REMOTE_FAILURE;
148
149 value = json_object_get(json, key);
150 if (value == NULL || json_is_null(value)) {
151 *result = 0;
152 return WA_ERR_NONE;
153 }
154 if (!json_is_integer(value))
155 return wai_error_set(ctx, s, "value of %s is not an integer", key);
156 integer = json_integer_value(value);
157 if (integer < 0 || integer > LONG_MAX)
158 return wai_error_set(ctx, s, "value of %s (%" JSON_INTEGER_FORMAT
159 ") is out of range", key, integer);
160 *result = integer;
161 return WA_ERR_NONE;
162 }
163
164
165 /*
166 * Given a WebAuth context, a JSON object, a key, and storage for an unsigned
167 * long, retrieve the value of that key as a string and store a pool copy of
168 * it in the provided location. If the key is not present or is null, set
169 * result to NULL and return success. If the key is present but the value is
170 * not an integer or is too long for an unsigned long, return an error code.
171 */
172 static int
json_parse_string(struct webauth_context * ctx,json_t * json,const char * key,const char ** result)173 json_parse_string(struct webauth_context *ctx, json_t *json, const char *key,
174 const char **result)
175 {
176 json_t *value;
177 int s = WA_ERR_REMOTE_FAILURE;
178
179 value = json_object_get(json, key);
180 if (value == NULL || json_is_null(value)) {
181 *result = NULL;
182 return WA_ERR_NONE;
183 }
184 if (!json_is_string(value))
185 return wai_error_set(ctx, s, "value of %s is not a string", key);
186 *result = apr_pstrdup(ctx->pool, json_string_value(value));
187 return WA_ERR_NONE;
188 }
189
190
191 /*
192 * Parse factors from a JSON document, given an object and a key in that
193 * object. The factors may be present in two different forms: a simple list
194 * of strings, or a list of objects with keys factor and expiration. If
195 * expiration information is present, and the expiration pointer is non-NULL,
196 * the lowest expiration is stored in that pointer. Returns a status code.
197 */
198 static int
json_parse_factors(struct webauth_context * ctx,json_t * json,const char * key,const struct webauth_factors ** result,time_t * expiration)199 json_parse_factors(struct webauth_context *ctx, json_t *json, const char *key,
200 const struct webauth_factors **result, time_t *expiration)
201 {
202 json_t *value, *array;
203 apr_array_header_t *factors = NULL;
204 const char **factor;
205 unsigned long expires, min_expires = 0;
206 size_t i;
207 int s;
208
209 /* Ensure the output variables are initialized in case of error. */
210 if (result != NULL)
211 *result = NULL;
212 if (expiration != NULL)
213 *expiration = 0;
214
215 /* Get the array of factors. */
216 array = json_object_get(json, key);
217 if (array == NULL || json_is_null(array))
218 return WA_ERR_NONE;
219 if (!json_is_array(array)) {
220 s = WA_ERR_REMOTE_FAILURE;
221 return wai_error_set(ctx, s, "value of %s is not an array", key);
222 }
223
224 /* Walk the array looking for strings or objects. */
225 for (i = 0; i < json_array_size(array); i++) {
226 value = json_array_get(array, i);
227 if (factors == NULL)
228 factors = apr_array_make(ctx->pool, 2, sizeof(const char *));
229 factor = apr_array_push(factors);
230 if (json_is_string(value))
231 *factor = apr_pstrdup(ctx->pool, json_string_value(value));
232 else if (json_is_object(value)) {
233 PARSE_STRING(ctx, value, "factor", factor);
234 PARSE_INTEGER(ctx, value, "expiration", &expires);
235 if (expires > 0 && (expires < min_expires || min_expires == 0))
236 min_expires = expires;
237 } else {
238 s = WA_ERR_REMOTE_FAILURE;
239 return wai_error_set(ctx, s, "%s element is not string or object",
240 key);
241 }
242 }
243
244 /* Save the factors if we found any and the caller wants them. */
245 if (factors != NULL && result != NULL)
246 *result = webauth_factors_new(ctx, factors);
247
248 /* Save the minimum expiration time if the caller wants it. */
249 if (expiration != NULL)
250 *expiration = min_expires;
251
252 /* FIXME: Warn if expiration != NULL but no expiration was found. */
253 return WA_ERR_NONE;
254 }
255
256
257 /*
258 * Parse the logins section of a userinfo JSON document. Stores the results
259 * in the provided array. Returns a status code.
260 */
261 static int
json_parse_history(struct webauth_context * ctx,json_t * json,const char * key,const apr_array_header_t ** result)262 json_parse_history(struct webauth_context *ctx, json_t *json, const char *key,
263 const apr_array_header_t **result)
264 {
265 json_t *value, *array;
266 apr_array_header_t *logins = NULL;
267 struct webauth_login *login;
268 int s;
269 size_t size, i;
270
271 /* Ensure the output variables are initialized in case of error. */
272 if (result != NULL)
273 *result = NULL;
274
275 /* Get the array of logins. */
276 array = json_object_get(json, key);
277 if (array == NULL || json_is_null(array))
278 return WA_ERR_NONE;
279 if (!json_is_array(array)) {
280 s = WA_ERR_REMOTE_FAILURE;
281 return wai_error_set(ctx, s, "value of %s is not an array", key);
282 }
283
284 /* Walk the array looking for objects. */
285 for (i = 0; i < json_array_size(array); i++) {
286 value = json_array_get(array, i);
287 if (!json_is_object(value)) {
288 s = WA_ERR_REMOTE_FAILURE;
289 return wai_error_set(ctx, s, "%s element is not object", key);
290 }
291
292 /* Create a new login entry, allocating the array if needed. */
293 if (logins == NULL) {
294 size = sizeof(struct webauth_login);
295 logins = apr_array_make(ctx->pool, 5, size);
296 }
297 login = apr_array_push(logins);
298 memset(login, 0, sizeof(*login));
299
300 /* Parse the login information. */
301 PARSE_STRING(ctx, value, "ip", &login->ip);
302 if (login->ip == NULL) {
303 s = WA_ERR_REMOTE_FAILURE;
304 return wai_error_set(ctx, s, "%s element has no ip key", key);
305 }
306 PARSE_INTEGER(ctx, value, "timestamp", &login->timestamp);
307 PARSE_STRING(ctx, value, "hostname", &login->hostname);
308 }
309
310 /* Return the results. */
311 *result = logins;
312 return WA_ERR_NONE;
313 }
314
315
316 /*
317 * Parse the devices section of a userinfo JSON document. Stores the results
318 * in the provided array. Returns a status code.
319 */
320 static int
json_parse_devices(struct webauth_context * ctx,json_t * json,const char * key,const apr_array_header_t ** result)321 json_parse_devices(struct webauth_context *ctx, json_t *json, const char *key,
322 const apr_array_header_t **result)
323 {
324 json_t *value, *array;
325 apr_array_header_t *devices = NULL;
326 struct webauth_device *device;
327 int s;
328 size_t size, i;
329
330 /* Ensure the output variables are initialized in case of error. */
331 if (result != NULL)
332 *result = NULL;
333
334 /* Get the array of devices. */
335 array = json_object_get(json, key);
336 if (array == NULL || json_is_null(array))
337 return WA_ERR_NONE;
338 if (!json_is_array(array)) {
339 s = WA_ERR_REMOTE_FAILURE;
340 return wai_error_set(ctx, s, "value of %s is not an array", key);
341 }
342
343 /* Walk the array looking for objects. */
344 for (i = 0; i < json_array_size(array); i++) {
345 value = json_array_get(array, i);
346 if (!json_is_object(value)) {
347 s = WA_ERR_REMOTE_FAILURE;
348 return wai_error_set(ctx, s, "%s element is not object", key);
349 }
350
351 /* Create a new device entry, allocating the array if needed. */
352 if (devices == NULL) {
353 size = sizeof(struct webauth_device);
354 devices = apr_array_make(ctx->pool, 5, size);
355 }
356 device = apr_array_push(devices);
357 memset(device, 0, sizeof(*device));
358
359 /* Parse the login information. */
360 PARSE_STRING(ctx, value, "name", &device->name);
361 PARSE_STRING(ctx, value, "id", &device->id);
362 PARSE_FACTORS(ctx, value, "factors", &device->factors, NULL);
363 }
364
365 /* Return the results. */
366 *result = devices;
367 return WA_ERR_NONE;
368 }
369
370
371 /*
372 * Given JSON returned by the webkdc-userinfo call, finish parsing it into a
373 * newly-allocated webauth_user_info struct. This function and all of the
374 * functions it calls intentionally ignores unknown JSON attributes. Returns
375 * a status code.
376 */
377 static int
json_parse_user_info(struct webauth_context * ctx,json_t * json,struct webauth_user_info ** result)378 json_parse_user_info(struct webauth_context *ctx, json_t *json,
379 struct webauth_user_info **result)
380 {
381 const char *message, *detail;
382 unsigned long code;
383 json_t *value, *object, *def;
384 int s;
385 struct webauth_user_info *info;
386
387 /* Check for the success key. */
388 value = json_object_get(json, "success");
389 if (value == NULL) {
390 s = WA_ERR_REMOTE_FAILURE;
391 return wai_error_set(ctx, s, "no success key in JSON");
392 }
393
394 /* Create the data structure for the reply and pull out login_state. */
395 info = apr_pcalloc(ctx->pool, sizeof(struct webauth_user_info));
396 PARSE_STRING(ctx, json, "login_state", &info->login_state);
397
398 /*
399 * If validation failed, return the message_detail string as the user
400 * message if it is set. If it is not set, report an internal error using
401 * the message and the code.
402 */
403 if (json_is_false(value)) {
404 PARSE_INTEGER(ctx, json, "code", &code);
405 PARSE_STRING( ctx, json, "message", &message);
406 PARSE_STRING( ctx, json, "message_detail", &detail);
407 if (detail == NULL) {
408 s = WA_ERR_REMOTE_FAILURE;
409 return wai_error_set(ctx, s, "%s [%lu]", message, code);
410 } else {
411 wai_log_notice(ctx, "userinfo: webkdc-userinfo failed: %s [%lu]",
412 message, code);
413 info->error = detail;
414 *result = info;
415 return WA_ERR_NONE;
416 }
417 }
418
419 /* Grab the result key, which contains the meat of the reply. */
420 object = json_object_get(json, "response");
421 if (object == NULL || !json_is_object(object)) {
422 s = WA_ERR_REMOTE_FAILURE;
423 return wai_error_set(ctx, s, "no or malformed response key in JSON");
424 }
425
426 /* User info succeeded. Pull the data out of the JSON reply. */
427 PARSE_INTEGER(ctx, object, "persistent_threshold", &info->valid_threshold);
428 PARSE_INTEGER(ctx, object, "password_expires", &info->password_expires);
429 PARSE_INTEGER(ctx, object, "max_level_of_assurance", &info->max_loa);
430 PARSE_STRING( ctx, object, "message", &info->user_message);
431 PARSE_FACTORS(ctx, object, "available_factors", &info->factors, NULL);
432 PARSE_FACTORS(ctx, object, "additional_factors", &info->additional, NULL);
433 PARSE_FACTORS(ctx, object, "required_factors", &info->required, NULL);
434 PARSE_HISTORY(ctx, object, "logins", &info->logins);
435 PARSE_DEVICES(ctx, object, "devices", &info->devices);
436
437 /* If there is default device information, extract that. */
438 def = json_object_get(object, "default");
439 if (def != NULL) {
440 if (!json_is_object(def)) {
441 s = WA_ERR_REMOTE_FAILURE;
442 return wai_error_set(ctx, s, "malformed default device in JSON");
443 }
444 PARSE_STRING(ctx, def, "id", &info->default_device);
445 PARSE_STRING(ctx, def, "capability", &info->default_factor);
446 }
447
448 /* Return the results. */
449 *result = info;
450 return WA_ERR_NONE;
451 }
452
453
454 /*
455 * Given JSON returned by the webkdc-validate call, finish parsing it into a
456 * newly-allocated webauth_user_validate struct. This function and all of the
457 * functions it calls intentionally ignores unknown JSON attributes. Returns
458 * a status code.
459 */
460 static int
json_parse_user_validate(struct webauth_context * ctx,json_t * json,struct webauth_user_validate ** result)461 json_parse_user_validate(struct webauth_context *ctx, json_t *json,
462 struct webauth_user_validate **result)
463 {
464 const char *message, *detail;
465 unsigned long code;
466 json_t *value, *object;
467 int s;
468 struct webauth_user_validate *validate;
469
470 /* Check for the success key. */
471 value = json_object_get(json, "success");
472 if (value == NULL) {
473 s = WA_ERR_REMOTE_FAILURE;
474 return wai_error_set(ctx, s, "no success key in JSON");
475 }
476
477 /* Create the data structure for the reply. */
478 validate = apr_pcalloc(ctx->pool, sizeof(struct webauth_user_validate));
479 PARSE_STRING(ctx, json, "login_state", &validate->login_state);
480
481 /*
482 * If validation failed, return the message_detail string as the user
483 * message if it is set. If it is not set, report an internal error using
484 * the message and the code.
485 */
486 if (json_is_false(value)) {
487 PARSE_INTEGER(ctx, json, "code", &code);
488 PARSE_STRING( ctx, json, "message", &message);
489 PARSE_STRING( ctx, json, "message_detail", &detail);
490 if (detail == NULL) {
491 s = WA_ERR_REMOTE_FAILURE;
492 return wai_error_set(ctx, s, "%s [%lu]", message, code);
493 } else {
494 wai_log_notice(ctx, "userinfo: webkdc-validate failed: %s [%lu]",
495 message, code);
496 validate->user_message = detail;
497 *result = validate;
498 return WA_ERR_NONE;
499 }
500 }
501 validate->success = true;
502
503 /* Grab the result key, which contains the meat of the reply. */
504 object = json_object_get(json, "response");
505 if (object == NULL || !json_is_object(object)) {
506 s = WA_ERR_REMOTE_FAILURE;
507 return wai_error_set(ctx, s, "no or malformed response key in JSON");
508 }
509
510 /* Validation succeeded. Pull the data out of the JSON reply. */
511 PARSE_INTEGER(ctx, object, "persistent_threshold",
512 &validate->valid_threshold);
513 PARSE_INTEGER(ctx, object, "level_of_assurance", &validate->loa);
514 PARSE_STRING( ctx, object, "message", &validate->user_message);
515 PARSE_FACTORS(ctx, object, "factors", &validate->factors,
516 &validate->factors_expiration);
517 PARSE_FACTORS(ctx, object, "persistent_factors", &validate->persistent,
518 &validate->persistent_expiration);
519
520 /* Return the results. */
521 *result = validate;
522 return WA_ERR_NONE;
523 }
524
525
526 /*
527 * Given an JSON document in a struct wai_buffer, parse it into a json_t
528 * structure and store it in the provided argument. The contents of the
529 * buffer must be nul-terminated. The caller is responsible for decrementing
530 * the reference count on the json_t object when finished with it.
531 *
532 * If the parse fails, set the WebAuth error and return a status code.
533 */
534 static int
json_parse_document(struct webauth_context * ctx,struct wai_buffer * string,json_t ** json)535 json_parse_document(struct webauth_context *ctx, struct wai_buffer *string,
536 json_t **json)
537 {
538 json_error_t error;
539
540 *json = json_loads(string->data, 0, &error);
541 if (*json == NULL)
542 return wai_error_set(ctx, WA_ERR_REMOTE_FAILURE,
543 "JSON parse error: %s at byte %lu in %s",
544 error.text, (unsigned long) error.position,
545 string->data);
546 return WA_ERR_NONE;
547 }
548
549
550 /*
551 * Construct a remctl command vector given the subcommand and the JSON to use
552 * as the argument. Sets result to point to a a NULL-terminated array of
553 * strings suitable for passing to remctl_command. Returns a status code.
554 */
555 static int
json_command(struct webauth_context * ctx,const char * subcommand,json_t * json,const char *** result)556 json_command(struct webauth_context *ctx, const char *subcommand,
557 json_t *json, const char ***result)
558 {
559 const char **argv;
560 char *data;
561
562 /* Ensure the output variable is NULL on any error. */
563 *result = NULL;
564
565 /* Generate the JSON representation of the call data. */
566 data = json_dumps(json, 0);
567 if (data == NULL)
568 return wai_error_set(ctx, WA_ERR_NO_MEM, "cannot build JSON call");
569
570 /* Build the remctl command vector. */
571 argv = apr_pcalloc(ctx->pool, 4 * sizeof(const char *));
572 argv[0] = ctx->user->command;
573 argv[1] = subcommand;
574 argv[2] = apr_pstrdup(ctx->pool, data);
575 argv[3] = NULL;
576 *result = argv;
577
578 /* Free resources and return. */
579 free(data);
580 return WA_ERR_NONE;
581 }
582
583
584 /*
585 * Construct the JSON data for a webkdc-userinfo command. protocol. Takes
586 * the user, ip, random flag, URL, and factor string, and stores the result in
587 * the result argument. Caller is responsible for calling decref on the JSON
588 * structure when finished with it. Returns a status code.
589 */
590 static int
json_command_userinfo(struct webauth_context * ctx,const char * user,const char * ip,int random_mf,const char * url,const char * factors_string,json_t ** result)591 json_command_userinfo(struct webauth_context *ctx, const char *user,
592 const char *ip, int random_mf, const char *url,
593 const char *factors_string, json_t **result)
594 {
595 json_t *json = NULL, *factors_json = NULL;
596 json_t *factor;
597 struct webauth_factors *factors;
598 apr_array_header_t *factors_array;
599 int i;
600
601 /* Build the root JSON object. */
602 json = json_object();
603 if (json == NULL)
604 goto fail;
605
606 /* Add the simple values from our call. */
607 ADD_STRING(json, "username", user);
608 if (ip != NULL)
609 ADD_STRING(json, "ip", ip);
610 ADD_INTEGER(json, "timestamp", time(NULL));
611 ADD_BOOLEAN(json, "random", random_mf);
612 if (url != NULL)
613 ADD_STRING(json, "return_url", url);
614
615 /* Add the array of factors. */
616 factors = webauth_factors_parse(ctx, factors_string);
617 factors_array = webauth_factors_array(ctx, factors);
618 factors_json = json_array();
619 for (i = 0; i < factors_array->nelts; i++) {
620 factor = json_string(APR_ARRAY_IDX(factors_array, i, const char *));
621 if (factor == NULL)
622 goto fail;
623 if (json_array_append_new(factors_json, factor) < 0) {
624 json_decref(factor);
625 goto fail;
626 }
627 }
628 if (json_object_set_new(json, "factors", factors_json) < 0)
629 goto fail;
630
631 /* Return the result. */
632 *result = json;
633 return WA_ERR_NONE;
634
635 fail:
636 if (factors_json != NULL)
637 json_decref(factors_json);
638 if (json != NULL)
639 json_decref(json);
640 *result = NULL;
641 return wai_error_set(ctx, WA_ERR_NO_MEM, "cannot build JSON call");
642 }
643
644
645 /*
646 * Call the user information service via remctl and parse the results into a
647 * webauth_user_info struct.
648 */
649 int
wai_user_info_json(struct webauth_context * ctx,const char * user,const char * ip,int random_mf,const char * url,const char * factors,struct webauth_user_info ** info)650 wai_user_info_json(struct webauth_context *ctx, const char *user,
651 const char *ip, int random_mf, const char *url,
652 const char *factors, struct webauth_user_info **info)
653 {
654 int s;
655 struct wai_buffer *output;
656 const char **argv;
657 json_t *json = NULL;
658
659 /* Build the command. */
660 s = json_command_userinfo(ctx, user, ip, random_mf, url, factors, &json);
661 if (s != WA_ERR_NONE)
662 return s;
663 s = json_command(ctx, "webkdc-userinfo", json, &argv);
664 if (s != WA_ERR_NONE)
665 return s;
666 json_decref(json);
667
668 /* Make the call. */
669 output = wai_buffer_new(ctx->pool);
670 s = wai_user_remctl(ctx, argv, output);
671 if (s != WA_ERR_NONE)
672 return s;
673
674 /* Parse the JSON results. */
675 s = json_parse_document(ctx, output, &json);
676 if (s != WA_ERR_NONE)
677 return s;
678 s = json_parse_user_info(ctx, json, info);
679 json_decref(json);
680 return s;
681 }
682
683
684 /*
685 * Construct the arguments to a webkdc-validate command using the JSON
686 * protocol. Takes the user, ip, code, type, and state information, and
687 * stores the result in the result argument. Caller is responsible for
688 * calling decref on the JSON structure when finished with it. Returns a
689 * status code.
690 */
691 static int
json_command_validate(struct webauth_context * ctx,const char * user,const char * ip,const char * code,const char * type,const char * device,const char * state,json_t ** result)692 json_command_validate(struct webauth_context *ctx, const char *user,
693 const char *ip, const char *code, const char *type,
694 const char *device, const char *state, json_t **result)
695 {
696 json_t *json = NULL, *factor = NULL;
697
698 /* Build the root JSON object. */
699 json = json_object();
700 if (json == NULL)
701 goto fail;
702
703 /* Add the simple values from our call. */
704 ADD_STRING(json, "username", user);
705 if (ip != NULL)
706 ADD_STRING(json, "ip", ip);
707 if (state != NULL)
708 ADD_STRING(json, "login_state", state);
709
710 /* Add the factor information. */
711 factor = json_object();
712 if (factor == NULL)
713 goto fail;
714 if (type != NULL)
715 ADD_STRING(factor, "capability", type);
716 if (code != NULL)
717 ADD_STRING(factor, "passcode", code);
718 if (device != NULL)
719 ADD_STRING(factor, "device", device);
720 if (json_object_set_new(json, "factor", factor) < 0)
721 goto fail;
722
723 /* Return the result. */
724 *result = json;
725 return WA_ERR_NONE;
726
727 fail:
728 if (factor != NULL)
729 json_decref(factor);
730 if (json != NULL)
731 json_decref(json);
732 *result = NULL;
733 return wai_error_set(ctx, WA_ERR_NO_MEM, "cannot build JSON call");
734 }
735
736
737 /*
738 * Call the user validation service via remctl and parse the results into a
739 * webauth_user_validate struct.
740 */
741 int
wai_user_validate_json(struct webauth_context * ctx,const char * user,const char * ip,const char * code,const char * type,const char * device,const char * state,struct webauth_user_validate ** validate)742 wai_user_validate_json(struct webauth_context *ctx, const char *user,
743 const char *ip, const char *code, const char *type,
744 const char *device, const char *state,
745 struct webauth_user_validate **validate)
746 {
747 int s;
748 const char **argv = NULL;
749 struct wai_buffer *output;
750 json_t *json = NULL;
751
752 /* Build the command. */
753 s = json_command_validate(ctx, user, ip, code, type, device, state, &json);
754 if (s != WA_ERR_NONE)
755 return s;
756 s = json_command(ctx, "webkdc-validate", json, &argv);
757 if (s != WA_ERR_NONE)
758 return s;
759 json_decref(json);
760
761 /* Make the call. */
762 output = wai_buffer_new(ctx->pool);
763 s = wai_user_remctl(ctx, argv, output);
764 if (s != WA_ERR_NONE)
765 return s;
766
767 /* Parse the JSON results. */
768 s = json_parse_document(ctx, output, &json);
769 if (s != WA_ERR_NONE)
770 return s;
771 s = json_parse_user_validate(ctx, json, validate);
772 json_decref(json);
773 return s;
774 }
775
776 #endif /* HAVE_JANSSON */
777