root/branches/release-38/lib/MT/Auth/TypeKey.pm @ 2365

Revision 2365, 6.7 kB (checked in by bchoate, 19 months ago)

Revised commenter sessions to include user id (as we do with authors) so we can load by id rather than by name. BugId:79253

  • Property svn:keywords set to Author Date Id Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::Auth::TypeKey;
8use strict;
9
10use MT::Util qw( decode_url is_valid_email escape_unicode );
11use MT::I18N qw( encode_text );
12
13sub handle_sign_in {
14    my $class = shift;
15    my ($app, $auth_type) = @_;
16    my $q = $app->param;
17
18    my $sig_str = $q->param('sig');
19    unless ($sig_str) {
20        $app->error($app->translate('Sign in requires a secure signature.'));
21        return 0;
22    }
23
24    my $entry_id = $q->param('entry_id');
25    my $entry = MT::Entry->load($entry_id)
26        or return 0;
27    my $blog = MT::Blog->load($q->param('blog_id') || $entry->blog_id)
28        or return 0;
29
30    my $ts = $q->param('ts') || "";
31    my $email = $q->param('email') || "";
32    my $name = $q->param('name') || "";
33    my $nick = $q->param('nick') || "";
34    my $cmntr;
35    my $session;
36
37    if (!$class->_validate_signature($app, $sig_str, 
38                                   token => $blog->effective_remote_auth_token,
39                                   email => decode_url($email),
40                                   name => decode_url($name),
41                                   nick => decode_url($nick),
42                                   ts => $ts))
43    {
44        # Signature didn't match, or timestamp was out of date.
45        # This implies tampering, not a user mistake.
46        $app->error($app->translate("The sign-in validation failed."));
47        return 0;
48    }
49
50    if ($blog->require_typekey_emails && !is_valid_email($email)) {
51        $q->param('email', '');  # blank out email address since it's invalid
52        $app->error($app->translate("This weblog requires commenters to pass an email address. If you'd like to do so you may log in again, and give the authentication service permission to pass your email address."));
53        return 0;
54    }
55
56    my $url = $app->config('IdentityURL');
57    $url .= "/" unless $url =~ m|/$|;
58    $url .= $name;
59
60    # Signature was valid, so create a session, etc.
61    $cmntr = $app->_make_commenter(
62        email => $email,
63        nickname => $nick,
64        name => $name,
65        url => $url,
66        auth_type => $auth_type,
67    );
68    $session = $app->make_commenter_session($cmntr);
69    unless ($session) {
70        $app->error($app->errstr() || $app->translate("Couldn't save the session"));
71        return 0;
72    }
73    if ($q->param('sig') && !$cmntr) {
74        return 0;
75    }
76    return $cmntr;
77}
78
79my $SIG_WINDOW = 60 * 10;  # ten minute handoff between TP and MT
80
81sub _validate_signature {
82    my $class = shift;
83    my ($app, $sig_str, %params) = @_;
84
85    # the DSA sig parameter is composed of the two pieces of the
86    # real DSA sig, packed in Base64, separated by a colon.
87    my ($r, $s) = split /:/, $sig_str;
88    $r =~ s/ /+/g;
89    $s =~ s/ /+/g;
90
91    $params{email} =~ s/ /+/g;
92    require MIME::Base64;
93    import MIME::Base64 qw(decode_base64);
94    use MT::Util qw(bin2dec);
95    $r = bin2dec(decode_base64($r));
96    $s = bin2dec(decode_base64($s));
97
98    my $sig = {'s' => $s,
99               'r' => $r};
100    my $timer = time;
101    require MT::Util; import MT::Util ('dsa_verify');
102    my $msg;
103    if ($app->config('TypeKeyVersion') eq '1.1') {
104        $msg = ($params{email} . "::" . $params{name} . "::" .
105                $params{nick} . "::" . $params{ts} . "::" . $params{token});
106    } else {
107        $msg = ($params{email} . "::" . $params{name} . "::" .
108                $params{nick} . "::" . $params{ts});
109    }
110
111    my $dsa_key;
112    require MT::Session;
113    $dsa_key = eval { MT::Session->load({id => 'KY',
114                                         kind => 'KY'}); } || undef; 
115    if ($dsa_key) {
116        if ($dsa_key->start() < time - 24 * 60 * 60) {
117            $dsa_key = undef;
118        }
119        $dsa_key = $dsa_key->data if $dsa_key;
120    }
121    if ( ! $dsa_key ) {
122        # Load the override key
123        $dsa_key = $app->config->get('SignOnPublicKey');
124    }
125    # Load the DSA key from the RegKeyURL
126    my $key_location = $app->config('RegKeyURL');
127    if (!$dsa_key && $key_location) {
128        my $ua = $app->new_ua;
129        unless ($ua) {
130            my $err = $app->translate("Couldn't get public key from url provided");
131            $app->log({
132                message => $err,
133                level => MT::Log::ERROR(),
134                class => 'system',
135                category => 'commenter_authentication',
136            });
137            return $app->error($err);
138        }
139        my $req = new HTTP::Request(GET => $key_location);
140        my $resp = $ua->request($req);
141        unless ($resp->is_success()) {
142            my $err = $app->translate("Couldn't get public key from url provided");
143            $app->log({
144                message => $err,
145                level => MT::Log::ERROR(),
146                class => 'system',
147                category => 'commenter_authentication',
148            });
149            return $app->error($err);
150        }
151        # TBD: Check the content-type
152        $dsa_key = $resp->content();
153
154        require MT::Session;
155        my $key_cache = new MT::Session();
156
157        my @chs = ('a' .. 'z', '0' .. '9');
158        $key_cache->set_values({id => 'KY',
159                                data => $dsa_key,
160                                kind => 'KY',
161                                start => time});
162        $key_cache->save();
163    }
164    if (!$dsa_key) {
165        $app->log({
166            message => $app->translate("No public key could be found to validate registration."),
167            level => MT::Log::ERROR(),
168            class => 'system',
169            category => 'commenter_authentication',
170        });
171        return $app->error($app->translate(
172                    "No public key could be found to validate registration."));
173    }
174    my ($p) = $dsa_key =~ /p=([0-9a-f]*)/i;
175    my ($q) = $dsa_key =~ /q=([0-9a-f]*)/i;
176    my ($g) = $dsa_key =~ /g=([0-9a-f]*)/i;
177    my ($pub_key) = $dsa_key =~ /pub_key=([0-9a-f]*)/i;
178    $dsa_key = {p=>$p, q=>$q, g=>$g, pub_key=>$pub_key};
179    my $valid = dsa_verify(Key => $dsa_key,
180                           Signature => $sig,
181                           Message => $msg);
182    $timer = time - $timer;
183
184    $app->log({
185        message => $app->translate("TypeKey signature verif'n returned [_1] in [_2] seconds verifying [_3] with [_4]",
186            ($valid ? "VALID" : "INVALID"), $timer, $msg, $sig_str),
187        class => 'system',
188        level => MT::Log::WARNING(),
189    }) unless $valid;
190
191    $app->log({
192        message => $app->translate("The TypeKey signature is out of date ([_1] seconds old). Ensure that your server's clock is correct", ($params{ts} - time)),
193        class => 'system',
194        level => MT::Log::WARNING(),
195    }) unless ($params{ts} + $SIG_WINDOW >= time);
196
197    return ($valid && $params{ts} + $SIG_WINDOW >= time);
198}
199
2001;
Note: See TracBrowser for help on using the browser.