root/branches/release-26/lib/MT/Auth/TypeKey.pm @ 1174

Revision 1174, 8.0 kB (checked in by bchoate, 23 months ago)

Updated copyright year for source.

  • 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        if ($cookies{$app->COMMENTER_COOKIE_NAME()}
79            && ($session = $cookies{$app->COMMENTER_COOKIE_NAME()}->value())) 
80        {
81            require MT::Session;
82            require MT::Author;
83            my $sess = MT::Session->load({id => $session});
84            $cmntr = MT::Author->load({name => $sess->name,
85                                       type => MT::Author::COMMENTER(),
86                                       auth_type => $auth_type});
87            if ($blog->require_typekey_emails
88                && !is_valid_email($cmntr->email))
89            {
90                $app->error($app->translate("This blog requires commenters to provide an email address"));
91                return 0;
92            }
93        }
94    }
95    if ($q->param('sig') && !$cmntr) {
96        return 0;
97    }
98    return $cmntr;
99}
100
101my $SIG_WINDOW = 60 * 10;  # ten minute handoff between TP and MT
102
103sub _validate_signature {
104    my $class = shift;
105    my ($app, $sig_str, %params) = @_;
106
107    # the DSA sig parameter is composed of the two pieces of the
108    # real DSA sig, packed in Base64, separated by a colon.
109
110#    my ($r, $s) = split /:/, decode_url($sig_str);
111    my ($r, $s) = split /:/, $sig_str;
112    $r =~ s/ /+/g;
113    $s =~ s/ /+/g;
114
115    $params{email} =~ s/ /+/g;
116    require MIME::Base64;
117    import MIME::Base64 qw(decode_base64);
118    use MT::Util qw(bin2dec);
119    $r = bin2dec(decode_base64($r));
120    $s = bin2dec(decode_base64($s));
121
122    my $sig = {'s' => $s,
123               'r' => $r};
124    my $timer = time;
125    require MT::Util; import MT::Util ('dsa_verify');
126    my $msg;
127    if ($app->{cfg}->TypeKeyVersion eq '1.1') {
128        $msg = ($params{email} . "::" . $params{name} . "::" .
129                $params{nick} . "::" . $params{ts} . "::" . $params{token});
130    } else {
131        $msg = ($params{email} . "::" . $params{name} . "::" .
132                $params{nick} . "::" . $params{ts});
133    }
134
135    my $dsa_key;
136    require MT::Session;
137    $dsa_key = eval { MT::Session->load({id => 'KY',
138                                         kind => 'KY'}); } || undef; 
139    if ($dsa_key) {
140        if ($dsa_key->start() < time - 24 * 60 * 60) {
141            $dsa_key = undef;
142        }
143        $dsa_key = $dsa_key->data if $dsa_key;
144    }
145    if ( ! $dsa_key ) {
146        # Load the override key
147        $dsa_key = $app->{cfg}->get('SignOnPublicKey');
148    }
149    # Load the DSA key from the RegKeyURL
150    my $key_location = $app->{cfg}->RegKeyURL;
151    if (!$dsa_key && $key_location) {
152        my $ua = $app->new_ua;
153        unless ($ua) {
154            my $err = $app->translate("Couldn't get public key from url provided");
155            $app->log({
156                message => $err,
157                level => MT::Log::ERROR(),
158                class => 'system',
159                category => 'commenter_authentication',
160            });
161            return $app->error($err);
162        }
163        my $req = new HTTP::Request(GET => $key_location);
164        my $resp = $ua->request($req);
165        unless ($resp->is_success()) {
166            my $err = $app->translate("Couldn't get public key from url provided");
167            $app->log({
168                message => $err,
169                level => MT::Log::ERROR(),
170                class => 'system',
171                category => 'commenter_authentication',
172            });
173            return $app->error($err);
174        }
175        # TBD: Check the content-type
176        $dsa_key = $resp->content();
177
178        require MT::Session;
179        my $key_cache = new MT::Session();
180
181        my @chs = ('a' .. 'z', '0' .. '9');
182        $key_cache->set_values({id => 'KY',
183                                data => $dsa_key,
184                                kind => 'KY',
185                                start => time});
186        $key_cache->save();
187    }
188    if (!$dsa_key) {
189        $app->log({
190            message => $app->translate("No public key could be found to validate registration."),
191            level => MT::Log::ERROR(),
192            class => 'system',
193            category => 'commenter_authentication',
194        });
195        return $app->error($app->translate(
196                    "No public key could be found to validate registration."));
197    }
198    my ($p) = $dsa_key =~ /p=([0-9a-f]*)/i;
199    my ($q) = $dsa_key =~ /q=([0-9a-f]*)/i;
200    my ($g) = $dsa_key =~ /g=([0-9a-f]*)/i;
201    my ($pub_key) = $dsa_key =~ /pub_key=([0-9a-f]*)/i;
202    $dsa_key = {p=>$p, q=>$q, g=>$g, pub_key=>$pub_key};
203    my $valid = dsa_verify(Key => $dsa_key,
204                           Signature => $sig,
205                           Message => $msg);
206    $timer = time - $timer;
207
208    $app->log({
209        message => $app->translate("TypeKey signature verif'n returned [_1] in [_2] seconds verifying [_3] with [_4]",
210            ($valid ? "VALID" : "INVALID"), $timer, $msg, $sig_str),
211        class => 'system',
212        level => MT::Log::WARNING(),
213    }) unless $valid;
214
215    $app->log({
216        message => $app->translate("The TypeKey signature is out of date ([_1] seconds old). Ensure that your server's clock is correct", ($params{ts} - time)),
217        class => 'system',
218        level => MT::Log::WARNING(),
219    }) unless ($params{ts} + $SIG_WINDOW >= time);
220
221    return ($valid && $params{ts} + $SIG_WINDOW >= time);
222}
223
2241;
Note: See TracBrowser for help on using the browser.