#!/usr/local/bin/perl
#
# Copyright (c) 2007, Eric F Crist
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or 
# without modification, are permitted provided that the following 
# conditions are met:
#
# Redistributions of source code must retain the above copyright 
# notice, this list of conditions and the following disclaimer.
# 
# Redistributions in binary form must reproduce the above copyright 
# notice, this list of conditions and the following disclaimer in 
# the documentation and/or other materials provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# VERSION: ~~~VERSION~~~
# $Id: ssl-admin 367 2014-10-21 15:54:27Z ecrist $

use strict;
use warnings;

use File::Copy;

## Read config file and die if there's a syntax error.
my $config_file = "/usr/local/etc/ssl-admin/ssl-admin.conf";
die "$config_file doesn't exist.  Did you copy the sample from $config_file.default?" unless (-e $config_file);
my $result = do $config_file;
die "Syntax error in $config_file\n\n" unless ($result);

$ENV{'KEY_DIR'} = "/usr/local/etc/ssl-admin";


### Program-specific settings here ###
my $program_name = "ssl-admin";
my $build = "prod";
my $menu_item = "";
my $script_dir;
my $working_dir = $ENV{'KEY_DIR'};
my $key_days = $ENV{'KEY_DAYS'};
my $crl = "$working_dir/prog/crl.pem";
my $key_config = "$working_dir/openssl.conf";
my $cn= "";
my $cn_literal="";
my $user;
my $cn_o = 0;		#Override existing value in $cn
my $zip_cmd;
my $curr_serial;
my $new_runtime = 0;
my $intermediate = "NO";
my $key_size = $ENV{'KEY_SIZE'};
my $serial;

# Gather project information and run-time variables

sub update_serial {
	chomp($curr_serial = `cat $working_dir/prog/serial`);
}

sub common_name {
	my $cn_regex = '^[\w\s\-\.]+$';
	my $cn_new;
	do {
		print "Please enter certificate owner's name or ID.\nUsual format is first initial-last name (jdoe) or\n";
		print "hostname of server which will use this certificate.\nAll lower case, numbers OK.\nOwner [$cn]: ";
		chomp($cn_new = <>);
	} until $cn_new =~ m/$cn_regex/ or $cn =~ m/$cn_regex/;
	if (($cn_new ne $cn) and ($cn_new ne "")){
		$cn_literal = $cn_new;
		($cn = $cn_literal) =~ s/\s/_/g;
		print "\n\nFile names will use $cn.\n";
	}
	$ENV{'KEY_CN'} = "$cn_literal";
}

sub project_info {
	my $key_days_new;
	my $intermediate_new;
	if ($new_runtime == 1){
		print "Number of days key is valid for [$key_days]: ";
		chomp($key_days_new = <>);
		if (($key_days_new ne "$ENV{'KEY_DAYS'}") and ($key_days_new ne "")){
			$ENV{'KEY_DAYS'} = $key_days_new;
			$key_days = $key_days_new;
		}
		do {
		print "Key size in bits (up to 4096) [$key_size]: ";
		chomp($key_size = <>);
		} until ($key_size =~ m/^$/) or ($key_size =~ m/^[0-9]+$/ and ($key_size lt 4097));
		print "######################################################\n";
		print "####################  CAUTION!!!  ####################\n";
		print "######################################################\n";
		do {
		print "\nTurn on Intermediate CA certificate signing? (y/n): ";
		chomp($intermediate_new = <>);
		} until $intermediate_new =~ m/^[yn]+$/;
		if ($intermediate_new eq "y"){ 
			$intermediate = "YES"; 
			print "\nYou have enabled intermediate certificate signing! This means\n";
			print "any certicates you sign this session will be authorized to sign\n";
			print "new certificates which will be trusted by you and anyone who\n";
			print "trusts you.  This option is generally not going to be used.\n";
		}
		elsif ($intermediate_new eq "n"){ 
			$intermediate = "NO"; 
			print "Intermediate CA certificate signing is disabled.";
		}
	}
}

