diff options
-rw-r--r-- | bpaste.sh | 10 | ||||
-rwxr-xr-x | index.cgi | 66 | ||||
-rw-r--r-- | script.js | 11 |
3 files changed, 38 insertions, 49 deletions
@@ -14,6 +14,9 @@ # projects@yorhel.nl # License: MIT # +# 0.4 - 2016-06-21 +# - Remove -l option +# # 0.3 - 2016-02-14 # - Rewrote from perl to shell+curl # - Add HTTPS support @@ -34,8 +37,7 @@ SYNTAX=nosyntax FILE=- WRAP=0 CLICK=0 -LONG=0 -VERSION=0.3 +VERSION=0.4 showhelp() { @@ -46,7 +48,6 @@ $0 [-wcl] [-s <syntax>] [-f <file>] [-h <host>] -s <syntax> Enable syntax highlighting using <syntax>. -w Allow line wrapping. -c Don't make URLs clickable. - -l Generate a long and unguessable URL. To upload pastes with a passcode, store your passcode in ~/.bpastepass (per user) or /etc/bpastepass (globally). @@ -62,7 +63,6 @@ while getopts 'h:s:f:wcl?' opt; do f) FILE=$OPTARG ;; w) WRAP=1 ;; c) CLICK=1 ;; - l) LONG=1 ;; ?) showhelp ;; esac done @@ -81,6 +81,6 @@ PASS= [ -s ~/.bpastepass ] && PASS=`head -n 1 ~/.bpastepass` -curl -si -A "bpaste $VERSION" -F "f=<$TMPFILE" -F "s=$SYNTAX" -F "w=$WRAP" -F "c=$CLICK" -F "l=$LONG" -F "p=$PASS" $HOST\ +curl -si -A "bpaste $VERSION" -F "f=<$TMPFILE" -F "s=$SYNTAX" -F "w=$WRAP" -F "c=$CLICK" -F "p=$PASS" $HOST\ | grep 'Location: '\ | sed 's/^Location: //' @@ -24,7 +24,8 @@ TUWF::set( pre_request_handler => \&init, ); -my $codematch = qr/([a-z0-9]{5}|[a-zA-Z0-9\.-]{32})/; +# The first two matches are legacy paste codes, will not be generated anymore. +my $codematch = qr/([a-z0-9]{5}|[a-zA-Z0-9\.-]{32}|[a-zA-Z0-9]{12})/; TUWF::register( qr// => \&home, qr/mypastes/ => \&mypastes, @@ -75,12 +76,10 @@ sub upload { { post => 's', required => 0, default => 'nosyntax', enum => \@syntax }, { post => 'w', required => 0, default => 0 }, { post => 'c', required => 0, default => 0 }, - { post => 'l', required => 0, default => 0 }, ); return $self->msg('Unknown syntax code', 'backform') if $f->{_err} && grep $_->[0] eq 's', @{$f->{_err}}; - my $code = $self->getcode(!!$f->{l}); - return if !$code; + my $code = $self->getcode(); # create redirect response first, so that any Set-Cookie headers set in # ->passcode() aren't forgotten. msg() calls resInit() anyway @@ -273,7 +272,9 @@ sub _get_html { package TUWF::Object; use TUWF ':html', 'html_escape'; +use POSIX 'floor'; use Socket 'inet_pton', 'inet_ntop', 'AF_INET', 'AF_INET6'; +use feature 'state'; sub htmlHeader { @@ -349,9 +350,6 @@ sub htmlUploadForm { input type => 'checkbox', class => 'check', id => 'c', name => 'c', value => 1, $r->{parse_urls} ? (checked => 'checked') : (); label for => 'c', ' make URLs clickable'; br; - input type => 'checkbox', class => 'check', id => 'l', name => 'l', value => 1; - label for => 'l', ' secure but ugly URL'; - br; i 'Syntax highlighting: '; input type => 'text', name => 's', id => 's', size => 10, value => $r->{syntax}; i; @@ -509,39 +507,33 @@ sub passcode { } -sub getcode { - my($self, $secure) = @_; - # The secure character set must be a power-of-two number of chars, otherwise - # we'd introduce a bias in the random string generator below. 32 characters - # in base64 correspond to 192 bits, which is quite secure. - my @chars = $secure ? ('0'..'9', 'a'..'z', 'A'..'Z', '-', '.') : ('0'..'9', 'a'..'z'); - my $numchars = $secure ? 32 : 5; - - open my $R, '<', '/dev/urandom' or die "Unable to open /dev/urandom\n"; - - my($i, $code) = (0); - while($i < 10) { - # Use one byte of random for each character. We're throwing away some - # random data this way (256 possibilities when we only have 36 or 64), but - # that's alright. - my $r = sysread($R, $code, $numchars); - die "Did not read enough random numbers (got $r, $!)\n" if $r != $numchars; - $code = join '', map $chars[$_ % @chars], unpack 'C*', $code; - - # Weird characters at the start or end are annoying, skip these - next if $code =~ /^[-.]/ || $code =~ /[-.]$/; - - last if !$self->dbRow('SELECT 1 AS exist FROM pastes WHERE code = ?', $code)->{exist}; - warn "Generated duplicate code: $code. Trying again...\n"; - $i++; +# Get a secure and uniform random number between 0 and $n (0 inclusive, $n exclusive, $n <= 256) +sub getrand { + my $n = shift; + state $R; + if(!$R) { + open $R, '<', '/dev/urandom' or die "Unable to open /dev/urandom\n"; } - - if($i == 10) { - warn "!! No unused code found within 10 iterations!\n"; - $self->msg('Unable to allocate new code, please try again later.'); - return undef; + my $i = 20; + while($i-- > 0) { + my $buf; + die "Unable to read from /dev/urandom" if sysread($R, $buf, 1) != 1; + my $c = unpack('C', $buf); + return $c % $n if $c < $n*floor(256/$n); } + die "Unable to fetch random number\n"; +} + +# Paste codes are now 12 characters of [a-zA-Z0-9], which is around 71.5 bits +# in strength. This is already hard (but not entirely infeasable) to +# brute-force at high speeds, but is completely infeasable to guess with the +# throttle of 1 guess per 10 seconds in place. +sub getcode { + my $self = shift; + my @chars = ('0'..'9', 'a'..'z', 'A'..'Z'); + my $code = join '', map $chars[getrand(scalar @chars)], 1..12; + die "Generated duplicate code: $code.\n" if $self->dbRow('SELECT 1 AS exist FROM pastes WHERE code = ?', $code)->{exist}; return $code; } @@ -107,21 +107,19 @@ if(x) { var w = byId('w'); var c = byId('c'); var s = byId('s'); - var l = byId('l'); var def = function() { var cook = getCookie('formatdef'); if(!cook) - return [w.checked, c.checked, s.value, l.checked]; + return [w.checked, c.checked, s.value]; else { - var v = cook.split(/,/); // wrap, click, syntax, secure + var v = cook.split(/,/); // wrap, click, syntax, secure (not used anymore) v[0] = v[0]=='1' ? true : false; v[1] = v[1]=='1' ? true : false; - v[3] = v[3]=='1' ? true : false; return v; } }; var setclass = function() { - x.className = w.checked==v[0] && c.checked==v[1] && s.value==v[2] && l.checked==v[3] ? '' : 'notdef'; + x.className = w.checked==v[0] && c.checked==v[1] && s.value==v[2] ? '' : 'notdef'; }; var v = def(); if(!byId('p').value) { // Don't apply some settings when copying another paste @@ -129,14 +127,13 @@ if(x) { c.checked = v[1]; s.value = v[2]; } - l.checked = v[3]; x.onclick = function() { setCookie('formatdef', (w.checked ? '1' : '0')+','+(c.checked ? '1' : '0')+','+s.value+','+(l.checked ? '1' : '0')); v = def(); setclass(); return false; }; - w.onclick = c.onclick = l.onclick = s.onkeyup = s.onfocus = setclass; + w.onclick = c.onclick = s.onkeyup = s.onfocus = setclass; setclass(); } |