Imported Debian patch 4.0.5-6~numeezy

This commit is contained in:
Alexandre Ellert
2016-02-17 15:07:45 +01:00
committed by Mario Fetka
parent c44de33144
commit 10dfc9587b
1203 changed files with 53869 additions and 241462 deletions

View File

@@ -17,8 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import print_function
import string
import tempfile
import subprocess
@@ -27,41 +25,32 @@ import os, sys, traceback
import copy
import stat
import shutil
import urllib2
import socket
import struct
from types import *
import re
import xmlrpclib
import datetime
import netaddr
import time
import gssapi
import krbV
import pwd
import grp
from contextlib import contextmanager
import locale
import collections
from dns import resolver, rdatatype, reversename
from dns import resolver, rdatatype
from dns.exception import DNSException
import six
from six.moves import input
from six.moves import urllib
from ipapython.ipa_log_manager import *
from ipapython import ipavalidate
from ipapython import config
from ipaplatform.paths import paths
from ipapython.dn import DN
from ipapython.dnsutil import DNSName
SHARE_DIR = paths.USR_SHARE_IPA_DIR
PLUGINS_SHARE_DIR = paths.IPA_PLUGINS
GEN_PWD_LEN = 12
# Having this in krb_utils would cause circular import
KRB5_KDC_UNREACH = 2529639068 # Cannot contact any KDC for requested realm
KRB5KDC_ERR_SVC_UNAVAILABLE = 2529638941 # A service is not available that is
# required to process the request
IPA_BASEDN_INFO = 'ipa v2.0'
try:
from subprocess import CalledProcessError
@@ -121,7 +110,7 @@ class CheckedIPAddress(netaddr.IPAddress):
# netaddr.IPAddress doesn't handle zone indices in textual
# IPv6 addresses. Try removing zone index and parse the
# address again.
if not isinstance(addr, six.string_types):
if not isinstance(addr, basestring):
raise
addr, sep, foo = addr.partition('%')
if sep != '%':
@@ -155,10 +144,8 @@ class CheckedIPAddress(netaddr.IPAddress):
elif addr.version == 6:
family = 'inet6'
result = run(
[paths.IP, '-family', family, '-oneline', 'address', 'show'],
capture_output=True)
lines = result.output.split('\n')
ipresult = run([paths.IP, '-family', family, '-oneline', 'address', 'show'])
lines = ipresult[0].split('\n')
for line in lines:
fields = line.split()
if len(fields) < 4:
@@ -242,6 +229,7 @@ def template_file(infilename, vars):
with open(infilename) as f:
return template_str(f.read(), vars)
def copy_template_file(infilename, outfilename, vars):
"""Copy a file, performing template substitutions"""
txt = template_file(infilename, vars)
@@ -250,42 +238,20 @@ def copy_template_file(infilename, outfilename, vars):
def write_tmp_file(txt):
fd = tempfile.NamedTemporaryFile('w+')
fd = tempfile.NamedTemporaryFile()
fd.write(txt)
fd.flush()
return fd
def shell_quote(string):
if isinstance(string, str):
return "'" + string.replace("'", "'\\''") + "'"
else:
return b"'" + string.replace(b"'", b"'\\''") + b"'"
return "'" + string.replace("'", "'\\''") + "'"
if six.PY3:
def _log_arg(s):
"""Convert string or bytes to a string suitable for logging"""
if isinstance(s, bytes):
return s.decode(locale.getpreferredencoding(),
errors='replace')
else:
return s
else:
_log_arg = str
class _RunResult(collections.namedtuple('_RunResult',
'output error_output returncode')):
"""Result of ipautil.run"""
def run(args, stdin=None, raiseonerr=True, nolog=(), env=None,
capture_output=False, skip_output=False, cwd=None,
runas=None, timeout=None, suplementary_groups=[],
capture_error=False, encoding=None, redirect_output=False):
def run(args, stdin=None, raiseonerr=True,
nolog=(), env=None, capture_output=True, skip_output=False, cwd=None,
runas=None):
"""
Execute an external command.
Execute a command and return stdin, stdout and the process return code.
:param args: List of arguments for the command
:param stdin: Optional input to the command
@@ -306,67 +272,24 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None,
If a value isn't found in the list it is silently ignored.
:param env: Dictionary of environment variables passed to the command.
When None, current environment is copied
:param capture_output: Capture stdout
:param skip_output: Redirect the output to /dev/null and do not log it
:param capture_output: Capture stderr and stdout
:param skip_output: Redirect the output to /dev/null and do not capture it
:param cwd: Current working directory
:param runas: Name of a user that the command should be run as. The spawned
:param runas: Name of a user that the command shold be run as. The spawned
process will have both real and effective UID and GID set.
:param timeout: Timeout if the command hasn't returned within the specified
number of seconds.
:param suplementary_groups: List of group names that will be used as
suplementary groups for subporcess.
The option runas must be specified together with this option.
:param capture_error: Capture stderr
:param encoding: For Python 3, the encoding to use for output,
error_output, and (if it's not bytes) stdin.
If None, the current encoding according to locale is used.
:param redirect_output: Redirect (error) output to standard (error) output.
:return: An object with these attributes:
`returncode`: The process' exit status
`output` and `error_output`: captured output, as strings. Under
Python 3, these are encoded with the given `encoding`.
None unless `capture_output` or `capture_error`, respectively, are
given
`raw_output`, `raw_error_output`: captured output, as bytes.
`output_log` and `error_log`: The captured output, as strings, with any
unencodable characters discarded. These should only be used
for logging or error messages.
If skip_output is given, all output-related attributes on the result
(that is, all except `returncode`) are None.
For backwards compatibility, the return value can also be used as a
(output, error_output, returncode) triple.
"""
assert isinstance(suplementary_groups, list)
p_in = None
p_out = None
p_err = None
if isinstance(nolog, six.string_types):
if isinstance(nolog, basestring):
# We expect a tuple (or list, or other iterable) of nolog strings.
# Passing just a single string is bad: strings are iterable, so this
# Passing just a single string is bad: strings are also, so this
# would result in every individual character of that string being
# replaced by XXXXXXXX.
# This is a sanity check to prevent that.
raise ValueError('nolog must be a tuple of strings.')
if skip_output and (capture_output or capture_error):
raise ValueError('skip_output is incompatible with '
'capture_output or capture_error')
if redirect_output and (capture_output or capture_error):
raise ValueError('redirect_output is incompatible with '
'capture_output or capture_error')
if skip_output and redirect_output:
raise ValueError('skip_output is incompatible with redirect_output')
if env is None:
# copy default env
env = copy.deepcopy(os.environ)
@@ -375,53 +298,29 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None,
p_in = subprocess.PIPE
if skip_output:
p_out = p_err = open(paths.DEV_NULL, 'w')
elif redirect_output:
p_out = sys.stdout
p_err = sys.stderr
else:
elif capture_output:
p_out = subprocess.PIPE
p_err = subprocess.PIPE
if encoding is None:
encoding = locale.getpreferredencoding()
if six.PY3 and isinstance(stdin, str):
stdin = stdin.encode(encoding)
if timeout:
# If a timeout was provided, use the timeout command
# to execute the requested command.
args[0:0] = [paths.BIN_TIMEOUT, str(timeout)]
arg_string = nolog_replace(' '.join(_log_arg(a) for a in args), nolog)
arg_string = nolog_replace(' '.join(shell_quote(a) for a in args), nolog)
root_logger.debug('Starting external process')
root_logger.debug('args=%s' % arg_string)
preexec_fn = None
if runas is not None:
pent = pwd.getpwnam(runas)
suplementary_gids = [
grp.getgrnam(group).gr_gid for group in suplementary_groups
]
root_logger.debug('runas=%s (UID %d, GID %s)', runas,
pent.pw_uid, pent.pw_gid)
if suplementary_groups:
for group, gid in zip(suplementary_groups, suplementary_gids):
root_logger.debug('suplementary_group=%s (GID %d)', group, gid)
preexec_fn = lambda: (
os.setgroups(suplementary_gids),
os.setregid(pent.pw_gid, pent.pw_gid),
os.setreuid(pent.pw_uid, pent.pw_uid),
)
preexec_fn = lambda: (os.setregid(pent.pw_gid, pent.pw_gid),
os.setreuid(pent.pw_uid, pent.pw_uid))
try:
p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err,
close_fds=True, env=env, cwd=cwd,
preexec_fn=preexec_fn)
stdout, stderr = p.communicate(stdin)
stdout,stderr = p.communicate(stdin)
stdout,stderr = str(stdout), str(stderr) # Make pylint happy
except KeyboardInterrupt:
root_logger.debug('Process interrupted')
p.wait()
@@ -433,66 +332,29 @@ def run(args, stdin=None, raiseonerr=True, nolog=(), env=None,
if skip_output:
p_out.close() # pylint: disable=E1103
if timeout and p.returncode == 124:
root_logger.debug('Process did not complete before timeout')
root_logger.debug('Process finished, return code=%s', p.returncode)
# The command and its output may include passwords that we don't want
# to log. Replace those.
if skip_output or redirect_output:
output_log = None
error_log = None
else:
if six.PY3:
output_log = stdout.decode(locale.getpreferredencoding(),
errors='replace')
else:
output_log = stdout
if six.PY3:
error_log = stderr.decode(locale.getpreferredencoding(),
errors='replace')
else:
error_log = stderr
output_log = nolog_replace(output_log, nolog)
root_logger.debug('stdout=%s' % output_log)
error_log = nolog_replace(error_log, nolog)
root_logger.debug('stderr=%s' % error_log)
if capture_output:
if six.PY2:
output = stdout
else:
output = stdout.decode(encoding)
else:
output = None
if capture_error:
if six.PY2:
error_output = stderr
else:
error_output = stderr.decode(encoding)
else:
error_output = None
if capture_output and not skip_output:
stdout = nolog_replace(stdout, nolog)
stderr = nolog_replace(stderr, nolog)
root_logger.debug('stdout=%s' % stdout)
root_logger.debug('stderr=%s' % stderr)
if p.returncode != 0 and raiseonerr:
raise CalledProcessError(p.returncode, arg_string, str(output))
raise CalledProcessError(p.returncode, arg_string, stdout)
result = _RunResult(output, error_output, p.returncode)
result.raw_output = stdout
result.raw_error_output = stderr
result.output_log = output_log
result.error_log = error_log
return result
return (stdout, stderr, p.returncode)
def nolog_replace(string, nolog):
"""Replace occurences of strings given in `nolog` with XXXXXXXX"""
for value in nolog:
if not isinstance(value, six.string_types):
if not isinstance(value, basestring):
continue
quoted = urllib.parse.quote(value)
quoted = urllib2.quote(value)
shquoted = shell_quote(value)
for nolog_value in (shquoted, value, quoted):
string = string.replace(nolog_value, 'XXXXXXXX')
@@ -528,18 +390,18 @@ def backup_file(fname):
if file_exists(fname):
os.rename(fname, fname + ".orig")
def _ensure_nonempty_string(string, message):
if not isinstance(string, str) or not string:
raise ValueError(message)
# uses gpg to compress and encrypt a file
def encrypt_file(source, dest, password, workdir = None):
_ensure_nonempty_string(source, 'Missing Source File')
if type(source) is not StringType or not len(source):
raise ValueError('Missing Source File')
#stat it so that we get back an exception if it does no t exist
os.stat(source)
_ensure_nonempty_string(dest, 'Missing Destination File')
_ensure_nonempty_string(password, 'Missing Password')
if type(dest) is not StringType or not len(dest):
raise ValueError('Missing Destination File')
if type(password) is not StringType or not len(password):
raise ValueError('Missing Password')
#create a tempdir so that we can clean up with easily
tempdir = tempfile.mkdtemp('', 'ipa-', workdir)
@@ -560,12 +422,16 @@ def encrypt_file(source, dest, password, workdir = None):
def decrypt_file(source, dest, password, workdir = None):
_ensure_nonempty_string(source, 'Missing Source File')
if type(source) is not StringType or not len(source):
raise ValueError('Missing Source File')
#stat it so that we get back an exception if it does no t exist
os.stat(source)
_ensure_nonempty_string(dest, 'Missing Destination File')
_ensure_nonempty_string(password, 'Missing Password')
if type(dest) is not StringType or not len(dest):
raise ValueError('Missing Destination File')
if type(password) is not StringType or not len(password):
raise ValueError('Missing Password')
#create a tempdir so that we can clean up with easily
tempdir = tempfile.mkdtemp('', 'ipa-', workdir)
@@ -645,15 +511,14 @@ class CIDict(dict):
for key in keys():
self.__setitem__(key, new[key], seen)
seen = set()
for key, value in kwargs.items():
for key, value in kwargs.iteritems():
self.__setitem__(key, value, seen)
def __contains__(self, key):
return super(CIDict, self).__contains__(key.lower())
if six.PY2:
def has_key(self, key):
return super(CIDict, self).has_key(key.lower())
def has_key(self, key):
return super(CIDict, self).has_key(key.lower())
def get(self, key, failobj=None):
try:
@@ -662,38 +527,29 @@ class CIDict(dict):
return failobj
def __iter__(self):
return six.itervalues(self._keys)
return self._keys.itervalues()
def keys(self):
if six.PY2:
return list(self.iterkeys())
else:
return self.iterkeys()
return list(self.iterkeys())
def items(self):
if six.PY2:
return list(self.iteritems())
else:
return self.iteritems()
return list(self.iteritems())
def values(self):
if six.PY2:
return list(self.itervalues())
else:
return self.itervalues()
return list(self.itervalues())
def copy(self):
"""Returns a shallow copy of this CIDict"""
return CIDict(list(self.items()))
return CIDict(self.items())
def iteritems(self):
return ((k, self[k]) for k in six.itervalues(self._keys))
return ((k, self[k]) for k in self._keys.itervalues())
def iterkeys(self):
return six.itervalues(self._keys)
return self._keys.itervalues()
def itervalues(self):
return (v for k, v in six.iteritems(self))
return (v for k, v in self.iteritems())
def setdefault(self, key, value=None):
try:
@@ -863,57 +719,60 @@ def ipa_generate_password(characters=None,pwd_len=None):
def user_input(prompt, default = None, allow_empty = True):
if default == None:
while True:
try:
ret = input("%s: " % prompt)
if allow_empty or ret.strip():
return ret.strip()
except EOFError:
if allow_empty:
return ''
raise RuntimeError("Failed to get user input")
ret = raw_input("%s: " % prompt)
if allow_empty or ret.strip():
return ret
if isinstance(default, six.string_types):
if isinstance(default, basestring):
while True:
try:
ret = input("%s [%s]: " % (prompt, default))
if not ret and (allow_empty or default):
return default
elif ret.strip():
return ret.strip()
except EOFError:
ret = raw_input("%s [%s]: " % (prompt, default))
if not ret and (allow_empty or default):
return default
elif ret.strip():
return ret
if isinstance(default, bool):
choice = "yes" if default else "no"
if default:
choice = "yes"
else:
choice = "no"
while True:
try:
ret = input("%s [%s]: " % (prompt, choice))
ret = ret.strip()
if not ret:
return default
elif ret.lower()[0] == "y":
return True
elif ret.lower()[0] == "n":
return False
except EOFError:
ret = raw_input("%s [%s]: " % (prompt, choice))
if not ret:
return default
elif ret.lower()[0] == "y":
return True
elif ret.lower()[0] == "n":
return False
if isinstance(default, int):
while True:
try:
ret = input("%s [%s]: " % (prompt, default))
ret = ret.strip()
ret = raw_input("%s [%s]: " % (prompt, default))
if not ret:
return default
ret = int(ret)
except ValueError:
pass
except EOFError:
return default
else:
return ret
def get_gsserror(e):
"""
A GSSError exception looks differently in python 2.4 than it does
in python 2.5. Deal with it.
"""
try:
major = e[0]
minor = e[1]
except:
major = e[0][0]
minor = e[0][1]
return (major, minor)
def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None):
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type):
af, socktype, proto, canonname, sa = res
@@ -934,7 +793,7 @@ def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=No
s.recv(512)
return True
except socket.error as e:
except socket.error, e:
pass
finally:
if s:
@@ -955,14 +814,14 @@ def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=Non
try:
addr_infos = socket.getaddrinfo(host, port, family, socket_type, 0,
socket.AI_PASSIVE)
except socket.error as e:
except socket.error, e:
last_socket_error = e
continue
for res in addr_infos:
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error as e:
except socket.error, e:
last_socket_error = e
s = None
continue
@@ -1001,7 +860,7 @@ def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=Non
# Timeout is expectable as it was requested by caller, raise
# the exception back to him
raise
except socket.error as e:
except socket.error, e:
last_socket_error = e
s.close()
s = None
@@ -1013,6 +872,16 @@ def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=Non
if s is None and last_socket_error is not None:
raise last_socket_error # pylint: disable=E0702
def is_host_resolvable(fqdn):
for rdtype in (rdatatype.A, rdatatype.AAAA):
try:
resolver.query(fqdn, rdtype)
except DNSException:
continue
else:
return True
return False
def host_exists(host):
"""
@@ -1027,112 +896,45 @@ def host_exists(host):
else:
return True
def reverse_record_exists(ip_address):
def get_ipa_basedn(conn):
"""
Checks if IP address have some reverse record somewhere.
Does not care where it points.
Get base DN of IPA suffix in given LDAP server.
Returns True/False
None is returned if the suffix is not found
:param conn: Bound LDAPClient that will be used for searching
"""
reverse = reversename.from_address(str(ip_address))
try:
resolver.query(reverse, "PTR")
except DNSException:
# really don't care what exception, PTR is simply unresolvable
return False
return True
entry = conn.get_entry(
DN(), attrs_list=['defaultnamingcontext', 'namingcontexts'])
# FIXME: import ipalib here to prevent import loops
from ipalib import errors
def check_zone_overlap(zone, raise_on_error=True):
root_logger.info("Checking DNS domain %s, please wait ..." % zone)
if not isinstance(zone, DNSName):
zone = DNSName(zone).make_absolute()
# automatic empty zones always exist so checking them is pointless,
# do not report them to avoid meaningless error messages
if is_auto_empty_zone(zone):
return
try:
containing_zone = resolver.zone_for_name(zone)
except DNSException as e:
msg = ("DNS check for domain %s failed: %s." % (zone, e))
if raise_on_error:
raise ValueError(msg)
else:
root_logger.warning(msg)
return
if containing_zone == zone:
contexts = entry['namingcontexts']
if 'defaultnamingcontext' in entry:
# If there is a defaultNamingContext examine that one first
default = entry.single_value['defaultnamingcontext']
if default in contexts:
contexts.remove(default)
contexts.insert(0, default)
for context in contexts:
root_logger.debug("Check if naming context '%s' is for IPA" % context)
try:
ns = [ans.to_text() for ans in resolver.query(zone, 'NS')]
except DNSException as e:
root_logger.debug("Failed to resolve nameserver(s) for domain"
" {0}: {1}".format(zone, e))
ns = []
msg = u"DNS zone {0} already exists in DNS".format(zone)
if ns:
msg += u" and is handled by server(s): {0}".format(', '.join(ns))
raise ValueError(msg)
def is_auto_empty_zone(zone):
assert isinstance(zone, DNSName)
automatic_empty_zones = [DNSName(aez).make_absolute() for aez in [
# RFC 1918
"10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA",
"18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA",
"21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA",
"24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA",
"27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA",
"30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA",
# RFC 6598
"64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA",
"67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA",
"70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA",
"73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA",
"76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA",
"79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA",
"82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA",
"85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA",
"88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA",
"91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA",
"94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA",
"97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA",
"100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA",
"102.100.IN-ADDR.ARPA", "103.100.IN-ADDR.ARPA",
"104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA",
"106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA",
"108.100.IN-ADDR.ARPA", "109.100.IN-ADDR.ARPA",
"110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA",
"112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA",
"114.100.IN-ADDR.ARPA", "115.100.IN-ADDR.ARPA",
"116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA",
"118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA",
"120.100.IN-ADDR.ARPA", "121.100.IN-ADDR.ARPA",
"122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA",
"124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA",
"126.100.IN-ADDR.ARPA", "127.100.IN-ADDR.ARPA",
# RFC 5735 and RFC 5737
"0.IN-ADDR.ARPA", "127.IN-ADDR.ARPA", "254.169.IN-ADDR.ARPA",
"2.0.192.IN-ADDR.ARPA", "100.51.198.IN-ADDR.ARPA",
"113.0.203.IN-ADDR.ARPA", "255.255.255.255.IN-ADDR.ARPA",
# Local IPv6 Unicast Addresses
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA",
# LOCALLY ASSIGNED LOCAL ADDRESS SCOPE
"D.F.IP6.ARPA", "8.E.F.IP6.ARPA", "9.E.F.IP6.ARPA", "A.E.F.IP6.ARPA",
"B.E.F.IP6.ARPA",
# Example Prefix, RFC 3849.
"8.B.D.0.1.0.0.2.IP6.ARPA",
# RFC 7534
"EMPTY.AS112.ARPA",
]]
return zone in automatic_empty_zones
[entry] = conn.get_entries(
DN(context), conn.SCOPE_BASE, "(info=IPA*)")
except errors.NotFound:
root_logger.debug("LDAP server did not return info attribute to "
"check for IPA version")
continue
info = entry.single_value['info'].lower()
if info != IPA_BASEDN_INFO:
root_logger.debug("Detected IPA server version (%s) did not match the client (%s)" \
% (info, IPA_BASEDN_INFO))
continue
root_logger.debug("Naming context '%s' is a valid IPA context" % context)
return DN(context)
return None
def config_replace_variables(filepath, replacevars=dict(), appendvars=dict()):
"""
@@ -1348,7 +1150,7 @@ def wait_for_open_ports(host, ports, timeout=0):
if port_open:
break
if timeout and time.time() > op_timeout: # timeout exceeded
raise socket.timeout("Timeout exceeded")
raise socket.timeout()
time.sleep(1)
def wait_for_open_socket(socket_name, timeout=0):
@@ -1365,7 +1167,7 @@ def wait_for_open_socket(socket_name, timeout=0):
s.connect(socket_name)
s.close()
break
except socket.error as e:
except socket.error, e:
if e.errno in (2,111): # 111: Connection refused, 2: File not found
if timeout and time.time() > op_timeout: # timeout exceeded
raise e
@@ -1373,76 +1175,27 @@ def wait_for_open_socket(socket_name, timeout=0):
else:
raise e
def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
def kinit_hostprincipal(keytab, ccachedir, principal):
"""
Given a ccache_path, keytab file and a principal kinit as that user.
Given a ccache directory and a principal kinit as that user.
The optional parameter 'attempts' specifies how many times the credential
initialization should be attempted in case of non-responsive KDC.
This blindly overwrites the current CCNAME so if you need to save
it do so before calling this function.
Thus far this is used to kinit as the local host.
"""
errors_to_retry = {KRB5KDC_ERR_SVC_UNAVAILABLE,
KRB5_KDC_UNREACH}
root_logger.debug("Initializing principal %s using keytab %s"
% (principal, keytab))
root_logger.debug("using ccache %s" % ccache_name)
for attempt in range(1, attempts + 1):
old_config = os.environ.get('KRB5_CONFIG')
if config is not None:
os.environ['KRB5_CONFIG'] = config
else:
os.environ.pop('KRB5_CONFIG', None)
try:
name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
store = {'ccache': ccache_name,
'client_keytab': keytab}
cred = gssapi.Credentials(name=name, store=store, usage='initiate')
root_logger.debug("Attempt %d/%d: success"
% (attempt, attempts))
return cred
except gssapi.exceptions.GSSError as e:
if e.min_code not in errors_to_retry: # pylint: disable=no-member
raise
root_logger.debug("Attempt %d/%d: failed: %s"
% (attempt, attempts, e))
if attempt == attempts:
root_logger.debug("Maximum number of attempts (%d) reached"
% attempts)
raise
root_logger.debug("Waiting 5 seconds before next retry")
time.sleep(5)
finally:
if old_config is not None:
os.environ['KRB5_CONFIG'] = old_config
else:
os.environ.pop('KRB5_CONFIG', None)
def kinit_password(principal, password, ccache_name, config=None,
armor_ccache_name=None):
"""
perform interactive kinit as principal using password. If using FAST for
web-based authentication, use armor_ccache_path to specify http service
ccache.
"""
root_logger.debug("Initializing principal %s using password" % principal)
args = [paths.KINIT, principal, '-c', ccache_name]
if armor_ccache_name is not None:
root_logger.debug("Using armor ccache %s for FAST webauth"
% armor_ccache_name)
args.extend(['-T', armor_ccache_name])
env = {'LC_ALL': 'C'}
if config is not None:
env['KRB5_CONFIG'] = config
# this workaround enables us to capture stderr and put it
# into the raised exception in case of unsuccessful authentication
result = run(args, stdin=password, env=env, raiseonerr=False,
capture_error=True)
if result.returncode:
raise RuntimeError(result.error_output)
try:
ccache_file = 'FILE:%s/ccache' % ccachedir
krbcontext = krbV.default_context()
ktab = krbV.Keytab(name=keytab, context=krbcontext)
princ = krbV.Principal(name=principal, context=krbcontext)
os.environ['KRB5CCNAME'] = ccache_file
ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=princ)
ccache.init(princ)
ccache.init_creds_keytab(keytab=ktab, principal=princ)
return ccache_file
except krbV.Krb5Error, e:
raise StandardError('Error initializing principal %s in %s: %s' % (principal, keytab, str(e)))
def dn_attribute_property(private_name):
'''
@@ -1483,77 +1236,5 @@ def restore_hostname(statestore):
if old_hostname is not None and old_hostname != system_hostname:
try:
run([paths.BIN_HOSTNAME, old_hostname])
except CalledProcessError as e:
print("Failed to set this machine hostname back to %s: %s" % (old_hostname, str(e)), file=sys.stderr)
def posixify(string):
"""
Convert a string to a more strict alpha-numeric representation.
- Alpha-numeric, underscore, dot and dash characters are accepted
- Space is converted to underscore
- Other characters are omitted
- Leading dash is stripped
Note: This mapping is not one-to-one and may map different input to the
same result. When using posixify, make sure the you do not map two different
entities to one unintentionally.
"""
def valid_char(char):
return char.isalnum() or char in ('_', '.', '-')
# First replace space characters
replaced = string.replace(' ','_')
omitted = ''.join(filter(valid_char, replaced))
# Leading dash is not allowed
return omitted.lstrip('-')
@contextmanager
def private_ccache(path=None):
if path is None:
dir_path = tempfile.mkdtemp(prefix='krbcc')
path = os.path.join(dir_path, 'ccache')
else:
dir_path = None
original_value = os.environ.get('KRB5CCNAME', None)
os.environ['KRB5CCNAME'] = path
try:
yield
finally:
if original_value is not None:
os.environ['KRB5CCNAME'] = original_value
else:
os.environ.pop('KRB5CCNAME', None)
if os.path.exists(path):
os.remove(path)
if dir_path is not None:
try:
os.rmdir(dir_path)
except OSError:
pass
if six.PY2:
def fsdecode(value):
"""
Decode argument using the file system encoding, as returned by
`sys.getfilesystemencoding()`.
"""
if isinstance(value, six.binary_type):
return value.decode(sys.getfilesystemencoding())
elif isinstance(value, six.text_type):
return value
else:
raise TypeError("expect {0} or {1}, not {2}".format(
six.binary_type.__name__,
six.text_type.__name__,
type(value).__name__))
else:
fsdecode = os.fsdecode #pylint: disable=no-member
except CalledProcessError, e:
print >>sys.stderr, "Failed to set this machine hostname back to %s: %s" % (old_hostname, str(e))