On Wed, 2014-06-25 at 13:18 +0300, Alexander Bokovoy wrote:
> On Tue, 24 Jun 2014, Nathaniel McCallum wrote:
> >On Tue, 2014-06-03 at 09:18 -0400, Nathaniel McCallum wrote:
> >> On Tue, 2014-06-03 at 10:27 +0200, Petr Vobornik wrote:
> >> > On 3.6.2014 05:08, Nathaniel McCallum wrote:
> >> > > This command calls the token sync HTTP POST call in the server 
> >> > > providing
> >> > > the CLI interface to synchronization.
> >> > >
> >> > > https://fedorahosted.org/freeipa/ticket/4260
> >> > >
> >> > > This patch depends on my patch #0055.
> >> > >
> >> >
> >> > Build fails on validation. You forgot to update API.txt and also the
> >> > command misses __doc__.
> >> >
> >> > (not a proper review)
> >>
> >> Thanks, fixed.
> >
> >Attached is a new revision which is rebased on master.
> >
> >In addition it:
> >
> >1. Moves user to a parameter and moves token to an argument. Doing it
> >this way both mirrors the existing otptoken APIs and sets us up for
> >future Kerberos based syncing where the username/password will be
> >optional.
> >
> >2. Converts the token ID to a DN.
> ACK.
> 
> Please do not commit this patch yet, we are not done with its
> dependencies.

