Thank you very much to all of you who have responded and helped me
with my problem.  I was warned again and again not to do setuid scripts
or code, and that I should use sudo instead.
I'm definitely going to install sudo in the very near future.  But 
for those who are interested in a solution of running c code with
setuid bit, there must be a line like "setuid(0);" in the code, in
addition to chmod 4111 on the executable.
Since I've received many responses, and some of those are pretty lengthy 
(thank you), I'll just pick out the major info that I found helpful and
place them randomly in the Solutions section.
My heartfelt THANKYOU to (sorry if I missed anybody's name):
"No Name"--per your wish
D. Stew McLeod
seanw@amgen.com
Jason marshall
Steve Franks
Francois Leclerc
Oscar Goldes
rsk@itw.com
Mariel Feder
Craig Robertson
Peter M Allan
Neal S. Pressman
Jens Fischer
Brian O'Mahoney
Steve Bigley
Jay a. Cohen
DonWilliams
Tim Carlson
Bobby Grover
Bryan Hodgson
Shriman Gurung
===========================================================================
Original question:
We want to allow any regular users to perform some daily tasks which
require root privilege, like mounting a file system from their removable
cartridge drives or copying some files to /tftpboot directory so that they
could download their code to another CPU (Motorola PPC).  I wrote some
C code to perform one of the above tasks, compiled the code, then setuid
on the executable to 4111.  However, when I (a regular user) executed the 
code, it said my euid (effective uid) is 0--root, but the command failed
with "permission denied."
OS:  Solaris 2.5.1
Platform:  tested on Ultra Enterprise II and SPARCstation 20
I'm enclosing a piece of c code to copy a file to /tftpboot.  After I 
compiled the code, I did a "chmod 4111" (or chmod 4755") on the executable;
then ran the code as a regular user.
The "effective uid" (euid) confirmed that I was root with my_euid=0 just
before the "cp" command.  However, it fails on the "cp" command because 
the target directory /tftpboot is owned by root:other and the protection 
is 755 (or any directory like /tmp/vicky with the same ownership and 
protection).
The same code, converted to ksh script with the above file ownership
and protections (root:other and 4111) and above ownership and protections on 
/tftpboot (root:other and 755) runs fine--the regular user can
copy a file to /tftpboot directory using the ksh script.
QUESTION:  How to set setuid on c executable to allow a regular user to
run a single or multiple commands that require root privilege?
Thank you for your help.
Vicky Lau
vlau@msmail2.hac.com
(714) 446-3077
<<<<<< Attached TEXT file named "mvbootx.c" follows >>>>>>
/****************************************************************************/
/*                                                                          */
/* File Name:  mvbootx.c                                                    */
/* Date:       August 25, 1997                                              */
/*                                                                          */
/*                                                                          */
/* Purpose:    Allows any user to copy /user1/jvill/boot.x code to          */
/*             /tftpboot directory.  This code is setuid to root.           */
/*                                                                          */
/* History:                                                                 */
/*                                                                          */
/****************************************************************************/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* #define BOOTX_PATH "/user1/jvill/boot.x"
#define BOOTX_FILE "boot.x" */
#define BOOTX_PATH "/user1/vlau/vicky.x"
#define BOOTX_FILE "vicky.x"
#define OPENMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
                                        /* define open file mode with r/w permission
                                           for user and group */
int sighandler();
main()
{
  char cmd[80];
  int bootx_fd;				/* bootx file descriptor for open and
                                           write functions */
  int status;				/* return status */
  uid_t my_euid;			/* tet euid (effective uid) */
  /* SET ERROR HANDLER TO EXIT CODE IF USER USES CONTROL_C */
  signal(SIGINT, sighandler);
sprintf(cmd,"/usr/bin/id\n");
system(cmd);
  /* Check if euid has been changed to root */
  my_euid = geteuid();
  printf("my euid = %d \n",my_euid ); 
  /* Check if file /user1/jvill/boot.x exists */
  bootx_fd=open(BOOTX_PATH, O_RDWR, OPENMODE);
  /* If can't open file--file does not exist */
  if ( bootx_fd == -1 )
  {
    printf ("File %s does not exist.  Exiting program...\n\n",BOOTX_PATH);
  }
  else
  {
    my_euid = geteuid();
    printf("my 2 euid = %d \n",my_euid ); 
    sprintf(cmd,"/usr/bin/cp %s /tftpboot",BOOTX_PATH);
    status=system(cmd);
    if ( status != 0 )			/* if return status is no good */
    {
      /* EXIT WITH ERROR */
      printf("\nCannot copy file %s to /tftpboot--check with your System
Administrator.\n",BOOTX_PATH);
      exit(1);
    }
    else
    {
      /* CHANGE FILE PROTECTIONS TO "read for all" */
      sprintf(cmd,"/usr/bin/chmod 444 /tftpboot/%s",BOOTX_FILE);
      status=system(cmd);
      if ( status != 0 )		/* if return status is bad */
      {
        /* EXIT WITH ERROR */
        printf("\nCannot change file protection on /tftpboot/%s--check with
your System Administrator.\n",BOOTX_FILE);
        exit(1);
      }
    }
    exit(0);
  }
}
  
  
int sighandler()
{
  printf("\nDetected ^C...exiting program.\n");
  exit(1);
}
===========================================================================
Solutions:
sudo
http://www.courtesan.com/courtesan/products/sudo/
ftp.cs.colorado.edu:/pub/sysadmin/sudo
http://ww.cs.colorado.edu/~millert/sudo
------------------
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>             /* for execXX */
main()
{
   setuid(geteuid()); 
   setuid(geteuid()); 
   execvp("/usr/local/bin/interleaf_license_killer","");
   printf("error\n\n");
}
try using execve() instead of system()
------------------
>     sprintf(cmd,"/usr/bin/cp %s /tftpboot",BOOTX_PATH);
>     status=system(cmd);
The problem is that the setuid status is not surviving the 'exec'
that is part of the 'system' call.
This is often controlled by a flag to the 'execve' system call that
you can't pass through the 'system' interface. Look at the public domain
utility 'sudo'.
Also you need to check if the real uid/gid can access the file -
else you create a huge security breach.
Having done an 'accesss' call to do that I would just copy the
data in your program. A lot more efficient than forking a new copy
of sh to parse and execute your cp command.
------------------
Setting the bit is not enough, you need to call setuid(0) too.
You may wish to grab the current uid of the
user so you can setuid(old_id) when you're done with the stuff that needs
root access.  Or, if the program is really simple you can become root
(uid=0; euid=0) and stay root until the program ends and root privs are
relinquished.  Be careful with this latter, though; it can get you in a
lot of trouble if you do non-priviileged stuff while you're still root!
you need to set your real UID in your program with setreuid,
e.g.:
   if(setreuid(geteuid(),geteuid()) != 0) {
      printf("Cannot change UID!\n");
      exit(1);
      }
------------------
to strengthen your code, you may want to read 
"Practical Unix and Internet Security" from Spafford & Garfinkel
Chapter 23 "Writing Secure SUID and Network Programs"
available at http://www.ora.com
In particular, system and sprintf are nice entry holes for hackers
man seteuid
man setuid
man exec
man snprintf
------------------
>From the man pages of the system command:
> NOTES
>      system() will fail to execute setuid() or setgid() if either
>      the uid or gid of the application's owner/group is less than
>      100.  (see useradd(1M) and setuid(2)).
------------------
You have the right euid in the program, but you then
call system() and get through to csh.  csh then refuses
to honour the setuid-ness and so no copy takes place.
sh and ksh don't do that.
------------------
You could look at sudo, or mtools.
There's also the rlprm approach, which I like,
but then I would.
At the end of this mail I'm including a man page.
ftp.cert.org has a ton of stuff to read.
OLD SUMMARY:
Date: Tue, 3 May 1994 13:16:17 -0400
From: Chris Metcalf <metcalf@catfish.LCS.MIT.EDU>
To: sun-managers@ra.mcs.anl.gov
Subject: WARNINGS: How to mount /pcfs without root privileges ?
> How to mount /pcfs without root privileges ?
Nearly all of the answers provided here allow a user to get root access
in under a minute.
> [1] setuid scripts. 
There are several ways to hijack shell scripts.  
The most entertaining is to symlink "-i" to a shell script and then run
the resulting "-i" script to get an interactive shell; using "-b" in a
csh script header or "-" in a sh script avoids this particular approach.
(Note that Sun's csh won't let you run setuid without a -b.)
In general one can take advantage of the fact that there is a lag between
when the shell specified in the script starts, and when the shell itself
reads the script.  For example, if you create a symlink to a setuid shell
script and execute from the symlink, you can replace the symlink with
a script of your own during the window before the shell reads the script
(this usually requires careful timing and many attempts, but is likely
to succeed in the end).
In any case, many systems (though not SunOS 4.x; I don't know about 5.x)
don't perform the setuid change on scripts, which keeps people from
making these mistakes.  I believe setuid Perl scripts can be run safely,
via the taintperl binary, and even on systems that have disabled setuid
shell scripts.
> [2] setuid programs.
>    
>    This is similar to above, though more secure. 
>    Write a quick and dirty C program to do it. This way you 
>    get around the security risk of a setuid script.
In fact, the examples provided in this section are even LESS secure than
the shell scripts provided (which used csh -b scripts).
> main ()
> {
>    system ("/etc/mount /pcfs") ;
> }
Since system() invokes /bin/sh, all you need to do is set IFS to
"/", create a script of your own named "etc", and run this binary.
Your "etc" script will run as root (with arguments "mount pcfs").
Similarly, one creates a "usr" script if system() is invoked with
"/usr/etc/mount_pcfs"..., of course.
> Here is one other provided by  danny@ews7.dseg.ti.com 
> 
> ...
>       system ("mount /pcfs");
Here, of course, the user just needs to set their PATH to start with ".",
place a script named "mount" in the current directory, and run the setuid
binary; the user's "mount" script will get run as root.
> [3] PD software 
For this specific situation, mtools is almost always the right answer.
I rarely bother to mount floppies; I just mcopy and mtype to them.
In general, I recommend "sudo" or "op" for allowing users to do
well-controlled operations that require root privilege.
If you want to use a simple C program to do it, you want something
like this.  Notice that the binary does *not* use system(), but rather
the essentially uncompromisable execve(), straight to the kernel.
    /*
     * Make this binary setuid to run mount as root.
     */
    
    char *envp[] = {
            "HOME=/",
            "LOGNAME=root",
            "PATH=/usr/etc:/usr/ucb:/usr/bin",
            "SHELL=/bin/sh",
            "TERM=dumb",
            "USER=root",
            0
    };
    
    char *argv[] = { "mount", "/pcfs", 0 };
    
    main()
    {
            return execve("/usr/etc/mount", argv, envp);
    }
                        Chris Metcalf, MIT Laboratory for Computer Science
                        metcalf@cag.lcs.mit.edu   //   +1 (617) 253-7766
SETUID(7)    ENVIRONMENTS, TABLES, AND TROFF MACROS     SETUID(7)
NAME
     setuid - checklist for security of setuid programs
DESCRIPTION
     Writing a secure  setuid  (or  setgid)  program  is  tricky.
     There  are  a  number  of possible ways of subverting such a
     program.  The most conspicuous security holes occur  when  a
     setuid  program  is not sufficiently careful to avoid giving
     away access to resources it legitimately  has  the  use  of.
     Most of the other attacks are basically a matter of altering
     the program's environment in unexpected ways and  hoping  it
     will fail in some security-breaching manner.  There are gen-
     erally three categories of environment manipulation: supply-
     ing  a  legal  but unexpected environment that may cause the
     program to directly do  something  insecure,  arranging  for
     error  conditions that the program may not handle correctly,
     and the specialized subcategory of giving the program inade-
     quate resources in hopes that it won't respond properly.
     The following are general considerations  of  security  when
     writing a setuid program.
     [] The program should run with the weakest userid  possible,
        preferably one used only by itself.  A security hole in a
        setuid program running with  a  highly-privileged  userid
        can  compromise an entire system.  Security-critical pro-
        grams like passwd(1) should always have private  userids,
        to minimize possible damage from penetrations elsewhere.
     [] The result of getlogin or ttyname may  be  wrong  if  the
        descriptors  have  been  meddled with.  There is no fool-
        proof way to determine the controlling  terminal  or  the
        login name (as opposed to uid) on V7.
     [] On some systems (not ours), the setuid  bit  may  not  be
        honored if the program is run by root, so the program may
        find itself running as root.
     [] Programs that attempt to use creat for locking  can  foul
        up when run by root; use of link is preferred when imple-
        menting locking.  Using chmod for locking is  an  obvious
        disaster.
     [] Breaking an existing lock is very dangerous;  the  break-
        down  of  a  locking  protocol  may be symptomatic of far
        worse problems.  Doing so on the basis of the lock  being
        `old'  is  sometimes  necessary, but programs can run for
        surprising lengths of time on heavily-loaded systems.
     [] Care must be taken that user requests for i/o are checked
        for  permissions  using  the  user's permissions, not the
        program's.  Use of access is recommended.
Sun Release 4.1        Last change: local                       1
SETUID(7)    ENVIRONMENTS, TABLES, AND TROFF MACROS     SETUID(7)
     [] Programs executed at user request  (e.g.  shell  escapes)
        must not receive the setuid program's permissions; use of
        daughter    processes    and    setuid(getuid())     plus
        setgid(getgid()) after fork but before exec is vital.
     [] Similarly, programs executed at  user  request  must  not
        receive  other sensitive resources, notably file descrip-
        tors.  Use of closeall(3) or close-on-exec  arrangements,
        on systems which have them, is recommended.
     [] Programs activated by one user but  handling  traffic  on
        behalf  of  others  (e.g.  daemons)  should  avoid  doing
        setuid(getuid()) or setgid(getgid()), since the  original
        invoker's identity is almost certainly inappropriate.  On
        systems which permit it,  use  of  setuid(geteuid())  and
        setgid(getegid())  is recommended when performing work on
        behalf of the system as opposed to a specific user.
     [] There are inherent permission problems when a setuid pro-
        gram  executes  another setuid program, since the permis-
        sions are  not  additive.   Care  should  be  taken  that
        created  files are not owned by the wrong person.  Use of
        setuid(geteuid()) and its gid counterpart  can  help,  if
        the system allows them.
     [] Care should be taken that newly-created files do not have
        the wrong permission or ownership even momentarily.  Per-
        missions should be arranged by using  umask  in  advance,
        rather than by creating the file wide-open and then using
        chmod.  Ownership can get sticky due to  the  limitations
        of  the setuid concept, although using a daughter process
        connected by a pipe can help.
     [] Setuid programs should be especially careful about  error
        checking,  and the normal response to a strange situation
        should be termination, rather than an  attempt  to  carry
        on.
     The following are ways in which the program may  be  induced
     to carelessly give away its special privileges.
     [] The directory the program is started in,  or  directories
        it  may plausibly chdir to, may contain programs with the
        same names as system programs, placed there in hopes that
        the  program will activate a shell with a permissive PATH
        setting.   PATH  should  always  be  standardized  before
        invoking  a  shell  (either  directly  or  via  popen  or
        execvp/execlp).
     [] Similarly, a bizarre IFS setting may alter the  interpre-
        tation  of a shell command in really strange ways, possi-
        bly causing a user-supplied program to be  invoked.   IFS
Sun Release 4.1        Last change: local                       2
SETUID(7)    ENVIRONMENTS, TABLES, AND TROFF MACROS     SETUID(7)
        too  should  always  be  standardized  before  invoking a
        shell.  (Our shell does this automatically.)
     [] Environment  variables  in  general  cannot  be  trusted.
        Their contents should never be taken for granted.
     [] Setuid shell files (on systems which implement such) sim-
        ply  cannot  cope adequately with some of these problems.
        They also have some nasty problems like trying to  run  a
        .profile when run under a suitable name.  They are termi-
        nally insecure, and must be avoided.
     [] Relying on the contents of files  placed  in  publically-
        writeable   directories,  such  as  /tmp,  is  a  nearly-
        incurable security problem.  Setuid programs should avoid
        using  /tmp  entirely,  if humanly possible.  The sticky-
        directories modification (sticky bit on for  a  directory
        means  only  owner of a file can remove it) (we have this
        feature) helps, but is not a complete solution.
     [] A related problem  is  that  spool  directories,  holding
        information that the program will trust later, must never
        be publically writeable even if the files in  the  direc-
        tory  are  protected.  Among other sinister manipulations
        that can be performed, note  that  on  many  Unixes  (not
        ours),  a  core  dump of a setuid program is owned by the
        program's owner and not by the user running it.
     The following are unusual but possible error conditions that
     the  program  should cope with properly (resource-exhaustion
     questions are considered separately, see below).
     [] The value of argc might be 0.
     [] The setting of the umask might not be sensible.   In  any
        case,  it  should be standardized when creating files not
        intended to be owned by the user.
     [] One or more of the standard descriptors might be  closed,
        so  that  an  opened  file  might get (say) descriptor 1,
        causing chaos if the program tries to do a printf.
     [] The current directory (or any  of  its  parents)  may  be
        unreadable and unsearchable.  On many systems pwd(1) does
        not run setuid-root, so it can  fail  under  such  condi-
        tions.
     [] Descriptors shared by other processes (i.e., any that are
        open  on  startup)  may be manipulated in strange ways by
        said processes.
     [] The standard descriptors may refer to  a  terminal  which
Sun Release 4.1        Last change: local                       3
SETUID(7)    ENVIRONMENTS, TABLES, AND TROFF MACROS     SETUID(7)
        has  a  bizarre  mode  setting, or which cannot be opened
        again, or which gives end-of-file on any read attempt, or
        which cannot be read or written successfully.
     [] The process may be hit by  interrupt,  quit,  hangup,  or
        broken-pipe  signals,  singly or in fast succession.  The
        user  may  deliberately  exploit  the   race   conditions
        inherent  in  catching signals; ignoring signals is safe,
        but catching them is not.
     [] Although non-keyboard signals cannot be sent by  ordinary
        users  in  V7,  they  may  perhaps  be sent by the system
        authorities (e.g. to indicate that the system is about to
        shut down), so the possibility cannot be ignored.
     [] On some systems (not ours) there may be an  alarm  signal
        pending on startup.
     [] The program may have children it did not create.  This is
        normal when the process is part of a pipeline.
     [] In some non-V7 systems, users can change  the  ownerships
        of  their  files.   Setuid programs should avoid trusting
        the owner identification of a file.
     [] User-supplied arguments and input data  must  be  checked
        meticulously.   Overly-long  input  stored  in  an  array
        without proper bound checking can easily breach security.
        When  software depends on a file being in a specific for-
        mat, user-supplied data should never be inserted into the
        file  without  being  checked first.  Meticulous checking
        includes allowing for the possibility of non-ASCII  char-
        acters.
     [] Temporary files left  in  public  directories  like  /tmp
        might vanish at inconvenient times.
     The following are resource-exhaustion possibilities that the
     program should respond properly to.
     [] The user might have used up all of his allowed processes,
        so  any  attempt  to create a new one (via fork or popen)
        will fail.
     [] There might be many files open, exhausting the supply  of
        descriptors.   Running closeall(3), on systems which have
        it, is recommended.
     [] There might be many arguments.
     [] The arguments and the environment together might occupy a
        great deal of space.
Sun Release 4.1        Last change: local                       4
SETUID(7)    ENVIRONMENTS, TABLES, AND TROFF MACROS     SETUID(7)
     Systems which impose other  resource  limitations  can  open
     setuid programs to similar resource-exhaustion attacks.
     Setuid programs  which  execute  ordinary  programs  without
     reducing  authority  pass  all the above problems on to such
     unprepared children.  Standardizing the  execution  environ-
     ment is only a partial solution.
SEE ALSO
     closeall(3), standard(3)
HISTORY
     Locally written, although based  on  outside  contributions.
     This  file  came with COPS_1.04, and is distributed by Peter
     Allan with rlprm because it bears repetition.
BUGS
     The list really is rather long...  and probably incomplete.
     Neither the author nor the University of  Toronto  (nor  PA)
     accepts  any  responsibility whatever for the use or non-use
     of this information
------------------
Appreciate all your help.  Vicky Lau
This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:12:01 CDT