diff options
-rwxr-xr-x | ipa-client/ipa-install/ipa-client-install | 4 | ||||
-rw-r--r-- | ipa-client/ipa-join.c | 41 | ||||
-rw-r--r-- | ipalib/errors.py | 17 | ||||
-rw-r--r-- | ipalib/rpc.py | 3 | ||||
-rw-r--r-- | ipaserver/rpcserver.py | 7 |
5 files changed, 67 insertions, 5 deletions
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index e763d07a7..8e945ce90 100755 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -264,6 +264,9 @@ def uninstall(options, env, quiet=False): if not options.on_master and os.path.exists('/etc/ipa/default.conf'): emit_quiet(quiet, "Unenrolling client from IPA server") join_args = ["/usr/sbin/ipa-join", "--unenroll", "-h", hostname] + if options.debug: + join_args.append("-d") + env['XMLRPC_TRACE_CURL'] = 'yes' (stdout, stderr, returncode) = run(join_args, raiseonerr=False, env=env) if returncode != 0: emit_quiet(quiet, "Unenrolling host failed: %s" % stderr) @@ -1037,6 +1040,7 @@ def install(options, env, fstore, statestore): join_args = ["/usr/sbin/ipa-join", "-s", cli_server, "-b", realm_to_suffix(cli_realm)] if options.debug: join_args.append("-d") + env['XMLRPC_TRACE_CURL'] = 'yes' if options.hostname: join_args.append("-h") join_args.append(options.hostname) diff --git a/ipa-client/ipa-join.c b/ipa-client/ipa-join.c index 6a8523135..c174e2c15 100644 --- a/ipa-client/ipa-join.c +++ b/ipa-client/ipa-join.c @@ -19,6 +19,7 @@ #define _GNU_SOURCE +#include "config.h" #include <unistd.h> #include <stdlib.h> #include <stdio.h> @@ -40,7 +41,6 @@ #include "ipa-client-common.h" #define NAME "ipa-join" -#define VERSION "1.0" #define JOIN_OID "2.16.840.1.113730.3.8.10.3" @@ -119,12 +119,32 @@ static int check_perms(const char *keytab) } /* + * There is no API in xmlrpc-c to set arbitrary headers but we can fake it + * by using a specially-crafted User-Agent string. + * + * The caller is responsible for freeing the return value. + */ +char * +set_user_agent(const char *ipaserver) { + int ret; + char *user_agent = NULL; + + ret = asprintf(&user_agent, "%s/%s\r\nReferer: https://%s/ipa/xml\r\nX-Original-User-Agent:", NAME, VERSION, ipaserver); + if (ret == -1) { + fprintf(stderr, _("Out of memory!")); + return NULL; + } + return user_agent; +} + +/* * Make an XML-RPC call to methodName. This uses the curl client to make * a connection over SSL using the CA cert that should have been installed * by ipa-client-install. */ static void -callRPC(xmlrpc_env * const envP, +callRPC(char * user_agent, + xmlrpc_env * const envP, xmlrpc_server_info * const serverInfoP, const char * const methodName, xmlrpc_value * const paramArrayP, @@ -149,6 +169,7 @@ callRPC(xmlrpc_env * const envP, curlXportParmsP->no_ssl_verifypeer = 1; curlXportParmsP->no_ssl_verifyhost = 1; curlXportParmsP->cainfo = "/etc/ipa/ca.crt"; + curlXportParmsP->user_agent = user_agent; /* Enable GSSAPI credentials delegation */ curlXportParmsP->gssapi_delegation = 1; @@ -523,6 +544,7 @@ join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **pri xmlrpc_value *hostdnP = NULL; const char *krblastpwdchange = NULL; char * url = NULL; + char * user_agent = NULL; int rval = 0; int ret; @@ -575,7 +597,11 @@ join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **pri xmlrpc_array_append_item(&env, paramArrayP, optionsP); xmlrpc_DECREF(optionsP); - callRPC(&env, serverInfoP, "join", paramArrayP, &resultP); + if ((user_agent = set_user_agent(ipaserver)) == NULL) { + rval = 3; + goto cleanup; + } + callRPC(user_agent, &env, serverInfoP, "join", paramArrayP, &resultP); if (handle_fault(&env)) { rval = 17; goto cleanup_xmlrpc; @@ -640,6 +666,7 @@ cleanup: if (resultP) xmlrpc_DECREF(resultP); cleanup_xmlrpc: + free(user_agent); free(url); free((char *)krblastpwdchange); xmlrpc_env_clean(&env); @@ -676,6 +703,7 @@ unenroll_host(const char *server, const char *hostname, const char *ktname, int xmlrpc_server_info * serverInfoP = NULL; xmlrpc_value *princP = NULL; char * url = NULL; + char * user_agent = NULL; if (server) { ipaserver = strdup(server); @@ -817,7 +845,11 @@ unenroll_host(const char *server, const char *hostname, const char *ktname, int xmlrpc_array_append_item(&env, paramArrayP, argArrayP); xmlrpc_DECREF(paramP); - callRPC(&env, serverInfoP, "host_disable", paramArrayP, &resultP); + if ((user_agent = set_user_agent(ipaserver)) == NULL) { + rval = 3; + goto cleanup; + } + callRPC(user_agent, &env, serverInfoP, "host_disable", paramArrayP, &resultP); if (handle_fault(&env)) { rval = 17; goto cleanup; @@ -845,6 +877,7 @@ unenroll_host(const char *server, const char *hostname, const char *ktname, int cleanup: + free(user_agent); if (keytab) krb5_kt_close(krbctx, keytab); free((char *)principal); free((char *)ipaserver); diff --git a/ipalib/errors.py b/ipalib/errors.py index 4463fee70..5b634880d 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -441,6 +441,23 @@ class XMLRPCMarshallError(PublicError): errno = 910 format = _('error marshalling data for XML-RPC transport: %(error)s') + +class RefererError(PublicError): + """ + **911** Raised when the the request does not contain an HTTP referer + + For example: + + >>> raise RefererError() + Traceback (most recent call last): + ... + RefererError: Missing or invalid HTTP Referer + """ + + errno = 911 + format = _('Missing or invalid HTTP Referer, %(referer)s') + + ############################################################################## # 1000 - 1999: Authentication errors class AuthenticationError(PublicError): diff --git a/ipalib/rpc.py b/ipalib/rpc.py index f8e4d9e6a..8ec3a2f27 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -208,6 +208,9 @@ class LanguageAwareTransport(Transport): extra_headers.append( ('Accept-Language', lang.replace('_', '-')) ) + extra_headers.append( + ('Referer', 'https://%s/ipa/xml' % str(host)) + ) return (host, extra_headers, x509) diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 68d4379bb..e12f2706e 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -27,7 +27,7 @@ from cgi import parse_qs from xml.sax.saxutils import escape from xmlrpclib import Fault from ipalib.backend import Executioner -from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError +from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError, RefererError from ipalib.request import context, Connection, destroy_context from ipalib.rpc import xml_dumps, xml_loads from ipalib.util import make_repr @@ -200,6 +200,11 @@ class WSGIExecutioner(Executioner): options = {} if not 'KRB5CCNAME' in environ: return self.marshal(result, CCacheError(), _id) + self.debug('Request environment: %s' % environ) + if not 'HTTP_REFERER' in environ: + return self.marshal(result, RefererError(referer='missing'), _id) + if not environ['HTTP_REFERER'].startswith('https://%s/ipa' % self.api.env.host) and not self.env.in_tree: + return self.marshal(result, RefererError(referer=environ['HTTP_REFERER']), _id) try: if ('HTTP_ACCEPT_LANGUAGE' in environ): lang_reg_w_q = environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0] |