As discussed off list, we also needed to verify the certificate so that
passwords were not sent in the clear to a MITM. This has now been
implemented. VERSION is bumped and ./makeapi was run. This patch is also
rebased on top of my patch 0058 (which is already ACK'd), so 0058 needs
to be merged before this patch (0056).

Nathaniel
From c06a4146d10759937116eb6ffe7201636febb1ab Mon Sep 17 00:00:00 2001
From: Nathaniel McCallum <npmccal...@redhat.com>
Date: Mon, 2 Jun 2014 23:00:52 -0400
Subject: [PATCH] Add otptoken-sync command

This command calls the token sync HTTP POST call in the server providing
the CLI interface to synchronization.

https://fedorahosted.org/freeipa/ticket/4260
---
 API.txt                    |   9 ++++
 VERSION                    |   4 +-
 ipalib/plugins/otptoken.py | 102 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 112 insertions(+), 3 deletions(-)

diff --git a/API.txt b/API.txt
index 3c3b6447fec3c313c3038390ac7317533c530d8b..0924402764ca29711d8a9094c1e4f7dec461ab3c 100644
--- a/API.txt
+++ b/API.txt
@@ -2420,6 +2420,15 @@ option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: otptoken_sync
+args: 1,5,1
+arg: Str('token?')
+option: Password('first_code', confirm=False)
+option: Password('password', confirm=False)
+option: Password('second_code', confirm=False)
+option: Str('user')
+option: Str('version?', exclude='webui')
+output: Output('result', None, None)
 command: passwd
 args: 3,1,3
 arg: Str('principal', autofill=True, cli_name='user', primary_key=True)
diff --git a/VERSION b/VERSION
index f0c2db55658f3ab4cfafffc5735aa77b52bc9cc8..1d2e81688b9934baf1790c390452d733b9bed2e9 100644
--- a/VERSION
+++ b/VERSION
@@ -89,5 +89,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=94
-# Last change: npmaccallum - otptoken-add-yubikey
+IPA_API_VERSION_MINOR=95
+# Last change: npmaccallum - otptoken-sync
diff --git a/ipalib/plugins/otptoken.py b/ipalib/plugins/otptoken.py
index 7962af0035fb9ac00e68c4b642bb62aa82d498c2..46ad77a2c81842cad9085651b794fd7959d783f0 100644
--- a/ipalib/plugins/otptoken.py
+++ b/ipalib/plugins/otptoken.py
@@ -19,15 +19,22 @@
 
 from ipalib.plugins.baseldap import DN, LDAPObject, LDAPAddMember, LDAPRemoveMember
 from ipalib.plugins.baseldap import LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, LDAPRetrieve
-from ipalib import api, Int, Str, Bool, Flag, Bytes, IntEnum, StrEnum, _, ngettext
+from ipalib import api, Int, Str, Bool, Flag, Bytes, IntEnum, StrEnum, Password, _, ngettext
 from ipalib.plugable import Registry
 from ipalib.errors import PasswordMismatch, ConversionError, LastMemberError, NotFound
 from ipalib.request import context
+from ipalib.frontend import Local
+
+from backports.ssl_match_hostname import match_hostname
 import base64
 import uuid
 import urllib
+import urllib2
+import httplib
+import urlparse
 import qrcode
 import os
+import ssl
 
 __doc__ = _("""
 OTP Tokens
@@ -383,3 +390,96 @@ class otptoken_remove_managedby(LDAPRemoveMember):
     __doc__ = _('Remove hosts that can manage this host.')
 
     member_attributes = ['managedby']
+
+class HTTPSConnection(httplib.HTTPConnection):
+    "Generates an SSL HTTP connection that performs hostname validation."
+
+    ssl_kwargs = ssl.wrap_socket.func_code.co_varnames[1:ssl.wrap_socket.func_code.co_argcount]
+    default_port = httplib.HTTPS_PORT
+
+    def __init__(self, host, **kwargs):
+        # Strip out arguments we want to pass to ssl.wrap_socket()
+        self.__kwargs = {k: v for k, v in kwargs.items() if k in self.ssl_kwargs}
+        for k in self.__kwargs:
+            del kwargs[k]
+
+        # Can't use super() because the parent is an old-style class.
+        httplib.HTTPConnection.__init__(self, host, **kwargs)
+
+    def connect(self):
+        # Create the raw socket and wrap it in ssl.
+        httplib.HTTPConnection.connect(self)
+        self.sock = ssl.wrap_socket(self.sock, **self.__kwargs)
+
+        # Verify the remote hostname.
+        match_hostname(self.sock.getpeercert(), self.host.split(':', 1)[0])
+
+class HTTPSHandler(urllib2.HTTPSHandler):
+    "Opens SSL HTTPS connections that perform hostname validation."
+
+    def __init__(self, **kwargs):
+        self.__kwargs = kwargs
+
+        # Can't use super() because the parent is an old-style class.
+        urllib2.HTTPSHandler.__init__(self)
+
+    def __inner(self, host, **kwargs):
+        tmp = self.__kwargs.copy()
+        tmp.update(kwargs)
+        return HTTPSConnection(host, **tmp)
+
+    def https_open(self, req):
+        return self.do_open(self.__inner, req)
+
+@register()
+class otptoken_sync(Local):
+    __doc__ = _('Synchronize an OTP token.')
+
+    header = 'X-IPA-TokenSync-Result'
+
+    takes_options = (
+        Str('user', label=_('User ID')),
+        Password('password', label=_('Password'), confirm=False),
+        Password('first_code', label=_('First Code'), confirm=False),
+        Password('second_code', label=_('Second Code'), confirm=False),
+    )
+
+    takes_args = (
+        Str('token?', label=_('Token ID')),
+    )
+
+    def forward(self, *args, **kwargs):
+        status = {'result': {self.header: 'unknown'}}
+
+        # Get the sync URI.
+        segments = list(urlparse.urlparse(self.api.env.xmlrpc_uri))
+        assert segments[0] == 'https' # Ensure encryption.
+        segments[2] = segments[2].replace('/xml', '/session/sync_token')
+        sync_uri = urlparse.urlunparse(segments)
+
+        # Prepare the query.
+        query = {k: v for k, v in kwargs.items()
+                    if k in {x.name for x in self.takes_options}}
+        if args and args[0] is not None:
+            obj = self.api.Object.otptoken
+            query['token'] = DN((obj.primary_key.name, args[0]),
+                                obj.container_dn, self.api.env.basedn)
+        query = urllib.urlencode(query)
+
+        # Sync the token.
+        handler = HTTPSHandler(ca_certs=os.path.join(self.api.env.confdir, 'ca.crt'),
+                               cert_reqs=ssl.CERT_REQUIRED,
+                               ssl_version=ssl.PROTOCOL_TLSv1)
+        rsp = urllib2.build_opener(handler).open(sync_uri, query)
+        if rsp.getcode() == 200:
+            status['result'][self.header] = rsp.info().get(self.header, 'unknown')
+        rsp.close()
+
+        return status
+
+    def output_for_cli(self, textui, result, *keys, **options):
+        textui.print_plain({
+            'ok': 'Token synchronized.',
+            'error': 'Error contacting server!',
+            'invalid-credentials': 'Invalid Credentials!',
+        }.get(result['result'][self.header], 'Unknown Error!'))
-- 
2.0.0

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to