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