#!/bin/sh # # This is a shell archive. To extract its contents, # execute this file with /bin/sh to create the file(s): # # README su_ontape.c # # This shell archive created: Wed Oct 2 10:50:32 EDT 1996 # echo "Extracting file README" sed -e 's/^X//' <<\SHAR_EOF > README X su_ontape X X XI wrote this program because I am fortunate enough to work for a company with Xa full-time operations staff, but wary enough to not want to give them the Xfull power of the "informix" login. Prior to writing this, I had called XInformix asking how, other than using onarchive, I could allow others to Xperform database archives and log backups. They suggested writing a setuid Xprogram to switch to "informix" and then run ontape from there. XUnfortunately, they could offer no advice on how to write such a program. X X XFinally, someone told me about a public domain program called sudo. After Xinvestigating sudo, I decided that it was not what I wanted, because it Xgrants access for some period of time. I wanted access granted for exactly Xthe duration of the Informix utility program. Also, as noted in the Xdocumentation for sudo, if you allow someone to run a script as super-user, Xthey may be able to halt the script and still retain super-user status. I Xwould have needed a script for my situation to insure environment variables Xare set, etc. X X XTo minimize this security exposure, su_ontape performs an exec() call, which Xloads Informix's ontape program directly on top of su_ontape, rather than Xspawning another child process. This should prevent anyone from breaking out Xof the program and still retaining the privileges of "informix". When the Xprogram is running, a "ps -ef|grep ttyxx" will show something like: X X mcollins 20813 20812 0 08:46:03 ttyp7 0:00 ksh X informix 21911 20813 0 10:03:09 ttyp7 0:00 /usr/informix/bin/ontape -s -L 0 X Xproving that ontape has indeed overlaid su_ontape and the user has "informix" Xprivileges only within the one process. Once that process goes away, when Xontape terminates, they return to their own id. X X XThis program is designed to be run exactly the same as ontape. It expects Xthe same environment variables to be set (with the possible exception of XONCONFIG). It also accepts any command line arguments you wish to pass. XThese arguments are tested against a list of acceptable arguments, to verify Xthat no one other than "informix" attempts a database restore, for example. XThere is no attempt to verify the correctness of the arguments. I leave that Xto ontape. This should minimize the maintenance su_ontape will require in Xthe event that Informix adds or changes ontape's arguments. X X XYou can run su_ontape from the command line or create a shell script to set Xenvironment variables prior to running the program. A simple example would Xbe: X #!/bin/sh X export INFORMIXDIR=/usr/informix X export INFORMIXSERVER=dbservername X export ONCONFIG=onconfig.dbserver X su_ontape -s -L 0 X if [ $? -ne 0 ] X then X echo "Problem with database backup"|mail informix X fi X X XI know of some changes you may want to make to the program. For example, you Xmight want to take out the test in check_env_vars() for the environment Xvariable ONCONFIG. I doubt it is needed by ontape, but I already had it in Xthere and didn't feel like taking it out. You may also want to customize Xwhat command-line arguments you are willing to accept. Perhaps you want to Xallow ANYONE to restore from an archive. It's your database. You may have Xto change the setresuid() call in main() to be a setuid() call instead. Some Xversions of Unix do not support setresuid(). X X XBy making a few modifications in create_command_line() and check_args(), you Xcan modify the program to call onmode, oninit, or any number of other Xprograms. One twist I found (and it is documented in the man page for exec() Xon HP), if you try to execute a shell script rather than an executable Xprogram, you MUST have "#!/bin/sh" or its equivalent as the first line. As Xan alternative, you could create a command line that performed "sh script Xarg1 arg2 ...". Unfortunately, running a script from this program has the Xsame security concerns as those discussed above for sudo. X X XThis program has only been tested with Informix 7.10 on HP-UX 9.04. I have Xtried to provide reasonable documentation within the program, but it is not Xmuseum-quality code. If you make any major enhancement to the program's Xfunctionality, I would appreciate seeing a copy. X X XNow that you've read through all of this, here's how to create the executable Xon HP-UX. X X log in as "informix" X compile the program X cc -Ae -o su_ontape su_ontape.c X set permissions X chmod 4111 su_ontape X check permissions X ---s--x--x informix informix ... su_ontape X X Xmcollins@us.dhl.com Xiiug #2860 SHAR_EOF if [ `wc -c < README` -ne 4581 ] then echo "Lengths do not match -- Bad Copy of README" fi echo "Extracting file su_ontape.c" sed -e 's/^X//' <<\SHAR_EOF > su_ontape.c X/*----------------------------------------------------------------------------- XModule: su_ontape.c XAuthor: mark collins XDate: 07/17/96 XComments: X This program allows anyone with execute authority to run the Informix X "ontape" utility. Normally, this utility must be run by either "root" X or "informix" user. The program constructs a full path reference to X the "ontape" executable. It then searches the user database (/etc/passwd) X to find the entry for user "informix" and switches to that user. X Finally, it performs execv() to perform the "ontape" command. It X passes any command line arguments to ontape. X X For this to work, the executable of this program must be owned by X user "informix" and permissions must be set to allow set-user X (e.g., -rwsrwxrwx). X X For those systems which do not support the setresuid() call, modify X main() to use the older setuid() call instead. For details, see X your man pages. X X With only a few changes in create_command_line() and check_args(), X this program can be used to invoke onmode, oncheck, oninit, or many X non-Informix utilities. X X XContents: X main() X check_args() X check_env_vars() X create_command_line() X print_error() X X XWarnings: X This program has only been tested on Informix 7.10 running on HP-UX 9.04. X No other platforms have been tested. No support is provided for any X platforms. X X Each installation will need to evaluate the list of allowed options in X check_args() for suitability to their own situation. X X---------------------------------------------------------------------------- */ X X#include X#include X#include X#include X#include X#include X Xextern char **environ; X X X X/*----------------------------------------------------------------------------- X function prototypes X (I know, I know. These should be in a "*.h" file. Sue me.) X---------------------------------------------------------------------------- */ Xint check_args(char **argv); Xint check_env_vars(void); Xint create_command_line(char *cmd_line); Xvoid print_error(char *error_msg); X X X X/*----------------------------------------------------------------------------- X Function: main X Author: mark collins X Date: 07/17/96 X Arguments: argc integer counter of arguments passed to the program, X argv a null-terminated list of pointers to character arrays X holding the arguments passed to the program X Decription: X Return Value: none. Since this program performs an execv(), the X return code is actually that from ontape. X---------------------------------------------------------------------------- */ Xmain(int argc, char** argv) X X{ X char user_name[] = "informix"; X char root_user_name[] = "root"; X char command_line[119]=""; X uid_t informix_uid; X uid_t root_uid; X uid_t current_uid; X X struct passwd *password_entry; X X/* */ X/* Check environemt variables. If they are not set, get out */ X/* now. */ X/* */ X if (check_env_vars ()) X return 1; X X/* */ X/* try to create the full path to "ontape". If any errors, */ X/* get out now. */ X/* */ X if (create_command_line (command_line)) X return 1; X X/* */ X/* lookup user entry for "root" in /etc/passwd */ X/* */ X password_entry = getpwnam(root_user_name); X root_uid = password_entry->pw_uid; X X/* */ X/* lookup user entry for "informix" in /etc/passwd */ X/* */ X password_entry = getpwnam(user_name); X informix_uid = password_entry->pw_uid; X X/* */ X/* get uid for current user */ X/* */ X current_uid = getuid(); X X/* */ X/* If you're already "root" or "informix", bypass */ X/* check_args() and setresuid(). Assume that these guys */ X/* know what args to use. Why would they use this and not */ X/* just go straight to ontape? Maybe this is being called */ X/* from a script. */ X/* */ X if ((current_uid != informix_uid) && X (current_uid != root_uid)) X { X X /* */ X /* Check command line arguments. If a forbidden value */ X /* is entered, quit. */ X /* */ X if (check_args (argv)) X return 1; X X /* */ X /* change real, effective, and saved user id to */ X /* "informix" */ X /* */ X setresuid(informix_uid, X informix_uid, X informix_uid); X } X X/* */ X/* reset argv[0] to be equal to the full-path description */ X/* of "ontape". Without this, ontape will receive argv[0] */ X/* equal to the name used to invoke this program. This may */ X/* or may not be important, so I did it just to be safe. */ X/* */ X argv[0] = command_line; X X/* */ X/* execv() to the "ontape" utility, passing command-line */ X/* arguments. The execv() will cause ontape to overlay */ X/* this executable in memory, rather than create a new */ X/* shell. This should minimize (hopefully, eliminate) */ X/* the risk of the newly-empowered user being able to use */ X/* his/her god-like powers improperly. */ X/* */ X execv(command_line, argv); X X/* */ X/* If we get to this point, the execv() did not work. */ X/* Display an error message and get out. */ X/* */ X print_error("Unable to execv(). Call DBA."); X return 1; X} X X X/*----------------------------------------------------------------------------- X Function: check_args X Author: mark collins X Date: 08/07/96 X Arguments: none X Decription: X X Test each of the command line arguments. If any forbidden values X are encountered, quit. Alternatively, it might be simpler just to X check for the allowed values and quit if anything other than those X were passed. X X For ontape, do not allow the following arguments: X -r - restore from archive X -l - perform logical restore X -p - perform physical restore to HDR X -N - change logging status to no logging X -B - change logging status to buffered logging X -U - change logging status to unbuffered logging X -A - change logging status to ANSI X X For ontape, allow the following arguments: X -s - perform archive as specified by accompanying args X -L - level of archive (used with -s, above) X -a - perform logical log backup X -c - perform continuous logical log backup X X Return Value: int - 0 if successful, 1 if error X---------------------------------------------------------------------------- */ X Xint check_args(char **argv) X{ X X int i,j; X X for (i = 1; argv[i]; i++) X { X /* */ X /* if first char of arg is '-', this is a switch for */ X /* ontape. Check each char in string. */ X /* */ X if (argv[i][0] == '-') X { X for (j = 1 ; argv[i,j]; j++) X { X if (argv[i][j] == 'r' \ X || argv[i][j] == 'l' \ X || argv[i][j] == 'p' \ X || argv[i][j] == 'N' \ X || argv[i][j] == 'B' \ X || argv[i][j] == 'U' \ X || argv[i][j] == 'A' \ X ) X { X print_error("Must be user \"informix\" or \"root\" to use these command line options"); X return 1; X } X } X } X } X X return 0; X} X X X/*----------------------------------------------------------------------------- X Function: check_env_vars X Author: mark collins X Date: 08/01/96 X Arguments: none X Decription: X X Test $INFORMIXSERVER and $ONCONFIG. This just checks to see if X they are set, it performs no validity testing. I'm not sure if X $ONCONFIG is actually required, may remove it later. $INFORMIXDIR X is tested in create_command_line(), so I do not bother with it X here. X X Return Value: int - 0 if successful, 1 if error X---------------------------------------------------------------------------- */ X Xint check_env_vars(void) X{ X char env_var[99]; X X/* */ X/* Check a couple of environment variables. If they are */ X/* not set, present error message and return to calling */ X/* statement. */ X/* */ X strcpy(env_var,getenv("INFORMIXSERVER")); X if (strlen(env_var) == 0) X { X print_error("Unable to read environment variable: INFORMIXSERVER"); X return 1; X } X strcpy(env_var,getenv("ONCONFIG")); X if (strlen(env_var) == 0) X { X print_error("Unable to read environment variable: ONCONFIG"); X return 1; X } X return 0; X} X X X X/*----------------------------------------------------------------------------- X Function: create_command_line X Author: mark collins X Date: 07/17/96 X Arguments: cmd_line - pointer to character array X Decription: X X It might be nice to add a stat() of the resulting cmd_line. This X could verify that the file is owned by "informix" and has proper X permissions. This is not required, since the ontape will fail if X these are not correct, thus someone will bring this to your X attention. X X Return Value: int - 0 if successful, 1 if error X---------------------------------------------------------------------------- */ X Xint create_command_line(char *cmd_line) X{ X char dbdir[99]; X int char_num=0; X X/* */ X/* Check value of $INFORMIXDIR. If it is not set, present */ X/* error message and return to calling statement. */ X/* */ X strcpy(dbdir,getenv("INFORMIXDIR")); X if (strlen(dbdir) == 0) X { X print_error("Unable to read environment variable: INFORMIXDIR"); X return 1; X } X strcat(cmd_line,dbdir); X char_num = strlen(cmd_line) - 1; X X/* */ X/* Does $INFORMIXDIR end with a "/"? */ X/* */ X if (cmd_line[char_num] != '/') X strcat(cmd_line,"/bin/ontape"); X else X strcat(cmd_line,"bin/ontape"); X X return 0; X} X X X X/*----------------------------------------------------------------------------- X Function: print_error X Author: mark collins X Date: 07/17/96 X Arguments: error_msg - pointer to character array X Decription: X Return Value: void X---------------------------------------------------------------------------- */ X Xvoid print_error(char *error_msg) X{ X fprintf(stderr,"\nERROR! ERROR! ERROR! ERROR! ERROR!\n"); X fprintf(stderr,"\t%s\n", error_msg); X} SHAR_EOF if [ `wc -c < su_ontape.c` -ne 9997 ] then echo "Lengths do not match -- Bad Copy of su_ontape.c" fi echo "Done." exit 0