#!/usr/bin/perl
# cpanel - autofixer2/authorize                   Copyright(c) 2013 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;

our $VERSION = 1.4;

BEGIN {
    unshift @INC, '/usr/local/cpanel';
}

{
    local $| = 1;

    my ( $ticket_id, $user, $server_num ) = get_args();
    $user ||= 'root';

    usage() if !$ticket_id;

    my $key = `curl 'https://tickets.cpanel.net/review/fetch_public_key.cgi?ticketid=$ticket_id&servernum=$server_num'`;
    $key =~ s/^\s+//;
    $key =~ s/\s+$//;

    if ( $key !~ m/^ssh/ ) {
        print "The server did not return a key for $ticket_id: $key\n";
        exit 1;
    }

    my ( $key_type, $key_data, $key_comment ) = split( /\s+/, $key );

    setuids_if_needed($user);

    fix_permissions_on_authorized_keys($user);
    add_key_authorized_keys( $user, $key_type, $key_data, $ticket_id, $server_num );

    printf( "Authorized key %d_server_%d\@tickets.cpanel.net for %s\n", $ticket_id, $server_num, $user );

    exit 0;
}

sub fix_permissions_on_authorized_keys {
    my ($user) = @_;

    my $homedir = ( getpwnam($user) )[7];

    if ( !$homedir ) {
        print "Failed to fetch homedir for $user\n";
        exit 1;
    }

    mkdir "$homedir/.ssh", 0700;
    system 'touch', "$homedir/.ssh/authorized_keys";
    chmod 0600, "$homedir/.ssh/authorized_keys";

    return 1;
}

sub add_key_authorized_keys {
    my ( $user, $key_type, $key_data, $ticket_id, $server_num ) = @_;
    my $homedir = ( getpwnam($user) )[7];
    eval { local $SIG{__DIE__}; local $SIG{__WARN__}; require Cpanel::SafeFile; };

    my $time                     = time();
    my $key_comment_without_time = sprintf( '%d_server_%d@tickets.cpanel.net', $ticket_id, $server_num );
    my $key_comment              = $key_comment_without_time . '_' . $time;
    my $authorized_keys_file     = "$homedir/.ssh/authorized_keys";
    my $lock;
    my $fh;
    if ( $INC{'Cpanel/SafeFile.pm'} ) {
        if ( !( $lock = Cpanel::SafeFile::safeopen( $fh, '+<', $authorized_keys_file ) ) ) {
            print "Failed to open + lock $authorized_keys_file: $1\n";
            exit 1;
        }
    }
    else {
        if ( !open( $fh, '+<', $authorized_keys_file ) ) {
            print "Failed to open $authorized_keys_file: $1\n";
            exit 1;
        }
    }
    my $txt;
    {
        local $/ = undef;
        $txt = readline($fh);
    }
    my @all_keys_line = grep { !m/\Q$key_comment_without_time\E_\d+\s*$/ } split( /\n/, $txt );
    seek( $fh, 0, 0 );
    print {$fh} join( "\n", @all_keys_line ) . "\n";
    print {$fh} "$key_type $key_data $key_comment\n";
    truncate( $fh, tell($fh) );
    if ( $INC{'Cpanel/SafeFile.pm'} ) {
        Cpanel::SafeFile::safeclose( $fh, $lock );
    }
    else {
        close($fh);
    }
    return;
}

sub setuids_if_needed {
    my ($user) = @_;
    if ( $user ne ( getpwuid($>) )[0] ) {
        my ( $uid, $gid ) = ( getpwnam($user) )[ 2, 3 ];
        if ( !$uid ) { die "Failed for fetch uids for $user"; }
        setuids( $uid, $gid ) || die "Could not setuid to $user to modify authorized_keys";
    }

    return 1;
}

sub setuids {
    my ( $uid, $gid ) = @_;
    $) = join( ' ', $gid, $gid );
    if ( $gid != int($)) ) {
        die("setuids failed: Error setting EGID ($gid) [$uid]");
    }

    $( = $gid;
    if ( $gid != $( ) {
        die("setuids failed: Error setting RGID ($gid) [$uid]");
    }

    $> = $uid;
    if ( $> != $uid ) {
        die("setuids failed: Error setting EUID ($uid) [$uid]");
    }

    $< = $uid;
    if ( $< != $uid ) {
        die("setuids failed: Error setting RUID ($uid) [$uid]");
    }
    return 1;
}

sub usage {
    print "\n" x 3;
    print "Usage: authorize <ticketid> <servernum>          - Authorize the cPanel ticket system key for root\n";
    print "Usage: authorize <user> <ticketid> <servernum>   - Authorize the cPanel ticket system key for a user\n";
    print "Enviorment variables are accepted for execution though the autorepair system.\n";
    print "AUTHUSER=<user> TICKET=<ticketid> SERVERNUM=<servernum> /scripts/autorepair authorize\n";
    print "\n\n";
    exit 1;
}

sub get_args {

    my $ticket_id;
    my $user;
    my $server_num = 1;
    if ( scalar @ARGV == 2 ) {
        ( $ticket_id, $server_num ) = @ARGV;
    }
    elsif ( scalar @ARGV == 3 ) {
        ( $user, $ticket_id, $server_num ) = @ARGV;
    }

    if ( defined $ENV{'AUTHUSER'} ) {
        ( $user, $ticket_id, $server_num ) = ( $ENV{'AUTHUSER'}, $ENV{'TICKET'}, $ENV{'SERVERNUM'} );
    }
    elsif ( defined $ENV{'TICKET'} ) {
        ( $ticket_id, $server_num ) = ( $ENV{'TICKET'}, $ENV{'SERVERNUM'} );
    }
    $user =~ s/\s+//g;
    $ticket_id =~ s/\s+//g;
    $server_num =~ s/\s+//g;

    if ($user) {
        die "Invalid user specified\n" if !( getpwnam($user) )[0];
    }

    die "Invalid ticket id specified\n" if $ticket_id && $ticket_id !~ m/^[0-9]+$/;
    die "Invalid server number specified\n" if $server_num !~ m/^[0-9]+$/;

    return ( $ticket_id, $user, $server_num ) if defined $ticket_id;

    print "\n\ncPanel Ticket System authorized_keys installer.\n\n";
    if ( !$ticket_id ) {
        open( STDIN, '<&STDOUT' ) || die "Failed to open STDOUT";    # required to escape autorepair's stdin redirection
        print "Which ticket number would you like to authorize? ";

        $ticket_id = <>;
        chomp($ticket_id);
        if ( $ticket_id !~ /^[0-9]+$/ ) {
            print "Invalid ticket id.\n";
            usage();
        }

        print "Which server number on ticket $ticket_id would you like to authorize?[1]";
        $server_num = <>;
        chomp($server_num);
        $server_num ||= 1;
        if ( $server_num !~ /^[0-9]+$/ ) {
            print "Invalid server number\n";
            usage();
        }

        print "Which user would you like to add the key to [root] ? ";
        $user = <>;
        chomp($user);
        $user ||= 'root';
        if ( !( getpwnam($user) )[0] ) {
            print "Invalid user.\n";
            usage();
        }
    }
    return ( $ticket_id, $user, $server_num );
}
