URL: https://github.com/freeipa/freeipa/pull/3544 Author: mulatinho Title: #3544: [WIP] ipa-join: allowing call with jsonrpc into freeipa API Action: opened
PR body: """ - Adding JSON-C and LibCURL library into configure.ac and Makefile.am - Creating a API call with option '-j' or '--jsonrpc' to make host join on FreeIPA with JSONRPC and libCURL. TODO: unenroll process with JSONRPC. To test the call: # kinit admin # ipa-join -s server.freeipa.ipadomain -j Debug: # ipa-join -s server.freeipa.ipadomain -j -d Related: https://pagure.io/freeipa/issue/7966 """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/3544/head:pr3544 git checkout pr3544
From 451e0e9d74cf4a3cbcf3cb85b5306134ffe40ed6 Mon Sep 17 00:00:00 2001 From: Alexandre Mulatinho <a...@mulatinho.net> Date: Thu, 15 Aug 2019 22:51:01 -0300 Subject: [PATCH] ipa-join: allowing call with jsonrpc into freeipa API [WIP] I will edit this commit message later. Adding JSON-C and LibCURL library into configure.ac and Makefile.am Creating a API call with option '-j' or '--jsonrpc' to make host join on FreeIPA with JSONRPC and libCURL. Related: https://pagure.io/freeipa/issue/7966 Signed-off-by: Alexandre Mulatinho <a...@mulatinho.net> --- client/Makefile.am | 2 + client/ipa-client-common.h | 5 ++ client/ipa-join.c | 158 +++++++++++++++++++++++++++++++++++-- configure.ac | 36 +++++++++ 4 files changed, 195 insertions(+), 6 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index 3d17432a6c..4fda835ce1 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -86,6 +86,8 @@ ipa_join_LDADD = \ $(LDAP_LIBS) \ $(SASL_LIBS) \ $(XMLRPC_LIBS) \ + $(JSON_LIBS) \ + $(LIBCURL_LIBS) \ $(POPT_LIBS) \ $(LIBINTL_LIBS) \ $(NULL) diff --git a/client/ipa-client-common.h b/client/ipa-client-common.h index d0db0637e3..8fe2988f56 100644 --- a/client/ipa-client-common.h +++ b/client/ipa-client-common.h @@ -28,3 +28,8 @@ #endif int init_gettext(void); + +struct json_results { + char *hostdn; + char *krbprinc; +}; diff --git a/client/ipa-join.c b/client/ipa-join.c index 750114896f..f53128c2b6 100644 --- a/client/ipa-join.c +++ b/client/ipa-join.c @@ -34,6 +34,9 @@ #include <sys/stat.h> #include <fcntl.h> #include <sys/wait.h> +#include <curl/curl.h> +#include <json-c/json.h> +#include <limits.h> #include "xmlrpc-c/base.h" #include "xmlrpc-c/client.h" @@ -47,10 +50,15 @@ #define IPA_CONFIG "/etc/ipa/default.conf" +#define BUFSIZE 1024 + char * read_config_file(const char *filename); char * get_config_entry(char * data, const char *section, const char *key); static int debug = 0; +static int use_json = 0; + +struct json_results *results = NULL; /* * Translate some IPA exceptions into specific errors in this context. @@ -167,7 +175,7 @@ callRPC(char * user_agent, /* Have curl do SSL certificate validation */ curlXportParmsP->no_ssl_verifypeer = 0; curlXportParmsP->no_ssl_verifyhost = 0; - curlXportParmsP->cainfo = "/etc/ipa/ca.crt"; + curlXportParmsP->cainfo = DEFAULT_CA_CERT_FILE; curlXportParmsP->user_agent = user_agent; /* Enable GSSAPI credentials delegation */ curlXportParmsP->gssapi_delegation = 1; @@ -479,7 +487,7 @@ join_ldap(const char *ipaserver, char *hostname, char ** binddn, const char *bin } static int -join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) { +join_krb5_xmlrpc(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) { xmlrpc_env env; xmlrpc_value * argArrayP = NULL; xmlrpc_value * paramArrayP = NULL; @@ -612,6 +620,133 @@ join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **pri return rval; } +size_t +jsonrpc_handle_response(char *ptr, size_t size, size_t nmemb, void *userdata) { + struct json_object *jsonobj, *result, *krb5princ, *hostdn, *arr, *content; + size_t arr_length; + int ret = 0; + + if (debug) { + fprintf(stdout, "JSONRPC CALL Respone:\n%s\n", ptr); + } + + jsonobj = json_tokener_parse(ptr); + json_object_object_get_ex(jsonobj, "result", &result); + + arr_length = json_object_array_length(result); + + if (arr_length == 2) { + results = (struct json_results *) malloc(sizeof(struct json_results)); + + hostdn = json_object_array_get_idx(result, 0); + ret = asprintf(&results->hostdn, "%s", json_object_get_string(hostdn)); + + arr = json_object_array_get_idx(result, 1); + json_object_object_get_ex(arr, "krbprincipalname", &content); + krb5princ = json_object_array_get_idx(content, 0); + ret = asprintf(&results->krbprinc, "%s", json_object_get_string(krb5princ)); + } + + json_object_put(jsonobj); + + return size * nmemb; +} + +static int +join_krb5_jsonrpc(const char *ipaserver, char *hostname, char **hostdn, const char **princ, int force, int quiet) { + CURL *curl; + CURLcode res; + struct curl_slist *chunk = NULL; + struct json_object *jsonobj, *array, *hostarr, *optsarr; + struct utsname uinfo; + char *host = NULL; + char buffer[BUFSIZE]; + + uname(&uinfo); + + if (NULL == hostname) { + host = strdup(uinfo.nodename); + } else { + host = strdup(hostname); + } + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = curl_easy_init(); + if (!curl) { + curl_global_cleanup(); + return 1; + } + + /* setting endpoint and custom headers */ + snprintf(buffer, sizeof(buffer)-1, "https://%s/ipa/json", ipaserver); + curl_easy_setopt(curl, CURLOPT_URL, buffer); + + snprintf(buffer, sizeof(buffer)-1, "referer: https://%s/ipa", ipaserver); + chunk = curl_slist_append(chunk, buffer); + snprintf(buffer, sizeof(buffer)-1, "User-Agent: %s/%s", NAME, VERSION); + chunk = curl_slist_append(chunk, buffer); + + chunk = curl_slist_append(chunk, "Accept: application/json"); + chunk = curl_slist_append(chunk, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + + curl_easy_setopt(curl, CURLOPT_CAINFO, DEFAULT_CA_CERT_FILE); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &jsonrpc_handle_response); + + /* delegating authentication to gssapi */ + curl_easy_setopt(curl, CURLOPT_GSSAPI_DELEGATION, CURLGSSAPI_DELEGATION_FLAG); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE); + curl_easy_setopt(curl, CURLOPT_USERPWD, ":"); + + if (debug) + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + /* making the payload using json-rpc */ + jsonobj = json_object_new_object(); + + json_object_object_add(jsonobj, "method", json_object_new_string("join")); + + 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(jsonobj, "params", json_object_get(array)); + + json_object_object_add(jsonobj, "id", json_object_new_int(0)); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_to_json_string(jsonobj)); + + /* Perform the call and check for errors if debug is setted */ + res = curl_easy_perform(curl); + if (res != CURLE_OK && debug != 0) + fprintf(stderr, _("jsonrpc call failed: %s\n"), curl_easy_strerror(res)); + + json_object_put(optsarr); + json_object_put(hostarr); + json_object_put(array); + json_object_put(jsonobj); + + curl_slist_free_all(chunk); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + free(host); + + return 0; +} + static int unenroll_host(const char *server, const char *hostname, const char *ktname, int quiet) { @@ -913,8 +1048,11 @@ join(const char *server, const char *hostname, const char *bindpw, const char *b rval = 6; goto cleanup; } - rval = join_krb5(ipaserver, host, &hostdn, &princ, force, - quiet); + + if (!use_json) + rval = join_krb5_xmlrpc(ipaserver, host, &hostdn, &princ, force, quiet); + else + rval = join_krb5_jsonrpc(ipaserver, host, &hostdn, &princ, force, quiet); } if (rval) goto cleanup; @@ -938,12 +1076,12 @@ join(const char *server, const char *hostname, const char *bindpw, const char *b argv[arg++] = "-s"; argv[arg++] = ipaserver; argv[arg++] = "-p"; - argv[arg++] = (char *)princ; + argv[arg++] = use_json == 0 ? (char *)princ : (char *)results->krbprinc; argv[arg++] = "-k"; argv[arg++] = (char *)keytab; if (bindpw) { argv[arg++] = "-D"; - argv[arg++] = (char *)hostdn; + argv[arg++] = use_json == 0 ? (char *)hostdn : (char *)results->hostdn; argv[arg++] = "-w"; argv[arg++] = (char *)bindpw; } @@ -989,6 +1127,12 @@ join(const char *server, const char *hostname, const char *bindpw, const char *b if (ccache) krb5_cc_close(krbctx, ccache); if (krbctx) krb5_free_context(krbctx); + if (results != NULL) { + free(results->hostdn); + free(results->krbprinc); + free(results); + } + return rval; } @@ -1027,6 +1171,8 @@ main(int argc, const char **argv) { _("LDAP password (if not using Kerberos)"), _("password") }, { "basedn", 'b', POPT_ARG_STRING, &basedn, 0, _("LDAP basedn"), _("basedn") }, + { "jsonrpc", 'j', POPT_ARG_NONE, &use_json, 0, + _("Use a JSON-RPC call instead of XML-RPC"), NULL }, POPT_AUTOHELP POPT_TABLEEND }; diff --git a/configure.ac b/configure.ac index 4dad4b09e3..e18ea495b9 100644 --- a/configure.ac +++ b/configure.ac @@ -178,6 +178,42 @@ if test x"$try_xmlrpc_fallback" = xtrue; then AC_SUBST(XMLRPC_LIBS) fi +dnl --------------------------------------------------------------------------- +dnl - Check for LIBCURL +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 + dnl --------------------------------------------------------------------------- dnl - Check for libintl dnl ---------------------------------------------------------------------------
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org