mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-10-12 16:32:01 -05: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:
1 parent
da4b4fc4d9
commit
2d6eeb205e
5 files changed
+67
-5
No files matched your search
@@ -264,6 +264,9 @@ def uninstall(options, env, quiet=False):
|
|||||||
if not options.on_master and os.path.exists('/etc/ipa/default.conf'):
|
if not options.on_master and os.path.exists('/etc/ipa/default.conf'):
|
||||||
emit_quiet(quiet, "Unenrolling client from IPA server")
|
emit_quiet(quiet, "Unenrolling client from IPA server")
|
||||||
join_args = ["/usr/sbin/ipa-join", "--unenroll", "-h", hostname]
|
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)
|
(stdout, stderr, returncode) = run(join_args, raiseonerr=False, env=env)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
emit_quiet(quiet, "Unenrolling host failed: %s" % stderr)
|
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)]
|
join_args = ["/usr/sbin/ipa-join", "-s", cli_server, "-b", realm_to_suffix(cli_realm)]
|
||||||
if options.debug:
|
if options.debug:
|
||||||
join_args.append("-d")
|
join_args.append("-d")
|
||||||
|
env['XMLRPC_TRACE_CURL'] = 'yes'
|
||||||
if options.hostname:
|
if options.hostname:
|
||||||
join_args.append("-h")
|
join_args.append("-h")
|
||||||
join_args.append(options.hostname)
|
join_args.append(options.hostname)
|
||||||
|
|||||||
+37
-4
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -40,7 +41,6 @@
|
|||||||
#include "ipa-client-common.h"
|
#include "ipa-client-common.h"
|
||||||
|
|
||||||
#define NAME "ipa-join"
|
#define NAME "ipa-join"
|
||||||
#define VERSION "1.0"
|
|
||||||
|
|
||||||
#define JOIN_OID "2.16.840.1.113730.3.8.10.3"
|
#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;
|
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
|
* 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
|
* a connection over SSL using the CA cert that should have been installed
|
||||||
* by ipa-client-install.
|
* by ipa-client-install.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
callRPC(xmlrpc_env * const envP,
|
callRPC(char * user_agent,
|
||||||
|
xmlrpc_env * const envP,
|
||||||
xmlrpc_server_info * const serverInfoP,
|
xmlrpc_server_info * const serverInfoP,
|
||||||
const char * const methodName,
|
const char * const methodName,
|
||||||
xmlrpc_value * const paramArrayP,
|
xmlrpc_value * const paramArrayP,
|
||||||
@@ -149,6 +169,7 @@ callRPC(xmlrpc_env * const envP,
|
|||||||
curlXportParmsP->no_ssl_verifypeer = 1;
|
curlXportParmsP->no_ssl_verifypeer = 1;
|
||||||
curlXportParmsP->no_ssl_verifyhost = 1;
|
curlXportParmsP->no_ssl_verifyhost = 1;
|
||||||
curlXportParmsP->cainfo = "/etc/ipa/ca.crt";
|
curlXportParmsP->cainfo = "/etc/ipa/ca.crt";
|
||||||
|
curlXportParmsP->user_agent = user_agent;
|
||||||
/* Enable GSSAPI credentials delegation */
|
/* Enable GSSAPI credentials delegation */
|
||||||
curlXportParmsP->gssapi_delegation = 1;
|
curlXportParmsP->gssapi_delegation = 1;
|
||||||
|
|
||||||
@@ -523,6 +544,7 @@ join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **pri
|
|||||||
xmlrpc_value *hostdnP = NULL;
|
xmlrpc_value *hostdnP = NULL;
|
||||||
const char *krblastpwdchange = NULL;
|
const char *krblastpwdchange = NULL;
|
||||||
char * url = NULL;
|
char * url = NULL;
|
||||||
|
char * user_agent = NULL;
|
||||||
int rval = 0;
|
int rval = 0;
|
||||||
int ret;
|
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_array_append_item(&env, paramArrayP, optionsP);
|
||||||
xmlrpc_DECREF(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)) {
|
if (handle_fault(&env)) {
|
||||||
rval = 17;
|
rval = 17;
|
||||||
goto cleanup_xmlrpc;
|
goto cleanup_xmlrpc;
|
||||||
@@ -640,6 +666,7 @@ cleanup:
|
|||||||
if (resultP) xmlrpc_DECREF(resultP);
|
if (resultP) xmlrpc_DECREF(resultP);
|
||||||
|
|
||||||
cleanup_xmlrpc:
|
cleanup_xmlrpc:
|
||||||
|
free(user_agent);
|
||||||
free(url);
|
free(url);
|
||||||
free((char *)krblastpwdchange);
|
free((char *)krblastpwdchange);
|
||||||
xmlrpc_env_clean(&env);
|
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_server_info * serverInfoP = NULL;
|
||||||
xmlrpc_value *princP = NULL;
|
xmlrpc_value *princP = NULL;
|
||||||
char * url = NULL;
|
char * url = NULL;
|
||||||
|
char * user_agent = NULL;
|
||||||
|
|
||||||
if (server) {
|
if (server) {
|
||||||
ipaserver = strdup(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_array_append_item(&env, paramArrayP, argArrayP);
|
||||||
xmlrpc_DECREF(paramP);
|
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)) {
|
if (handle_fault(&env)) {
|
||||||
rval = 17;
|
rval = 17;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -845,6 +877,7 @@ unenroll_host(const char *server, const char *hostname, const char *ktname, int
|
|||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
|
||||||
|
free(user_agent);
|
||||||
if (keytab) krb5_kt_close(krbctx, keytab);
|
if (keytab) krb5_kt_close(krbctx, keytab);
|
||||||
free((char *)principal);
|
free((char *)principal);
|
||||||
free((char *)ipaserver);
|
free((char *)ipaserver);
|
||||||
|
|||||||
@@ -441,6 +441,23 @@ class XMLRPCMarshallError(PublicError):
|
|||||||
errno = 910
|
errno = 910
|
||||||
format = _('error marshalling data for XML-RPC transport: %(error)s')
|
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
|
# 1000 - 1999: Authentication errors
|
||||||
class AuthenticationError(PublicError):
|
class AuthenticationError(PublicError):
|
||||||
|
|||||||
@@ -208,6 +208,9 @@ class LanguageAwareTransport(Transport):
|
|||||||
extra_headers.append(
|
extra_headers.append(
|
||||||
('Accept-Language', lang.replace('_', '-'))
|
('Accept-Language', lang.replace('_', '-'))
|
||||||
)
|
)
|
||||||
|
extra_headers.append(
|
||||||
|
('Referer', 'https://%s/ipa/xml' % str(host))
|
||||||
|
)
|
||||||
|
|
||||||
return (host, extra_headers, x509)
|
return (host, extra_headers, x509)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from cgi import parse_qs
|
|||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
from xmlrpclib import Fault
|
from xmlrpclib import Fault
|
||||||
from ipalib.backend import Executioner
|
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.request import context, Connection, destroy_context
|
||||||
from ipalib.rpc import xml_dumps, xml_loads
|
from ipalib.rpc import xml_dumps, xml_loads
|
||||||
from ipalib.util import make_repr
|
from ipalib.util import make_repr
|
||||||
@@ -200,6 +200,11 @@ class WSGIExecutioner(Executioner):
|
|||||||
options = {}
|
options = {}
|
||||||
if not 'KRB5CCNAME' in environ:
|
if not 'KRB5CCNAME' in environ:
|
||||||
return self.marshal(result, CCacheError(), _id)
|
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:
|
try:
|
||||||
if ('HTTP_ACCEPT_LANGUAGE' in environ):
|
if ('HTTP_ACCEPT_LANGUAGE' in environ):
|
||||||
lang_reg_w_q = environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0]
|
lang_reg_w_q = environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0]
|
||||||
|
|||||||
Reference in new issue
Block a user