mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-23 15:40:01 -06:00
Require an HTTP Referer header in the server. Send one in ipa tools.
This is to prevent a Cross-Site Request Forgery (CSRF) attack where a rogue server tricks a user who was logged into the FreeIPA management interface into visiting a specially-crafted URL where the attacker could perform FreeIPA oonfiguration changes with the privileges of the logged-in user. https://bugzilla.redhat.com/show_bug.cgi?id=747710
This commit is contained in:
parent
da4b4fc4d9
commit
2d6eeb205e
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
@ -118,13 +118,33 @@ static int check_perms(const char *keytab)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user