# Things are kinda crazy, so we're going to functionalize some things.

sub create_csr {
	my $yn = "";
	common_name();
	if ( -e "$working_dir/active/$cn.crt"){
		print "$cn already has a key. Creating another one will overwrite the existing key.\n";
		do {
			print "$cn already has an active key.  Do you want to overwrite? (y/n): ";
			chomp($yn = <>);
		} until $yn =~ m/^[yn]$/;
		if ($yn eq "n"){
			common_name();
		}
	}
	do {
		print "Would you like to password protect the private key (y/n): ";
		chomp($yn = <>);
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y") {
		system("cd $working_dir && openssl req -new -keyout $cn.key -out $cn.csr -config $key_config -batch -extensions v3_req");
	} elsif ($yn eq "n") {
		system("cd $working_dir && openssl req -nodes -new -keyout $cn.key -out $cn.csr -config $key_config -batch -extensions v3_req");
	}
}

sub sign_csr {
	update_serial();
	print "===> Serial Number = $curr_serial\n";

	if ($intermediate eq "NO"){
		print "=========> Signing request for $cn\n";
		`cd $working_dir && openssl ca -config $key_config -days $key_days -out $cn.crt -in $cn.csr -batch -extensions v3_req`;
	} elsif ($intermediate eq "YES"){
		print "=========> Signing new Intermediate CA request for $cn\n";
		`cd $working_dir && openssl ca -config $key_config -policy policy_new_ca -out $cn.crt -extensions v3_ca -infiles $cn.csr -batch`;
	}
	if ($? != 0){ die "There was an error during openssl execution.  Please look for error messages above."; }
	print "=========> Moving certificates and keys to $working_dir/active for production.\n";
	system("mv $working_dir/$cn.crt $working_dir/active/");
	system("cp $working_dir/$cn.key $working_dir/csr/");
	system("mv $working_dir/$cn.key $working_dir/active/");
	system("mv $working_dir/active/$curr_serial.pem $working_dir/active/$cn.pem");
	my $yn = "";
	do {
		print "Can I move signing request ($cn.csr) to the csr directory for archiving? (y/n): ";
		if ($menu_item != 4){
			chomp($yn = <>);
		} else {
			$yn = 'y';
		}
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y"){
		`mv $working_dir/$cn.csr $working_dir/csr/`;
		print "===> $cn.csr moved.\n"
	} else { print "You will need to move $working_dir/$cn.csr to $working_dir/$cn.csr manually!"; } 
}
sub new_ca {
	my $yn;
	common_name();
	print "\n\n===> Creating private key with $key_size bits and generating request.\n";
	do {
		print "Do you want to password protect your CA private key? (y/n): ";
		chomp($yn = <>);
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y") {
		system("cd $working_dir && openssl genrsa -des3 -out $cn.key $key_size");
	} else {
		system("cd $working_dir && openssl genrsa -out $cn.key $key_size");
	}
	print "===> Self-Signing request.\n";
	system("openssl req -new -x509 -extensions v3_ca -key $working_dir/$cn.key -out $working_dir/$cn.crt -days $key_days -config $key_config -batch");
	if ($? != 0){ die "OpenSSL exited with errors.  Please read above and address the problems indicated."; }
	print "===> Moving certficate and key to appropriate directory.\n";
	system("mv $working_dir/$cn.key $working_dir/active/ca.key");
	system("mv $working_dir/$cn.crt $working_dir/active/ca.crt");
}

