#!/usr/bin/perl
#
# Copyright 1992, A. Brossard
#
# shadow_passwd changes your password file(s) to use a shadow password file
#	It will start the pwdauthd if it isn't running.
#	It also parses /var/yp/Makefile to find any other password file you may
# be using.
#
# /etc/passwd
#	Add C2 adjunct file if not already present, does NOT enable auditing
#
#**************************************************************************
# Patch-ID# 100201-04  & 4.1.2
# The following pseudo-users must be added to /etc/passwd and             *
# /etc/security/passwd.adjunct before changing any binaries               *
# This is so the auditing of the rpc.pwdauthd and rpc.yppasswd can occur  *
#                                                                         *
# /etc/passwd additions:                                                  *
# # Note: for systems not running NIS, the AUyppasswdd can be left out    *
#                                                                         *
#                                                                         *
#  AUpwdauthd:##AUpwdauthd:10:10:::/bin/false                             *
#  AUyppasswdd:##AUyppasswdd:11:10:::/bin/false                           *
#                                                                         *
#                                                                         *
#/etc/security/passwd.adjunct additions:                                  *
#                                                                         *
#  AUpwdauthd:*:::::                                                      *
#  AUyppasswdd:*:::::                                                     *
#                                                                         *
#**************************************************************************
#
sub shadow_passwd {
    local( $in_file, $out_file ) = @_;
    local( $plus, $audit_uid, $pwd_uid, $yp_uid );
    local( $username, @order, $passwd, %passwd, %adjunct, %uid, $uid );
    local( $Username );
#
#	1- Read in the passwd and passwd.adjunct files
#
    open ( IN, $in_file );
    while ( <IN> ) {	# sync and uucp is never used locally is a security risk
	/^\+:\s*$/ && ($plus = 1) && next;
	/^\+::0:0:::$/ && ($plus = 1) && next;
	($username) = /^[-+]?([^:]+):/;
	$passwd{ $username } = $_;
	push( @order, $username );
	($uid) = /^[^:]+:[^:]*:(\d+):/;
	if( $username eq 'audit' ) { ($audit_uid) = $uid; }
	elsif( $username eq 'AUpwdauthd' ) { ($pwd_uid) = $uid; }
	elsif( $username eq 'AUyppasswdd' ) { ($yp_uid) = $uid; }
			# Record uid's in case of conflicts
	$uid{ $uid } = 1;
    }
    close IN;
    if( -e $out_file ) {
	open ( IN, $out_file );
	while ( <IN> ) {
	    ($username) = /^([^:]+):/;
	    $adjunct{ $username } = $_;
	}
	close IN;
    }
#
#	2- Assign uid's to sys, audit, staff, pwdauthd and yppasswdd
#	2.1- Write out the adjunct file, using the order in the passwd file 
#
    if( ! $audit_uid ) { $audit_uid = &sp_get_uid( 9 ); }
    if( ! $pwd_uid ) { $pwd_uid = &sp_get_uid( 11 ); }
    if( ! $yp_uid ) { $yp_uid = &sp_get_uid( 12 ); }

    open ( OUT, ">$out_file.new" );
    print OUT "AUpwdauthd:*:::::all\n" unless $passwd{ 'AUpwdauthd' };
    print OUT "AUyppasswdd:*:::::all\n" unless $passwd{ 'AUyppasswdd' };
    print OUT "audit:*:::::all\n" unless $passwd{ 'audit' };
    foreach $username ( @order ) {
	# Keep + of NIS entries
	($Username)=$passwd{ $username } =~ /^([^:]+):/ if  $passwd{ $username };
	($passwd) = $passwd{ $username } =~ /^[^:]+:([^:]*):/;
	if( $passwd =~ /^##(.*)$/ && ! $adjunct{ $1 } ) {
	    &print( "Password file says $username is already in adjunct file, but it wasn't!\n", "Le fichier de mots de passe indiquait que $username tait dja dans le fichier
adjunct, mais ce n'tait pas le cas!?\n");
	    print OUT "$Username:*:::::all\n";
	} elsif( $passwd =~ /^##(.*)$/ && $adjunct{ $1 } ) {
	    print OUT $adjunct{ $1 };
	} elsif( $adjunct{ $username } ) {
	    &print( "Password file did not indiquate that $username was in adjunct file but he was?\n", "Le fichier de mots de passe n'indiquait pas que $username tait dja dans le
fichier adjunct.\n");
	    $adjunct{ $username } =~ s/^([^:]*:)[^:]*(:.*)$/\1$passwd\2/;
	    print OUT $adjunct{ $username };
	} else {
	    print OUT "$Username:$passwd:::::all\n";
	}
    }
    close OUT;
    rename( "$out_file.new", $out_file );
    chmod 0600, $out_file;
#
#	3- Write out the passwd file
#
    open ( OUT, ">$in_file.new" );
    foreach $username ( @order ) {
	if( $passwd{ $username } =~ /^[-+]/ ) {	# NIS entry, print as is
	    print OUT $passwd{ $username };
	} else {
	    ($end) = $passwd{ $username } =~ /^[^:]+:[^:]*(:.*\n)$/;
	    print OUT "$username:##$username$end";
	}
    }
    print OUT "audit:##audit:$audit_uid:9:C2 Audit user:/etc/security/audit:/usr/bin/csh\n"				unless $passwd{ 'audit' };
    print OUT "AUpwdauthd:##AUpwdauthd:$pwd_uid:10:C2 security:/:/bin/false\n"
				unless $passwd{ 'AUpwdauthd' };
    print OUT "AUyppasswdd:##AUyppasswdd:$yp_uid:10:C2 security:/:/bin/false\n"
				unless $passwd{ 'AUyppasswdd' };
    print OUT "+:\n" if $plus;
    close OUT;
    rename( "$in_file", "$in_file.bak"); chmod 0600, "$in_file.bak";
    rename( "$in_file.new", "$in_file");
}

# -----------------   full_shadow_passwd  ---------------------

sub full_shadow_passwd {
    local( $root, $usr ) = @_;
#
#	1- create the /etc/security hierarchy if needed
#	2- Create the adjunct file, using the order in the passwd file 
#	3- Update rc.local
#	4- Modify /usr/etc/yp/ypinit because there is no group.adjunct
#
print "In full_shadow \n";
    chdir $root . 'etc';
    if( ! -d 'security' ) { mkdir( 'security',  02711 ); chown 0,0,'security'; }
    chdir 'security';
    if( ! -d 'audit' ) { mkdir( 'audit', 02711 ); chown 9,9,'audit'; }

    &shadow_passwd( $root . 'etc/passwd', "${root}etc/security/passwd.adjunct" );

    chdir $root . 'var/yp';
    require 'get_nis_passwd_file';
    local( $pwd, $shadow ) = &get_nis_passwd_file;
    if( $pwd && $pwd ne 'NIS' ) {	# $pwd true only if master NIS
	if( $pwd ne '/etc/passwd' ) {
#
#	This master NIS is using a special copy of the password file for NIS
#	so we need to shadow this once
#
	    if( ! $shadow || $shadow eq '/etc/security/passwd.adjunct' ) {
		local( $base ) = $pwd;
		if( $base =~ /\// ) { $base =~ s#^(.*)/[^/]+$#$1#; }
		else { $base = ''; }
		chdir $root . $base;
		if( ! -d 'security' ) {
		    mkdir( 'security',  02711 ); chown 0,0,'security';
		}
		$shadow = "$root$base/security/passwd.adjunct";
	    }
	    $shadow =~ s#//#/#g;
	    &shadow_passwd( $root . $pwd, $root . $shadow );
#
# Now we have to make sure the Makefile uses this new file!
#
	    &fsp_yp_makefile( $root, $shadow );
	    &print( "    New NIS shadow password file is: $shadow.\n",
		 "Le nouveau fichier de mots de passes NIS est $shadow.\n");
	} else {
	    $shadow = '/etc/security/passwd.adjunct' if !$shadow;
	}
    } elsif( $pwd eq 'NIS' ) {
	&print( "If this machine is the NIS master, then you have a problem because the NIS
password database was not converted to use the shadow password mechanism.\n",
	    "Si cette machine ($host) est le matre NIS, alors vous avez un problme
car la base de donne NIS pour les mots de passe n'a pas t convertie.\n");
    } # else, not the NIS master
#
#	Go patch the rc.local file, need yppasswdd, pwdauthd
#
    &fsp_rc_local( $root, $pwd, $shadow );
    &fsp_ypinit( $usr );
}

sub sp_get_uid {
    local( $default ) = shift;
    while( $uid{ $default } ) { $default++; }
    return $default;
}

# --------------------  fsp_rc_local  -----------------

sub fsp_rc_local {
    local( $root, $passwd, $shadow ) = @_;
    local( $yppasswdd, $pwdauthd, $sh );
    chdir $root . "/etc";
    open ( IN, "rc.local" );
    open ( OUT, ">rc.local.new" );
    while ( <IN> ) {

#       Needed only for C2 and not for shadow password

        if( /if.*auditd/ && ! /group.adjunct/ ) {
            print OUT "if [ -f /usr/etc/auditd -a -f /etc/security/group.adjunct ]; then\n";
            next;
        }
        if ( /ypxfrd/ ) {             # uncomment out ypxfrd if using NIS
	    s|^#|| if $passwd;
	} elsif ( /rpc.yppasswdd/ && /^#/ && $passwd ) {
	    s|^#||; print OUT; <IN>;
	    $sh = 'passwd.adjunct' if $shadow;
	    if( $passwd ) {
		print OUT "		/usr/etc/rpc.yppasswdd $passwd $shadow -m passwd passwd.adjunct\n";
	    } else {
		print OUT "		/usr/etc/rpc.yppasswdd /etc/passwd -m passwd $sh\n";
	    }
	    $_ = <IN>; s|^\s*#||; print OUT;	# echo
	    $_ = <IN>; s|^\s*#||;	# fi
	    $yppasswdd = 1;
        } elsif( /^\s*#\s*if.*rpc.pwdauthd/ ) {      # Needed for shadow password
	    if( !$shadow ) { print OUT; next; }
            s/^\s*#//; print OUT;
            $_ = <IN>; s/^\s*#//; print OUT;
            $_ = <IN>; s/^\s*#//;
	    $pwdauthd = 1;
        }
	print OUT;
    }
    close IN; close OUT;
    rename( 'rc.local.new', 'rc.local');
    if( ($passwd && !$yppasswdd) || ($shadow &&!$pwdauthd) ) {	# daemon does not appear in rc.local
	open ( IN, "rc.local" );
	open ( OUT, ">rc.local.new" );
	while ( (!$yppasswdd && !$pwdauthd) && <IN> ) {
	    if ( ! $yppasswdd && /ypxfrd/ ) {
		print OUT $_ . "
                if [ -f /usr/etc/rpc.yppasswdd -a -d /var/yp/\$dname ]; then
                      /usr/etc/rpc.yppasswdd $passwd $shadow -m passwd passwd.adjunct
                      echo -n ' yppasswdd'
                fi\n";
		$yppasswdd = 1;
		next;
            }
	    if ( ! $pwdauthd && /rpc.lockd &/ ) {
		print OUT; $_ = <IN>; print OUT $_ . "
#
# start up authentication daemon if present and if adjunct file exists
#
if [ -f /usr/etc/rpc.pwdauthd -a -f /etc/security/passwd.adjunct ]; then
	rpc.pwdauthd &          echo -n ' pwdauthd'
fi\n";
		$pwdauthd = 1;
		next;
	    }
	    print OUT;
	}
	print OUT <IN>;
    }
    close IN; close OUT;
    rename( 'rc.local.new', 'rc.local');
}


# --------------------  fsp_yp_makefile  -----------------

sub fsp_yp_makefile {
    local( $root, $shadow ) = @_;
    chdir $root . 'var/yp';
    open ( IN, 'Makefile' );
    open ( OUT, '>Makefile.new' );
inner:    while ( <IN> ) {
	if( /^all:/ ) {
	    while( /\\\n$/ ) {
		if( /c2secure/ || /passwd.adjunct.time/ ) {
		    print OUT; next inner;
		}
		print OUT;
		$_ = <IN>;
	    }
	    if( /c2secure/ || /passwd.adjunct.time/ ) { print OUT; next; }
	    s/\n$/ c2secure\n/;
	    print OUT;
	    next;
	}
	if( /^c2secure:/ ) {
	    print OUT "PWD_ADJ=$shadow\n" . $_;
	    $_ = <IN> while ! /fi\s*\n$/;
	    print OUT
'	-@if [ -f $(PWD_ADJ) ]; then \\
	    if [ ! $(NOPUSH) ]; then $(MAKE)  $(MFLAGS) -k passwd.adjunct.time;\\
	    else $(MAKE) $(MFLAGS) -k NOPUSH=$(NOPUSH) passwd.adjunct.time; \\
	    fi; \\
	fi
	-@if [ -f $(DIR)/security/group.adjunct ]; then \\
	    if [ ! $(NOPUSH) ]; then $(MAKE)  $(MFLAGS) -k group.adjunct.time;\\
	    else $(MAKE) $(MFLAGS) -k NOPUSH=$(NOPUSH) group.adjunct.time; \\
	    fi; \\
	fi
';
	    next;
	}
	if( /^passwd.adjunct.time:/ ) {
	    print OUT 'passwd.adjunct.time:	$(PWD_ADJ)' . "\n";
	    $_ = <IN>; print OUT;
	    print OUT '	  $(PWD_ADJ) $(CHKPIPE)) | \\' . "\n";
	    <IN>; next;
	}
	print OUT;
    }
    close IN; close OUT;
    rename( 'Makefile.new', 'Makefile');
    &print( "/var/yp/Makefile was rewritten to handle the new shadow password file,
you should copy the new file over to your slave server BEFORE rebuilding your
NIS maps:
cd /var/yp
rcp `domainname`/passwd.adjunct.byname.* slave:/var/yp/`domainname`
rcp `domainname`/hosts.nis.* slave:/var/yp/`domainname`
make\n",
"/var/yp/Makefile a t modifie pour tenir compte du shadow password file.
Vous allez devoir copier cette nouvelle base NIS avant de faire un make:
cd /var/yp
rcp `domainname`/passwd.adjunct.byname.* slave:/var/yp/`domainname`
rcp `domainname`/hosts.nis.* slave:/var/yp/`domainname`
make\n");
}

#-----------------------   fsp_ypinit   --------------------

# Modify ypinit such that the map group.adjunct.byname will only be
# created if group.adjunct exists (not created by my shadow_passwd script

sub fsp_ypinit {
    local( $usr ) = shift;

    chdir $usr . "/etc/yp" || return;  # if it fails /usr/etc/yp doesn't exist 
    open ( IN, 'ypinit' ) || return;
    if( !open ( OUT, '>ypinit.new' ) ) { close RC; return; }
    while ( <IN> ) {
	if( /maps="/ ) {
	    print OUT;
	    while ( <IN> ) {
		if( /^\s*fi\s*$/ ) {
		    print OUT $_ .
'if [ -f /etc/security/group.adjunct ] ; then
	maps="$maps group.adjunct.byname"
fi
';
		    last;
		}
		s/group.adjunct.byname//;
		print OUT;
	    }
	    next;
	}
	print OUT;
    }
    close IN; close OUT;
    rename( 'init.new', 'init');
}

#---------------------   Script   -------------------------------

if( !$src_files ) {     # standalone
    local( $mount ) = 0;
    $src_files = "/tmp/4.1.2";
    chop( $host=`hostname` );
    $rhost='sasun1';
    $mount_prog = "/export/mnt/4.1.2";
    push( @INC, "$src_files/perl-lib");

    $ENV{PATH}="/usr/etc:$ENV{PATH}";

    if( ! -d "$src_files/$arch" ) {
        $mount = 1;
        mkdir( $src_files, 02755);
        system "mount $rhost:$mount_prog $src_files";
    }
    if( ! -d "$src_files/$arch" ) {
        die  $lang ? "mount de $rhost:$mount_prog sur $src_files a chou, aborting\n" : "mount of $rhost:$mount_prog on $src_files failed, aborting\n" if $?;
    }
    push( @INC, "$src_files/perl-lib");
    require 'print.pl';

    if( $#ARGV >= 0 ) {
        while( $#ARGV >= 0 ) {
            shift @ARGV;
	    &shadow_passwd( $_, "$_.adjunct" );
        }
    } else {
       &full_shadow_passwd( '/', '/usr' );	# Used as a program
       &print( "/etc/passwd and the NIS password map have been converted.\n",
      "/etc/passwd et fichier password NIS ont t converties s'il y a lieu.\n");
    }
    &print( "Starting password authentification daemon (rpc.pwdauthd).\n", "Je dmarre le daemon qui authentifie les mots de passe (rpc.pwdauthd).\n");
    system 'rpc.pwdauthd';
    system "umount $src_files" if $mount;
    rmdir $src_files if $mount;
}
1;
