root/branches/release-33/lib/MT/Auth/TypeKey.pm @ 1720

Revision 1720, 8.0 kB (checked in by bchoate, 20 months ago)

Fixed usage of COMMENTER_COOKIE constant. BugId:69872

  • 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->{query};
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    my $blog = MT::Blog->load($q->param('blog_id') || $entry->blog_id);
27
28    my $ts = $q->param('ts') || "";
29    my $email = $q->param('email') || "";
30    my $name = $q->param('name') || "";
31    my $nick = $q->param('nick') || "";
32    my $cmntr;
33    my $session;
34    if ($sig_str) {
35        if (!$class->_validate_signature($app, $sig_str, 
36                                       token => $blog->effective_remote_auth_token,
37                                       email => decode_url($email),
38                                       name => decode_url($name),
39                                       nick => decode_url($nick),
40                                       ts => $ts))
41        {
42            # Signature didn't match, or timestamp was out of date.
43            # This implies tampering, not a user mistake.
44            $app->error($app->translate("The sign-in validation failed."));
45            return 0;
46        }
47
48        if ($blog->require_typekey_emails && !is_valid_email($email)) {
49            $q->param('email', '');  # blank out email address since it's invalid
50            $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."));
51            return 0;
52        }
53
54        my $url = $app->{cfg}->IdentityURL;
55        $url .= "/" unless $url =~ m|/$|;
56        $url .= $name;
57
58        # Signature was valid, so create a session, etc.
59        my $enc = $app->{cfg}->PublishCharset || '';
60        my $nick_escaped = escape_unicode($nick);
61        $nick = encode_text($nick, 'utf-8', undef);
62        $session = $app->_make_commenter_session($sig_str, $email,
63                                                 $name, $nick_escaped, undef, $url);
64        unless ($session) {
65            $app->error($app->errstr() || $app->translate("Couldn't save the session"));
66            return 0;
67        }
68        $cmntr = $app->_make_commenter(
69            email => $email,
70            nickname => $nick,
71            name => $name,
72            url => $url,
73            auth_type => $auth_type,
74        );
75    } else {
76        # If there's no signature, then we trust the cookie.
77        my %cookies = $app->cookies();
78        my $cookie_name = MT::App::COMMENTER_COOKIE_NAME();
79        if ($cookies{$cookie_name}
80            && ($session = $cookies{$cookie_name}->value())) 
81        {
82            require MT::Session;
83            require MT::Author;
84            my $sess = MT::Session->load({id => $session});
85            $cmntr = MT::Author->load({name => $sess->name,
86                                       type => MT::Author::COMMENTER(),
87                                       auth_type => $auth_type});
88            if ($blog->require_typekey_emails
89                && !is_valid_email($cmntr->email))
90            {
91                $app->error($app->translate("This blog requires commenters to provide an email address"));
92                return 0;
93            }
94        }
95    }
96    if ($q->param('sig') && !$cmntr) {
97        return 0;
98    }
99    return $cmntr;
100}
101
102my $SIG_WINDOW = 60 * 10;  # ten minute handoff between TP and MT
103
104sub _validate_signature {
105    my $class = shift;
106    my ($app, $sig_str, %params) = @_;
107
108    # the DSA sig parameter is composed of the two pieces of the
109    # real DSA sig, packed in Base64, separated by a colon.
110
111#    my ($r, $s) = split /:/, decode_url($sig_str);
112    my ($r, $s) = split /:/, $sig_str;
113    $r =~ s/ /+/g;
114    $s =~ s/ /+/g;
115
116    $params{email} =~ s/ /+/g;
117    require MIME::Base64;
118    import MIME::Base64 qw(decode_base64);
119    use MT::Util qw(bin2dec);
120    $r = bin2dec(decode_base64($r));
121    $s = bin2dec(decode_base64($s));
122
123    my $sig = {'s' => $s,
124               'r' => $r};
125    my $timer = time;
126    require MT::Util; import MT::Util ('dsa_verify');
127    my $msg;
128    if ($app->{cfg}->TypeKeyVersion eq '1.1') {
129        $msg = ($params{email} . "::" . $params{name} . "::" .
130                $params{nick} . "::" . $params{ts} . "::" . $params{token});
131    } else {
132        $msg = ($params{email} . "::" . $params{name} . "::" .
133                $params{nick} . "::" . $params{ts});
134    }
135
136    my $dsa_key;
137    require MT::Session;
138    $dsa_key = eval { MT::Session->load({id => 'KY',
139                                         kind => 'KY'}); } || undef; 
140    if ($dsa_key) {
141        if ($dsa_key->start() < time - 24 * 60 * 60) {
142            $dsa_key = undef;
143        }
144        $dsa_key = $dsa_key->data if $dsa_key;
145    }
146    if ( ! $dsa_key ) {
147        # Load the override key
148        $dsa_key = $app->{cfg}->get('SignOnPublicKey');
149    }
150    # Load the DSA key from the RegKeyURL
151    my $key_location = $app->{cfg}->RegKeyURL;
152    if (!$dsa_key && $key_location) {
153        my $ua = $app->new_ua;
154        unless ($ua) {
155            my $err = $app->translate("Couldn't get public key from url provided");
156            $app->log({
157                message => $err,
158                level => MT::Log::ERROR(),
159                class => 'system',
160                category => 'commenter_authentication',
161            });
162            return $app->error($err);
163        }
164        my $req = new HTTP::Request(GET => $key_location);
165        my $resp = $ua->request($req);
166        unless ($resp->is_success()) {
167            my $err = $app->translate("Couldn't get public key from url provided");
168            $app->log({
169                message => $err,
170                level => MT::Log::ERROR(),
171                class => 'system',
172                category => 'commenter_authentication',
173            });
174            return $app->error($err);
175        }
176        # TBD: Check the content-type
177        $dsa_key = $resp->content();
178
179        require MT::Session;
180        my $key_cache = new MT::Session();
181
182        my @chs = ('a' .. 'z', '0' .. '9');
183        $key_cache->set_values({id => 'KY',
184                                data => $dsa_key,
185                                kind => 'KY',
186                                start => time});
187        $key_cache->save();
188    }
189    if (!$dsa_key) {
190        $app->log({
191            message => $app->translate("No public key could be found to validate registration."),
192            level => MT::Log::ERROR(),
193            class => 'system',
194            category => 'commenter_authentication',
195        });
196        return $app->error($app->translate(
197                    "No public key could be found to validate registration."));
198    }
199    my ($p) = $dsa_key =~ /p=([0-9a-f]*)/i;
200    my ($q) = $dsa_key =~ /q=([0-9a-f]*)/i;
201    my ($g) = $dsa_key =~ /g=([0-9a-f]*)/i;
202    my ($pub_key) = $dsa_key =~ /pub_key=([0-9a-f]*)/i;
203    $dsa_key = {p=>$p, q=>$q, g=>$g, pub_key=>$pub_key};
204    my $valid = dsa_verify(Key => $dsa_key,
205                           Signature => $sig,
206                           Message => $msg);
207    $timer = time - $timer;
208
209    $app->log({
210        message => $app->translate("TypeKey signature verif'n returned [_1] in [_2] seconds verifying [_3] with [_4]",
211            ($valid ? "VALID" : "INVALID"), $timer, $msg, $sig_str),
212        class => 'system',
213        level => MT::Log::WARNING(),
214    }) unless $valid;
215
216    $app->log({
217        message => $app->translate("The TypeKey signature is out of date ([_1] seconds old). Ensure that your server's clock is correct", ($params{ts} - time)),
218        class => 'system',
219        level => MT::Log::WARNING(),
220    }) unless ($params{ts} + $SIG_WINDOW >= time);
221
222    return ($valid && $params{ts} + $SIG_WINDOW >= time);
223}
224
2251;
Note: See TracBrowser for help on using the browser.