sub create_server {
	my $yn = "";
	common_name();
	if ( -e "$working_dir/active/$cn.crt"){
		print "$cn already has a key.  Creating another one will overwrite the existing key.\n";
		do {
			print "$cn already has an active key.  Do you want to overwrite? (y/n): ";
			chomp($yn = <>);
		} until $yn =~ m/^[yn]$/;
		if ($yn eq "n") {
			$cn_o = 1;
			project_info();
		}
	}
	do {
		print "Would you like to password protect the private key (y/n): ";
		chomp($yn = <>);
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y") {
		system("cd $working_dir && openssl req -extensions server -new -keyout $cn.key -out $cn.csr -config $key_config -batch");
	} elsif ($yn eq "n"){
		system("cd $working_dir && openssl req -extensions server -nodes -new -keyout $cn.key -out $cn.csr -config $key_config -batch");
	}
}
sub sign_server {
	update_serial();
	print "===> Serial Number = $curr_serial\n";
		`cd $working_dir && openssl ca -config $key_config -extensions server -days $key_days -out $cn.crt -in $cn.csr -batch`;
	if ($? != 0){ die "There was an error during openssl execution.  Please look for error messages above."; }
	print "=========> Moving certificates and keys to $working_dir/active for production.\n";
	system("mv $working_dir/$cn.crt $working_dir/active/");
	system("cp $working_dir/$cn.key $working_dir/csr/");
	system("mv $working_dir/$cn.key $working_dir/active/");
	system("mv $working_dir/active/$curr_serial.pem $working_dir/active/$cn.pem");
	my $yn = "";
	do {
		print "Can I move signing request ($cn.csr) to the csr directory for archiving? (y/n): ";
		chomp($yn = <>);
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y"){
		`mv $working_dir/$cn.csr $working_dir/csr/`;
		print "===> $cn.csr moved.\n"
	} else { print "You will need to move $working_dir/$cn.csr to $working_dir/$cn.csr manually!\n"; 
			 print "Remember that if you do not keep your server .csr you will need to build a new CA\n";
			 print "if your server cert gets comprimised.\n";
	} 
}

sub gen_dh {
	# This function generates the Diffie Hellman parameters
	# openssl dhparam -out ./dh1024.pem 1024
	system("openssl dhparam -out $working_dir/dh$key_size.pem $key_size");
	print "Your Diffie Hellman parameters have been created.";
}

# System Menu

sub main_menu {
	update_serial();
	print "\n\n=====================================================\n";
	print '#               SSL-ADMIN v~~~VERSION~~~                    #';
	print "\n=====================================================\n";
	print "Please enter the menu option from the following list:\n";
	print "1) Update run-time options:\n";
	print "     Key Duration (days): $key_days\n";
	print "     Current Serial #: $curr_serial\n";
	print "     Key Size (bits): $key_size\n";
	print "     Intermediate CA Signing: $intermediate\n";
	print "2) Create new Certificate Request\n";
	print "3) Sign a Certificate Request\n";
	print "4) Perform a one-step request/sign\n";
	print "5) Revoke a Certificate\n";
	print "6) Renew/Re-sign a past Certificate Request\n";
	print "7) View current Certificate Revokation List\n";
	print "8) View index information for certificate.\n";
	print "i) Generate a user config with in-line certifcates and keys.\n"; 
	print "z) Zip files for end user.\n";
	print "dh) Generate Diffie Hellman parameters.\n";
	print "CA) Create new Self-Signed CA certificate.\n";
	print "S) Create new Signed Server certificate.\n";
	print "q) Quit $program_name\n\n";
	print "Menu Item: ";
	chomp($menu_item = <>);
	menu_handler();
}

# Menu Handler

