Initial commit
This commit is contained in:
commit
eb48e9e8d7
2 changed files with 310 additions and 0 deletions
149
Privileges/Drop.pm
Normal file
149
Privileges/Drop.pm
Normal file
|
@ -0,0 +1,149 @@
|
|||
package Privileges::Drop;
|
||||
use strict;
|
||||
use warnings;
|
||||
use English;
|
||||
use Carp;
|
||||
|
||||
our $VERSION = '1.01';
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Privileges::Drop - A module to make it simple to drop all privileges, even
|
||||
POSIX groups.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module tries to simplify the process of dropping privileges. This can be
|
||||
useful when your Perl program needs to bind to privileged ports, etc. This
|
||||
module is much like Proc::UID, except that it's implemented in pure Perl.
|
||||
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Privileges::Drop;
|
||||
|
||||
# Do privileged stuff
|
||||
|
||||
# Drops privileges and sets euid/uid to 1000 and egid/gid to 1000.
|
||||
drop_uidgid(1000, 1000);
|
||||
|
||||
# Drop privileges to user nobody looking up gid and uid with getpwname
|
||||
# This also set the enviroment variables USER, LOGNAME, HOME and SHELL.
|
||||
drop_privileges('nobody');
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
use base "Exporter";
|
||||
|
||||
our @EXPORT = qw(drop_privileges drop_uidgid);
|
||||
|
||||
=item drop_uidgid($uid, $gid, @groups)
|
||||
|
||||
Drops privileges and sets euid/uid to $uid and egid/gid to $gid.
|
||||
|
||||
Supplementary groups can be set in @groups.
|
||||
|
||||
=cut
|
||||
|
||||
sub drop_uidgid {
|
||||
my ($uid, $gid, @reqPosixGroups) = @_;
|
||||
|
||||
# Sort the groups and make sure they are uniq
|
||||
my %groupHash = map { $_ => 1 } ($gid, @reqPosixGroups);
|
||||
my $newgid ="$gid ".join(" ", sort { $a <=> $b } (keys %groupHash));
|
||||
|
||||
# Drop privileges to $uid and $gid for both effective and save uid/gid
|
||||
$GID = $EGID = $newgid;
|
||||
$UID = $EUID = $uid;
|
||||
|
||||
# Sort the output so we can compare it
|
||||
my %GIDHash = map { $_ => 1 } ($gid, split(/\s/, $GID));
|
||||
my $cgid = int($GID)." ".join(" ", sort { $a <=> $b } (keys %GIDHash));
|
||||
my %EGIDHash = map { $_ => 1 } ($gid, split(/\s/, $EGID));
|
||||
my $cegid = int($EGID)." ".join(" ", sort { $a <=> $b } (keys %EGIDHash));
|
||||
|
||||
# Check that we did actually drop the privileges
|
||||
if($UID ne $uid or $EUID ne $uid or $cgid ne $newgid or $cegid ne $newgid) {
|
||||
croak("Could not drop privileges to uid:$uid, gid:$newgid\n"
|
||||
."Currently is: UID:$UID, EUID=$EUID, GID=$cgid, EGID=$cegid\n");
|
||||
}
|
||||
}
|
||||
|
||||
=item drop_privileges($user)
|
||||
|
||||
Drops privileges to the $user, looking up gid and uid with getpwname and
|
||||
calling drop_uidgid() with these arguments.
|
||||
|
||||
The environment variables USER, LOGNAME, HOME and SHELL are also set to the
|
||||
values returned by getpwname.
|
||||
|
||||
Returns the $uid and $gid on success and dies on error.
|
||||
|
||||
NOTE: If drop_privileges() is called when you don't have root privileges
|
||||
it will just return undef;
|
||||
|
||||
=cut
|
||||
|
||||
sub drop_privileges {
|
||||
my ($user) = @_;
|
||||
|
||||
croak "No user give" if !defined $user;
|
||||
|
||||
# Check if we are root and stop if we are not.
|
||||
if($UID != 0 and $EUID != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Find user in passwd file
|
||||
my ($uid, $gid, $home, $shell) = (getpwnam($user))[2,3,7,8];
|
||||
if(!defined $uid or !defined $gid) {
|
||||
croak("Could not find uid and gid user $user");
|
||||
}
|
||||
|
||||
# Find all the groups the user is a member of
|
||||
my @groups;
|
||||
while (my ($name, $comment, $ggid, $mstr) = getgrent()) {
|
||||
my %membership = map { $_ => 1 } split(/\s/, $mstr);
|
||||
if(exists $membership{$user}) {
|
||||
push(@groups, $ggid) if $ggid ne 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Cleanup $ENV{}
|
||||
$ENV{USER} = $user;
|
||||
$ENV{LOGNAME} = $user;
|
||||
$ENV{HOME} = $home;
|
||||
$ENV{SHELL} = $shell;
|
||||
|
||||
drop_uidgid($uid, $gid, @groups);
|
||||
|
||||
return ($uid, $gid, @groups);
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
As this module only uses Perl's build in function, it relies on them to work
|
||||
correctly. That means setting $GID and $EGID should also call setgroups(),
|
||||
something that might not have been the case before Perl 5.004. So if you are
|
||||
running an older version, Proc::UID might be a better choice.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Troels Liebe Bentsen <tlb@rapanden.dk>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright(C) 2007-2009 Troels Liebe Bentsen
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself.
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
161
gopher.pl
Normal file
161
gopher.pl
Normal file
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#####################################
|
||||
# Thomas Schwery <thomas@schwery.me>
|
||||
####################################
|
||||
#
|
||||
# TODO:
|
||||
# - Support for a list of tags
|
||||
# - Cleanup on ctrl-c
|
||||
# - Multithreading
|
||||
# - ...
|
||||
|
||||
use strict;
|
||||
use IO::Socket;
|
||||
use Fcntl ':mode';
|
||||
use Privileges::Drop;
|
||||
use HTML::Parse;
|
||||
use HTML::FormatText;
|
||||
use Text::Wrap;
|
||||
use Date::Parse;
|
||||
use Date::Format;
|
||||
|
||||
our $port = 70;
|
||||
our %entries = ();
|
||||
|
||||
$SIG{INT} = \&signal_handler_interrupt;
|
||||
|
||||
my $socket = new IO::Socket::INET (LocalPort => $port, Proto => 'tcp', Listen => '1');
|
||||
die "Unable to open port\n" unless $socket;
|
||||
|
||||
drop_privileges('nobody');
|
||||
|
||||
while (1) {
|
||||
my $listen_socket = $socket->accept();
|
||||
$_ = <$listen_socket>;
|
||||
s/\r*\n*//g;
|
||||
s/\t.*//g; # Don't care about the parameters now ...
|
||||
my $directory = $_;
|
||||
print "Client " . $.. ": <" . $_ . ">\n";
|
||||
|
||||
if (-f $directory) {
|
||||
if (substr($directory, 0, 4) eq "blog") {
|
||||
$listen_socket->send(send_blog_entry($directory));
|
||||
} else {
|
||||
open FILE, $directory;
|
||||
while (<FILE>) {
|
||||
$listen_socket->send($_);
|
||||
}
|
||||
$listen_socket->send("\r\n.\r\n") if (-T $directory);
|
||||
}
|
||||
} else {
|
||||
my $header = generate_header($directory);
|
||||
my $blog_entries = list_blog_entries();
|
||||
|
||||
$listen_socket->send($header . "\r\n");
|
||||
$listen_socket->send("\r\n");
|
||||
$listen_socket->send("i----- Blog entries ---------------\tfake\tfake\t0\r\n");
|
||||
$listen_socket->send($blog_entries . "\r\n");
|
||||
$listen_socket->send("\r\n.\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
sub generate_header {
|
||||
my ($directory) = @_;
|
||||
$directory .= "/" if ($directory);
|
||||
|
||||
return "\r\n" unless (-T $directory . "header");
|
||||
|
||||
my $message = "";
|
||||
open FILE, $directory . "header";
|
||||
while (<FILE>) {
|
||||
$message .= "i" . $_ . "\tfake\tfake\t0\r\n";
|
||||
}
|
||||
close FILE;
|
||||
$message .= "0About this gopher server\theader\tlocalhost\t" . $port . "\r\n";
|
||||
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
sub map_content {
|
||||
my ($dir_entry, $directory) = @_;
|
||||
$directory .= "/" if ($directory);
|
||||
my $file_type = -f $directory . $dir_entry ? 0 : 1;
|
||||
$file_type = 9 if (-f $directory . $dir_entry && -B $directory . $dir_entry);
|
||||
return $file_type . $dir_entry . "\t" . $directory . $dir_entry . "\tlocalhost\t" . $port;
|
||||
}
|
||||
|
||||
sub filter_content {
|
||||
my ($dir_entry, $blog_dir) = @_;
|
||||
return 1 unless (substr($dir_entry,0,1) eq "." || $dir_entry eq "header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub list_blog_entries {
|
||||
my $blog_dir = "blog";
|
||||
unless (opendir(IMD, $blog_dir)) {
|
||||
die("Fuck you all ...");
|
||||
}
|
||||
my @files = readdir(IMD);
|
||||
closedir(IMD);
|
||||
|
||||
@files = grep filter_content($_, $blog_dir), @files;
|
||||
@files = map map_blog_entry($_, $blog_dir), @files;
|
||||
|
||||
@files = sort sort_entry_line($a,$b), @files;
|
||||
return join("\r\n", @files);
|
||||
}
|
||||
|
||||
sub sort_entry_line {
|
||||
my $a_date = $2 if ($a =~ m/^0(\[.*\]\s)?(.*?)\s*-.*$/);
|
||||
my $b_date = $2 if ($b =~ m/^0(\[.*\]\s)?(.*?)\s*-.*$/);
|
||||
|
||||
return str2time($a_date) <=> str2time($b_date);
|
||||
}
|
||||
|
||||
sub read_entry_file {
|
||||
my ($entry_file) = @_;
|
||||
my $subject = "";
|
||||
my $date_file = "";
|
||||
my $tags = "";
|
||||
|
||||
open ENTRY, $entry_file;
|
||||
while (<ENTRY>) {
|
||||
s/\r*\n*//;
|
||||
$subject = $1 if (m/^Subject:\s*(.*)$/);
|
||||
$tags = $1 if (m/^Tags:\s*(.*)$/);
|
||||
$date_file = $1 if (m/^Date:\s*(.*)$/);
|
||||
}
|
||||
close ENTRY;
|
||||
|
||||
my $time = str2time($date_file);
|
||||
my $date = time2str("%o %h %Y", $time);
|
||||
return "[" . $tags . "] " . $date . " - " . $subject if($tags);
|
||||
return $date. " - " . $subject;
|
||||
}
|
||||
|
||||
sub map_blog_entry {
|
||||
my ($entry_file, $entry_dir) = @_;
|
||||
my $entry_title = read_entry_file($entry_dir . "/" . $entry_file);
|
||||
return "0" . $entry_title . "\t" . $entry_dir . "/" . $entry_file . "\tlocalhost\t" . $port;
|
||||
}
|
||||
|
||||
sub send_blog_entry {
|
||||
my ($entry_file) = @_;
|
||||
open FILE, $entry_file;
|
||||
my $entry_text = "";
|
||||
while (<FILE>) {
|
||||
$entry_text .= $_;
|
||||
}
|
||||
$entry_text =~ s/\r?\n/<br>/mg;
|
||||
my $plain_text = HTML::FormatText->new->format(parse_html($entry_text));
|
||||
$Text::Wrap::columns = 80;
|
||||
$plain_text = wrap("\t", "", $plain_text);
|
||||
return $plain_text;
|
||||
}
|
||||
|
||||
sub signal_handler_interrupt {
|
||||
print "ctrl-c, exiting...\n";
|
||||
exit 0;
|
||||
}
|
Reference in a new issue