diff --git a/client/Makefile.am b/client/Makefile.am index 14903823e..9a3bc9d1e 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -87,7 +87,7 @@ ipa_join_LDADD = \ $(LDAP_LIBS) \ $(SASL_LIBS) \ $(XMLRPC_LIBS) \ - $(JSON_LIBS) \ + $(JANSSON_LIBS) \ $(LIBCURL_LIBS) \ $(POPT_LIBS) \ $(LIBINTL_LIBS) \ diff --git a/client/ipa-client-common.h b/client/ipa-client-common.h index 6c212615f..ea88ace7f 100644 --- a/client/ipa-client-common.h +++ b/client/ipa-client-common.h @@ -33,3 +33,9 @@ typedef struct { char *payload; size_t size; } curl_buffer; + +typedef struct { + char *dn; + char *krb_principal; + int is_provisioned; +} join_info; diff --git a/client/ipa-join.c b/client/ipa-join.c index 0315e3765..617c716a6 100644 --- a/client/ipa-join.c +++ b/client/ipa-join.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include "xmlrpc-c/base.h" @@ -665,49 +665,17 @@ jsonrpc_handle_response(char *ptr, size_t size, size_t nmemb, void *userdata) { } static int -join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) { +jsonrpc_request(const char *ipaserver, const json_t *json, curl_buffer *response, int quiet) { + int rval = 0; + CURL *curl = NULL; - CURLcode res; - struct utsname uinfo; - - char *host = NULL; char *url = NULL; char *referer = NULL; char *user_agent = NULL; struct curl_slist *headers = NULL; - curl_buffer cb; - - json_object *json_post = NULL; - json_object *array = NULL; - json_object *hostarr = NULL; - json_object *optsarr = NULL; - - json_object *json_resp = NULL; - json_object *json_result = NULL; - json_object *json_krbprinc = NULL; - - int rval = 0; - - *hostdn = NULL; - *princ = NULL; - - uname(&uinfo); - - if (!hostname) { - host = strdup(uinfo.nodename); - } else { - host = strdup(hostname); - } - - if (!host) { - if (!quiet) - fprintf(stderr, _("Out of memory!\n")); - - rval = 3; - goto cleanup; - } + char *json_str = NULL; if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { if (!quiet) @@ -726,16 +694,6 @@ join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const ch goto cleanup; } - cb.payload = (char *) malloc(sizeof(cb.payload)); - if (!cb.payload) { - if (!quiet) - fprintf(stderr, _("Out of memory!\n")); - - rval = 3; - goto cleanup; - } - cb.size = 0; - /* setting endpoint and custom headers */ ASPRINTF(&url, "https://%s/ipa/json", ipaserver); CURL_SETOPT(curl, CURLOPT_URL, url); @@ -770,7 +728,7 @@ join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const ch CURL_SETOPT(curl, CURLOPT_CAINFO, DEFAULT_CA_CERT_FILE); CURL_SETOPT(curl, CURLOPT_WRITEFUNCTION, &jsonrpc_handle_response); - CURL_SETOPT(curl, CURLOPT_WRITEDATA, &cb); + CURL_SETOPT(curl, CURLOPT_WRITEDATA, response); /* delegate authentication to GSSAPI */ CURL_SETOPT(curl, CURLOPT_GSSAPI_DELEGATION, CURLGSSAPI_DELEGATION_FLAG); @@ -780,30 +738,21 @@ join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const ch if (debug) CURL_SETOPT(curl, CURLOPT_VERBOSE, 1L); - /* create the JSON-RPC payload */ - json_post = json_object_new_object(); + json_str = json_dumps(json, 0); + if (!json_str) { + if (debug) + fprintf(stderr, _("json_dumps() failed\n")); - json_object_object_add(json_post, "method", json_object_new_string("join")); + rval = 17; + goto cleanup; + } + CURL_SETOPT(curl, CURLOPT_POSTFIELDS, json_str); - array = json_object_new_array(); - - hostarr = json_object_new_array(); - json_object_array_add(hostarr, json_object_new_string(host)); - json_object_array_add(array, json_object_get(hostarr)); - - optsarr = json_object_new_object(); - json_object_object_add(optsarr, "nsosversion", json_object_new_string(uinfo.release)); - json_object_object_add(optsarr, "nshardwareplatform", json_object_new_string(uinfo.machine)); - json_object_array_add(array, json_object_get(optsarr)); - - json_object_object_add(json_post, "params", json_object_get(array)); - - json_object_object_add(json_post, "id", json_object_new_int(0)); - - CURL_SETOPT(curl, CURLOPT_POSTFIELDS, json_object_to_json_string(json_post)); + if (debug) + fprintf(stderr, _("JSON-RPC request:\n%s\n"), json_str); /* Perform the call and check for errors */ - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { if (debug) @@ -813,35 +762,31 @@ join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const ch goto cleanup; } - if (cb.payload && debug) { - fprintf(stderr, "JSON-RPC call respone:\n%s\n", cb.payload); + long resp_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp_code); + + if (resp_code != 200) { + if (debug) + fprintf(stderr, _("JSON-RPC call failed with status code: %li\n"), resp_code); + + if (!quiet && resp_code == 401) + fprintf(stderr, _("JSON-RPC call was unauthorized. Check your credentials.\n")); + + rval = 17; + goto cleanup; } - json_resp = json_tokener_parse(cb.payload); - json_object_object_get_ex(json_resp, "result", &json_result); - - if (json_object_array_length(json_result) == 2) { - asprintf((char **)hostdn, "%s", json_object_get_string(json_object_array_get_idx(json_result, 0))); - - json_object_object_get_ex(json_object_array_get_idx(json_result, 1), "krbprincipalname", &json_krbprinc); - asprintf((char **)princ, "%s", json_object_get_string(json_object_array_get_idx(json_krbprinc, 0))); + if (debug && response->payload) { + fprintf(stderr, _("JSON-RPC response:\n%s\n"), response->payload); } cleanup: - json_object_put(optsarr); - json_object_put(hostarr); - json_object_put(array); - json_object_put(json_post); - json_object_put(json_resp); - curl_slist_free_all(headers); if (curl) curl_easy_cleanup(curl); curl_global_cleanup(); - if (host) - free(host); if (url) free(url); if (referer) @@ -849,6 +794,130 @@ cleanup: if (user_agent) free(user_agent); + if (json_str) + free(json_str); + + return rval; +} + +static int +jsonrpc_parse_join_response(const char *payload, join_info *join_i, int quiet) { + int rval = 0; + + json_error_t j_error; + + json_t *j_root = NULL; + json_t *j_result = NULL; + + j_root = json_loads(payload, 0, &j_error); + if (!j_root) { + if (debug) + fprintf(stderr, _("Parsing JSON-RPC response failed: %s\n"), j_error.text); + + rval = 17; + goto cleanup; + } + + j_result = json_object_get(j_root, "result"); + + char *tmp_hostdn = NULL; + char *tmp_princ = NULL; + char *tmp_pwdch = NULL; + if (json_unpack_ex(j_result, &j_error, 0, "[s, {s:[s], s?:[s]}]", + &tmp_hostdn, + "krbprincipalname", &tmp_princ, + "krblastpwdchange", &tmp_pwdch) != 0) { + if (debug) + fprintf(stderr, _("Extracting the data from the JSON-RPC response failed: %s\n"), j_error.text); + + rval = 17; + goto cleanup; + } + ASPRINTF(&join_i->dn, "%s", tmp_hostdn); + ASPRINTF(&join_i->krb_principal, "%s", tmp_princ); + + join_i->is_provisioned = tmp_pwdch != NULL; + +cleanup: + json_decref(j_root); + + return rval; +} + +static int +join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) { + int rval = 0; + + struct utsname uinfo; + char *host = NULL; + + curl_buffer cb = {0}; + + json_error_t j_error; + json_t *json_req = NULL; + + join_info join_i = {0}; + + *hostdn = NULL; + *princ = NULL; + + uname(&uinfo); + + if (!hostname) { + host = strdup(uinfo.nodename); + } else { + host = strdup(hostname); + } + + if (!host) { + if (!quiet) + fprintf(stderr, _("Out of memory!\n")); + + rval = 3; + goto cleanup; + } + + /* create the JSON-RPC payload */ + json_req = json_pack_ex(&j_error, 0, "{s:s, s:[[s], {s:s, s:s}]}", + "method", "join", + "params", + host, + "nsosversion", uinfo.release, + "nshardwareplatform", uinfo.machine); + + if (!json_req) { + if (debug) + fprintf(stderr, _("json_pack_ex() failed: %s\n"), j_error.text); + + rval = 17; + goto cleanup; + } + + rval = jsonrpc_request(ipaserver, json_req, &cb, quiet); + if (rval != 0) + goto cleanup; + + rval = jsonrpc_parse_join_response(cb.payload, &join_i, quiet); + if (rval != 0) + goto cleanup; + + *hostdn = join_i.dn; + *princ = join_i.krb_principal; + + if (!force && join_i.is_provisioned) { + if (!quiet) + fprintf(stderr, _("Host is already joined.\n")); + + rval = 13; + goto cleanup; + } + +cleanup: + if (host) + free(host); + + json_decref(json_req); + if (cb.payload) free(cb.payload); diff --git a/configure.ac b/configure.ac index 0d9af5b6c..e5b6d1986 100644 --- a/configure.ac +++ b/configure.ac @@ -180,40 +180,10 @@ if test x"$try_xmlrpc_fallback" = xtrue; then fi dnl --------------------------------------------------------------------------- -dnl - Check for LIBCURL +dnl - Check for jansson and libcurl for ipa-join dnl --------------------------------------------------------------------------- -PKG_CHECK_MODULES([LIBCURL], [libcurl], [], - [try_curl_fallback=true]) -if test x"$try_curl_fallback" = xtrue; then - CURL_LIBS= - AC_CHECK_HEADER([curl/curl.h], [], - [AC_MSG_ERROR([curl.h not found])]) - - AC_CHECK_LIB([curl], [curl_easy_perform], - [CURL_LIBS="-lcurl"]) - if test "x$CURL_LIBS" = "x" ; then - AC_MSG_ERROR([libcurl not found]) - fi - AC_SUBST(LIBCURL_LIBS) -fi - -dnl --------------------------------------------------------------------------- -dnl - Check for JSON-C -dnl --------------------------------------------------------------------------- -PKG_CHECK_MODULES([JSON], [json-c], [], - [try_json_fallback=true]) -if test x"$try_json_fallback" = xtrue; then - JSON_LIBS= - AC_CHECK_HEADER([json-c/json_object.h], [], - [AC_MSG_ERROR([json-c/json_object.h not found])]) - - AC_CHECK_LIB([json-c], [json_object_get], - [JSON_LIBS="-ljson-c"]) - if test "x$JSON_LIBS" = "x" ; then - AC_MSG_ERROR([json-c not found]) - fi - AC_SUBST(JSON_LIBS) -fi +PKG_CHECK_MODULES([JANSSON], [jansson]) +PKG_CHECK_MODULES([LIBCURL], [libcurl]) dnl --------------------------------------------------------------------------- dnl - Check for libintl diff --git a/freeipa.spec.in b/freeipa.spec.in index 87cb96585..062638dcf 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -167,6 +167,8 @@ BuildRequires: krb5-kdb-version = %{krb5_kdb_version} BuildRequires: krb5-devel >= %{krb5_version} # 1.27.4: xmlrpc_curl_xportparms.gssapi_delegation BuildRequires: xmlrpc-c-devel >= 1.27.4 +BuildRequires: libcurl-devel +BuildRequires: jansson-devel BuildRequires: popt-devel BuildRequires: gcc BuildRequires: make @@ -547,6 +549,7 @@ Requires: initscripts %endif Requires: libcurl >= 7.21.7-2 Requires: xmlrpc-c >= 1.27.4 +Requires: jansson Requires: sssd-ipa >= %{sssd_version} Requires: certmonger >= %{certmonger_version} Requires: nss-tools >= %{nss_version}