sub menu_handler {
	if ($menu_item eq "1"){
		$new_runtime = 1;
		project_info();
		print "Run-time options reconfigured.\n\n\n";
		$new_runtime = 0;
		main_menu();

### CREATE CERT MENU

	} elsif ($menu_item eq "2"){
		common_name();
		create_csr();

### SIGN CERT MENU

	} elsif ($menu_item eq "3"){
		common_name();
		sign_csr();

### CREATE/SIGN MENU

	} elsif ($menu_item eq "4"){
		common_name();
		create_csr();
		sign_csr();

### REVOKE MENU

	} elsif ($menu_item eq "5"){
		common_name();
		my $yn = "";
		print "=========> Revoking Certificate for $cn\n";
		do {
			print "We're going to REVOKE an SSL certificate.  Are you sure? (y/n): ";
			chomp($yn = <>);
		} until $yn =~ m/^[yn]$/;
		if ($yn eq "n"){ main_menu(); }
		my $revoke = `openssl x509 -noout -text -in $working_dir/active/$cn.crt | grep Serial`;
		$revoke =~ m/Serial Number: ([A-F0-9]+)/;
		$revoke = $1 or warn("Certificate doesn't seem valid.");
		print "\n \$revoke = $revoke\n";
		`cd $working_dir/active && openssl ca -revoke $working_dir/active/$cn.crt -config $key_config -batch`;
		print "=========> Generating new Certificate Revokation List $crl\n";
		`cd $working_dir/active && openssl ca -gencrl -out $crl -config $key_config`;
		print "=========> Verifying Revokation: ";
		my $check_revoke = `openssl crl -noout -text -in $crl | grep Serial`;
		my $check_status = 0;
		while ($check_revoke =~ m/Serial Number: ([A-F0-9]+)/g){
			if (hex $revoke == hex $1){
				$check_status = 1;
			}
		}
		if ($check_status){
			print "SUCCESS!\n";
		} else {
			print "ERRORS\n";
		}
		print "=========> Moving $cn\'s files to $working_dir/revoked\n";
		my @exts = ('csr', 'pem', 'crt', 'key');
		foreach (@exts){
			move("$working_dir/active/$cn.$_", "$working_dir/revoked//$cn.$_") unless (! -e "$working_dir/active/$cn.$_");
		}
		print "=========> Destroying previous packages built for $cn: ";
		unlink "$working_dir/packages/$cn.ovpn", "$working_dir/packages/$cn.zip";
		print "DONE\n";
		print "=========> CSR for all users is in $working_dir/csr\n";
		print "===============> Changing file name for $cn\'s request to *.revoked";
		move("$working_dir/csr/$cn.csr", "$working_dir/csr/$cn.csr.revoked");
		sleep 3;

### RE-SIGN/RENEW MENU

	} elsif ($menu_item eq "6"){
		$cn_o = 1;
		common_name();
		my $yn;
		if ( -e "$working_dir/csr/$cn.csr"){
			print "======> Moving archived request to working directory.";
			system("mv $working_dir/csr/$cn.csr $working_dir");
			system("mv $working_dir/csr/$cn.key $working_dir");
			sign_csr();
		} elsif ( -e "$working_dir/csr/$cn.csr.revoked"){
			print "\n\nThe certificate you're trying to renew has been revoked!\n";
			do {
				print "Are you sure you want to re-sign/renew this certficate? (y/n): ";
				chomp($yn = <>);
			} until $yn =~ m/^[yn]$/;
			if ($yn eq "n") { main_menu(); }
			else {
				system("mv $working_dir/csr/$cn.csr.revoked $working_dir/$cn.csr");
				system("mv $working_dir/csr/$cn.key $working_dir/$cn.key");
				sign_csr();
			}
		} else { 
			print "There is no request in the archive for $cn.\n"; 
		}
		sleep 2; 

### View Current CRL MENU

	} elsif ($menu_item eq "7"){
		if (! -e $crl){ 
			system("cd $working_dir/active && openssl ca -gencrl -config $key_config -out $crl -batch");
		}
		system("openssl crl -text -noout -in $crl");
		sleep 3;


### Read INDEX Menu

	} elsif ($menu_item eq "8"){
		common_name();
		system("more $working_dir/prog/index.txt | grep $cn");
		sleep 3;

### Create a config file with inline certifcates and keys.  If the config has
### options for the inline files, remove them

	} elsif ($menu_item eq "i"){
		
		my $yn;
		common_name();
		print "========> Creating in-line configuration for $cn in $working_dir/packages\n";
		open TEMPLATECONF, "<", "$working_dir/packages/client.ovpn" or die $!;
		open NEWCONF, "+>", "$working_dir/packages/$cn.ovpn" or die $!;
		my @files = ("ca.crt", "$cn.crt", "$cn.key", "ta.key");
		# read the template, get rid of the cert, key, and ca files
		while ($_ = <TEMPLATECONF>){
			if (/^[\t\s]*(cert|ca|key|tls-auth)[\t\s]+/){
				# we don't want to write these to the new config
			} else {
				print NEWCONF $_;
			}
		}
		# we've written our config, now lets write the certs
		my $loop=0;
		foreach my $file (@files){
			-e "$working_dir/active/$file" or next;
			open CERT, "<", "$working_dir/active/$file" or die $!;
			my $started = 0;
			my $certificate;
			while ($_ = <CERT>){
				# see if we've already started
				if ($started){
					$certificate .= $_;
				}
				# look for certificate, store it in memory
				if (/^-{5}BEGIN/){
					# start of cert
					$certificate .= $_;
					$started = 1;
				}
				# look for the end
				if (/^-{5}END/){
					# this is the end, do the right thing
					if ($loop == 0){
						print NEWCONF "\n<ca>\n$certificate</ca>";
					} elsif ($loop == 1){
						print NEWCONF "\n<cert>\n$certificate</cert>";
					} elsif ($loop == 2) {
						print NEWCONF "\n<key>\n$certificate</key>";
					} elsif ($loop == 3) {
						print NEWCONF "\n<tls-auth>\n$certificate\n</tls-auth>";
					}
					next;
				}
			}
			$loop++;
		}
		close TEMPLATECONF;
		close NEWCONF;
		print "======> Inline configuration available at $working_dir/packages/$cn.ovpn\n";

### Create ZIP FILE Menu

	} elsif ($menu_item eq "z"){
		my $yn;
		common_name();
		print "=========> Creating .zip file for $cn in $working_dir/packages\n";
		print "=================> Moving $cn.crt\n";
		`cp $working_dir/active/$cn.crt $working_dir/packages/client.crt`;
		print "=================> Moving $cn.key\n";
		`cp $working_dir/active/$cn.key $working_dir/packages/client.key`;
		do {
			print "Is this certificate for an OpenVPN client install? (y/n): ";
			chomp($yn = <>);
		} until $yn =~ m/^[yn]$/;
		if ($yn eq "n"){ 
			$zip_cmd = "cd $working_dir/packages/ && zip $cn.zip client.crt client.key ca.crt";
		} else { 
			$zip_cmd = "cd $working_dir/packages/ && zip $cn.zip client.crt client.key ca.crt client.ovpn";
		}
		print "=================> Zipping File\n";
		system($zip_cmd);
		print "=================> Cleaning up files: ";
		`rm $working_dir/packages/client.crt`;
		print "client.crt, ";
		`rm $working_dir/packages/client.key`;
		print "client.key.\n";
		print "\nYou may distribute $working_dir/packages/$cn.zip to the end user.\n";
		sleep 3;

	} elsif ($menu_item eq "dh"){
		gen_dh();
### CREATE NEW SELF-SIGNED CA CERTIFICATE
	} elsif ($menu_item eq "CA"){
		new_ca();
### CREATE NEW SIGNED SERVER CERTIFICATE
	} elsif ($menu_item eq "S"){
		common_name();
		create_server();
		sign_server();
	} elsif ($menu_item eq "q"){
		exit 0;
	}
}
# Software header/introduction
#
if ( ! -e "$working_dir"){
	print "$working_dir doesn't exist.  Is the variable set correctly?\n";
	exit 1;
}
print "This program will walk you through requesting, signing,\norganizing and revoking SSL certificates.\n\n";
if ($> != 0){
	if ($build eq "devel"){
		print "\n\nRunning devel build - this isn't secure!\n\n";
	} else {
		die "Sorry, but I need to be run as the root user.\n\n";
	}
}

if ( ! -e "$working_dir/prog/install"){

	if ( ! -e "$working_dir/active"){
		my $yn;
		print "Looks like this is a new install, installing...\n";
		do {
			print "You will first need to edit the /usr/local/etc/ssl-admin/ssl-admin.conf\n";
			print "default variables.  Have you done this? (y/n): ";
			chomp($yn = <>);
		} until $yn =~ m/^[yn]$/;
		if ($yn ne "y") { die "Please edit /usr/local/etc/ssl-admin/ssl-admin.conf and run me again." }
		mkdir "$working_dir/active", 0750;
		mkdir "$working_dir/revoked", 0750;
		mkdir "$working_dir/csr", 0750;
		mkdir "$working_dir/packages", 0750;
		mkdir "$working_dir/prog", 0750;
	}
	my $yn;
	do {
		print "I need the CA credentials.  Would you like to create a new CA key and\n";
		print "certificate now?  (y/n): ";
		chomp($yn = <>);
	} until $yn =~ m/^[yn]$/;
	if ($yn eq "y"){
		new_ca();
		if ( ! -e "$working_dir/index.txt"){
			open(FILE, ">$working_dir/prog/index.txt");
			close(FILE);
			open(FILE, ">$working_dir/prog/index.txt.attr");
			print FILE "unique_subject = no";
			close(FILE);
			open (FILE, ">$working_dir/prog/serial");
			print FILE "01";
			close(FILE);
		}
	}
	else {
		if ( ! -e "$working_dir/active/ca.crt"){ 
			my $ca_cert;
			print "I need your ca.crt file. Please enter path and filename so I can have a copy: \n";
			print "Location: ";
			chomp($ca_cert = <>);
			if (-e $ca_cert){
				system("more $ca_cert | grep -q \"BEGIN CERTIFICATE\"");
				if ($? != 0){ die "$ca_cert doesn\'t look like a certificate." }
				system("more $ca_cert | grep -q \"CA:TRUE\"");
#				if ($? != 0){die "$ca_cert looks valid, but isn't authorized to sign other certificates." }
				system("cp $ca_cert $working_dir/active/ca.crt");
				system("cp $ca_cert $working_dir/packages/ca.crt");
			} else { die "File $ca_cert doesn\'t exist.  Please locate the file and try again.\n"; }
		}
		if ( ! -e "$working_dir/active/ca.key"){
			my $ca_key;
			print "I need your ca.key file.  Please enter path and filename so I can have a copy: \n";
			print "Location: ";
			chomp($ca_key = <>);
			if (-e $ca_key){
				system("more $ca_key | grep -q \"BEGIN RSA PRIVATE KEY\"");
				if ($? != 0 ){ die "$ca_key doesn\'t look like a private key." }
				system("cp $ca_key $working_dir/active/ca.key");
			} else { die "File $ca_key doesn\'t exist.  Please locate the file and try again.\n"; }
		}
		print "If you have an existing index.txt or index.txt.attr, you need to manually\n";
		print "place them in the $working_dir/prog/ directory.\n";
		do {
			print "What serial number shall I start with? [01]: ";
			chomp($serial = <>);
		} until $serial =~ m/^[A-F0-9]+$/;
		open (FILE, ">$working_dir/prog/serial");
		print FILE "$serial";
		close(FILE);
	}
	if ( ! -e "$working_dir/index.txt"){
		open(FILE, ">$working_dir/prog/index.txt");
		close(FILE);
		open(FILE, ">$working_dir/prog/index.txt.attr");
		print FILE "unique_subject = no";
		close(FILE);
		open (FILE, ">$working_dir/prog/serial");
		print FILE "01";
		close(FILE);
	}
	system("echo `date` > $working_dir/prog/install");
}
if (! -e $crl){
	print "===> Creating initial CRL.";
	system("cd $working_dir/active && openssl ca -gencrl -batch -config $key_config -out $crl");
}
my $install_date = `cat $working_dir/prog/install`;
print "ssl-admin installed $install_date";
update_serial();

# Make sure packaged ca.crt is up to date.
system("cp $working_dir/active/ca.crt $working_dir/packages/");
if ( ! -e "$working_dir/packages/client.ovpn"){
	print "OPTIONAL: I can't find your OpenVPN client config.  Please copy your config to\n$working_dir/packages/client.ovpn";
}
while ($menu_item ne "q"){
	main_menu();
}
