home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / old / ckermit5a188 / ckuuid.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  19KB  |  590 lines

  1. /* ckuuid.c, Test program for C-Kermit's uid-managing code. */
  2. /* priv_*() routines by Kristoffer Ericksson (ske) */
  3. /* this program by Frank da Cruz (fdc), 5-26-90 */
  4. /* Modified by Dean Long, 5-28-90 */
  5. /* Modified ske, fdc, 6-10-90 */
  6. /* Modified to check access() and state conclusions, fdc, 12-29-92 */
  7.  
  8. /*
  9. INSTRUCTIONS
  10.  
  11. Compile and load the program in each of the following ways:
  12.  
  13.    $ cc -DANYBSD -DSAVEDUID -o ckuuid1 ckuuid.c     (1)
  14.    $ cc -DANYBSD -o ckuuid2 ckuuid.c                (2)
  15.    $ cc -DANYBSD -DNOSETREU -o ckuuid3 ckuuid.c     (3)
  16.    $ cc -o ckuuid4 ckuuid.c                         (4)
  17.  
  18. (1) is for Berkeley-based systems that have setregid() and setreuid() and that
  19.     have the saved-original-effective-uid feature, similar to AT&T System V.
  20.  
  21. (2) is for Berkeley-based systems that have setregid and setreuid, but do not
  22.     have the saved-original-effective-uid feature.
  23.  
  24. (3) is for Berkeley-based systems that don't have setregid() and setreuid().
  25.  
  26. (4) is for all others, including all AT&T-based versions, Xenix, etc.
  27.  
  28. After building the program, run it to make sure that the uid's don't change
  29. (they shouldn't if the program is not setuid'd).
  30.  
  31. Now make the program setuid and setgid to someone else, e.g.:
  32.  
  33.    $ su
  34.    Password: xxxx
  35.    su% chown uucp.uucp ckuuid2
  36.    su% chmod ug+s ckuuid2
  37.    su% exit
  38.  
  39. and then run it, recording the results.  Give the name of a directory (not a
  40. regular file) that you should have write-access to on the command line, e.g.:
  41.  
  42.    $ script
  43.    $ who am i
  44.    $ ls -lg ./ckuuid2
  45.    $ ./ckuuid2 .
  46.    $ exit
  47.  
  48. Read the output and make sure that the uids and gids can be changed back
  49. and forth.
  50.  
  51. In steps 2, 4, and 5, check to see if access() worked correctly when you
  52. have privs turned off.  If it doesn't, try recompiling with -DSW_ACC_ID
  53. (swap IDs for access()) and repeat the above procedure.
  54.  
  55. Please report the results (mail the typescript file) back to me,
  56. fdc@watsun.cc.columbia.edu, letting me know exactly what kind of machine you
  57. have, and which version of UNIX.
  58. */ 
  59.  
  60. /* Includes */
  61.  
  62. #include <stdio.h>
  63. #include <sys/stat.h>
  64. #ifndef S_ISDIR
  65. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  66. #endif /* S_ISDIR */
  67.  
  68. #ifndef UID_T                /* Define these on the CC command */
  69. #define UID_T int            /* line if your compiler complains */
  70. #endif /* UID_T */            /* about mismatched types, e.g. */
  71.                     /* -DUID_T=uid_t -DGID_T=gid_t */
  72. #ifndef GID_T
  73. #define GID_T int
  74. #endif /* GID_T */
  75.  
  76. /* setuid package variables */
  77.  
  78. /* User and group IDs */
  79.  
  80. static int realuid = (UID_T) -1, privuid = (UID_T) -1;
  81. static int realgid = (GID_T) -1, privgid = (GID_T) -1;
  82. static int ttpuid, ttpgid;
  83.  
  84. /* UID-holding variables */
  85.  
  86. int oku = 0;                /* uid switching works ok */
  87. int okg = 0;                /* gid switching works ok */
  88.  
  89. int uida, uidb;                /* Variables for remembering ids... */
  90. int gida, gidb;
  91.  
  92. int euid, egid;
  93. int ruid, rgid;
  94.  
  95. /* Function to get and print the current real and effective uid and gid */
  96.  
  97. chuid() {
  98.     uida = getuid();
  99.     uidb = geteuid();
  100.     printf("     getuid = %d, geteuid = %d\n",uida,uidb);
  101.     gida = getgid();
  102.     gidb = getegid();
  103.     printf("     getgid = %d, getegid = %d\n",gida,gidb);
  104. }
  105.  
  106. /* Main program */
  107.  
  108. struct stat statbuf;
  109. char *myname;
  110.  
  111. main(argc, argv) int argc; char **argv; {
  112.     int x, acc_ok = 1;
  113.     char *path = NULL;
  114.  
  115.     myname = argv[0];
  116.     if (argc < 2)
  117.       usage();
  118.     else
  119.       path = argv[1];
  120.  
  121. /* Announce which options we were compiled with. */
  122.  
  123. #ifndef NOSETREU
  124. #ifndef SETREUID
  125. #define SETREUID
  126. #endif /* SETREUID */
  127. #endif /* NOSETREU */
  128.  
  129. #ifdef BSD
  130. #ifndef ANYBSD
  131. #define ANYBSD
  132. #endif /* ANYBSD */
  133. #endif /* BSD */
  134.  
  135. #ifdef ANYBSD
  136. #ifndef SAVEDUID
  137.       printf("BSD, with SAVEDUID not defined\n");
  138. #else
  139.       printf("BSD, with SAVEDUID defined\n");
  140. #endif
  141. #ifdef NOSETREU
  142.       printf("No setre[ug]id, using set[ug]id\n");
  143. #else
  144.       printf("Using setre[ug]id\n");
  145. #endif
  146. #else
  147.       printf("Not BSD\n");
  148. #endif
  149.  
  150. /* Print uids and gids before, during and after switching back & forth. */
  151.  
  152.     printf("\n1. ids at startup...\n");
  153.     chuid();
  154.     euid = uidb; egid = gidb;
  155.  
  156.     /* Initialize uid package, change to real uid. */
  157.     printf("\n2. changing to real user and group ids...\n");
  158.     x = priv_ini();
  159.     printf("   priv_ini returns %d\n",x);
  160.     chuid();
  161.     if (stat(path,&statbuf) == -1) {    /* Do this after switching to self */
  162.     perror(path);
  163.     exit(1);
  164.     }
  165.     if (!S_ISDIR (statbuf.st_mode)) {
  166.     printf("%s: not a directory\n");
  167.     usage();
  168.     }
  169.     acc_ok = chkaccess(path);
  170.     ruid = uidb; rgid = gidb;
  171.     
  172.     printf("   this program %s setuid\n",(ruid != euid) ? "IS" : "is NOT");
  173.     printf("   this program %s setgid\n",(rgid != egid) ? "IS" : "is NOT");
  174.     if (ruid == euid || rgid == egid) {
  175.     printf("\nprogram is not privileged\n");
  176.     printf("please chown owner AND group and add \"ug+s\" bits\n\n");
  177.     exit(0);
  178.     }
  179.     /* Try to change back to effective uid */
  180.     printf("\n3. changing to original user and group ids...\n");
  181.     x = priv_on();
  182.     printf("   priv_on returns %d\n",x);
  183.     chuid();
  184. #ifdef SAVEDUID
  185.     printf("   saved-original-effective-uid feature %s present\n",
  186.        (uidb == euid) ? "IS" : "is NOT");
  187.     printf("   saved-original-effective-gid feature %s present\n",
  188.        (gidb == egid) ? "IS" : "is NOT");
  189. #else
  190.     printf("   original effective uid %s\n",
  191.        (uidb == euid) ? "restored ok" : "NOT restored");
  192.     printf("   original effective gid %s\n",
  193.        (gidb == egid) ? "restored ok" : "NOT restored");
  194. #endif /* SAVEDUID */
  195.     if (uidb != euid || gidb != egid) goto conclude;
  196.  
  197.     oku = okg = 1;            /* Seems OK so far */
  198.  
  199.     /* Change back to real uid */
  200.     printf("\n4. switching back to real ids...\n"); 
  201.     x = priv_off();
  202.     printf("   priv_off returns %d\n",x);
  203.     chuid();
  204.     x = chkaccess(path);
  205.     if (x < 0) acc_ok = x;
  206.     if (uidb != ruid) {
  207.     printf("  FAILURE to restore real uid\n");
  208.     oku = 0;
  209.     goto conclude;
  210.     }
  211.     if (gidb != rgid) {
  212.     printf("  FAILURE to restore real gid\n");
  213.     okg = 0;
  214.     goto conclude;
  215.     }
  216.     printf("   real ids restored ok\n");
  217.  
  218.     /* Change back to real uid, e.g. for a fork */
  219.     printf("\n5. cancelling privileges permanently...\n");
  220.     x = priv_can();
  221.     printf("   priv_can returns %d\n",x);
  222.     chuid();
  223.     x = chkaccess(path);
  224.     if (x < 0) acc_ok = x;
  225.  
  226.     /* Try to change back to effective uid */
  227.     printf("\n6. trying to restore canceled privileges\n");
  228.     x = priv_on();
  229.     printf("   priv_on returns %d\n",x);
  230.     chuid();
  231.  
  232.     printf("   privilege cancellation %s\n",
  233.        (uidb == euid || gidb == egid) ? "FAILED" : "SUCCEEDED");
  234.     if (uidb == euid) oku = 0;
  235.     if (gidb == egid) okg = 0;
  236.     
  237. conclude:
  238.     printf("\nCONCLUSIONS:\n");
  239.     printf("   It %s safe to install C-Kermit setuid.\n",
  240.        oku ? "IS" : "is NOT");
  241.     printf("   It %s safe to install C-Kermit setgid.\n",
  242.        okg ? "IS" : "is NOT");
  243. #ifdef ANYBSD
  244.     printf("...when built in the BSD environment with these options:\n");
  245. #ifdef SAVEDUID
  246.     printf("   -DSAVEDUID included\n");
  247. #else
  248.     printf("   -DSAVEDUID omitted\n");
  249. #endif
  250. #ifdef NOSETREU
  251.     printf("   -DNOSETREU included\n");
  252. #else
  253.     printf("   -DNOSETREU omitted (-DSETREUID implied)\n");
  254. #endif
  255. #ifdef SW_ACC_ID
  256.     printf("   -DSW_ACC_ID included\n");
  257. #else
  258.     printf("   -DSW_ACC_ID omitted\n");
  259. #endif
  260. #else
  261.     printf("...when built in the System V or POSIX environment\n");
  262. #endif /* ANYBSD */
  263.     if (acc_ok != 1) {
  264.     if (acc_ok < 0) {
  265.         printf(
  266. "\nBUT if %s is a directory that you would normally have write\n\
  267. access to, then something is wrong with this system's access() function.\n\n",
  268.            path, path);
  269.         printf("Try rebuilding %s -DSW_ACC_ID.\n",
  270. #ifdef SW_ACC_ID
  271.            "without"
  272. #else
  273.            "with"
  274. #endif /* SW_ACC_ID */
  275.            );
  276.     } else {
  277.         printf("\nAND access() seems to work properly.\n");
  278.     }
  279.     }
  280.     exit(0);
  281. }
  282.   
  283. #ifndef W_OK
  284. #define W_OK 2
  285. #endif /* W_OK */
  286.  
  287. int
  288. chkaccess(path) char *path; {
  289.     int x;
  290.     if (path) {
  291. #ifdef SW_ACC_ID
  292.     printf("   access check: temporarily swapping ids...\n");
  293.     priv_on();
  294.     x = access(path, W_OK);
  295.     priv_off();
  296. #else
  297.     printf("   access check: no temporary id swapping...\n");
  298.     x = access(path, W_OK);
  299. #endif /* SW_ACC_ID */
  300.     printf("   write-access to %s %s\n", path, x ? "FAILED" : "OK");
  301.     return(x);
  302.     }
  303.     return(1);
  304. }
  305.  
  306. /* 
  307.   Starting here is Kristoffer's code, as modified by fdc, dlong, et al.
  308. */
  309.  
  310. /*
  311.   setuid package, by Kristoffer Eriksson, with contributions from Dean
  312.   Long and fdc.
  313. */
  314.  
  315. #ifndef AIX370
  316. extern int getuid(), getgid(), geteuid(), getegid(), getreuid(), getregid();
  317. #endif
  318.  
  319. /*
  320. Subject: Set-user-id
  321. To: fdc@watsun.cc.columbia.edu (Frank da Cruz)
  322. Date: Sat, 21 Apr 90 4:48:25 MES
  323. From: Kristoffer Eriksson <ske@pkmab.se>
  324.  
  325. This is a set of functions to be used in programs that may be run set-user-id
  326. and/or set-group-id. They handle both the case where the program is not run
  327. with such privileges (nothing special happens then), and the case where one
  328. or both of these set-id modes are used.  The program is made to run with the
  329. user's real user and group ids most of the time, except for when more
  330. privileges are needed.  Don't set-user-id to "root".
  331.  
  332. This works on System V and POSIX.  In BSD, it depends on the
  333. "saved-set-user-id" feature.
  334. */
  335.  
  336. #define UID_ROOT 0            /* Root user and group ids */
  337. #define GID_ROOT 0
  338.  
  339. /* P R I V _ I N I  --  Initialize privileges package  */
  340.  
  341. /* Called as early as possible in a set-uid or set-gid program to store the
  342.  * set-to uid and/or gid and step down to the users real uid and gid. The
  343.  * stored id's can be temporarily restored (allowed in System V) during
  344.  * operations that require the privilege.  Most of the time, the program
  345.  * should execute in unpriviliged state, to not impose any security threat.
  346.  *
  347.  * Note: Don't forget that access() always uses the real id:s to determine
  348.  * file access, even with privileges restored.
  349.  *
  350.  * Returns an error mask, with error values or:ed together:
  351.  *   1 if setuid() fails,
  352.  *   2 if setgid() fails, and
  353.  *   4 if the program is set-user-id to "root", which can't be handled.
  354.  *
  355.  * Only the return value 0 indicates real success. In case of failure,
  356.  * those privileges that could be reduced have been, at least, but the
  357.  * program should be halted nonetheless.
  358.  *
  359.  * Also note that these functions do not expect the uid or gid to change
  360.  * without their knowing. It may work if it is only done temporarily, but
  361.  * you're on your own.
  362.  */
  363. int
  364. priv_ini() {
  365.     int err = 0;
  366.  
  367.     /* Save real ID:s. */
  368.     realuid = getuid();
  369.     realgid = getgid();
  370.  
  371.     /* Save current effective ID:s, those set to at program exec. */
  372.     ttpuid = privuid = geteuid();
  373.     ttpgid = privgid = getegid();
  374.  
  375.     /* If running set-uid, go down to real uid, otherwise remember that
  376.      * no privileged uid is available.
  377.      *
  378.      * Exceptions:
  379.      *
  380.      * 1) If the real uid is already "root" and the set-uid uid (the
  381.      * initial effective uid) is not "root", then we would have trouble
  382.      * if we went "down" to "root" here, and then temporarily back to the
  383.      * set-uid uid (not "root") and then again tried to become "root". I
  384.      * think the "saved set-uid" is lost when changing uid from effective
  385.      * uid "root", which changes all uid, not only the effective uid. But
  386.      * in this situation, we can simply go to "root" and stay there all
  387.      * the time. That should give sufficient privilege (understatement!),
  388.      * and give the right uids for subprocesses.
  389.      *
  390.      * 2) If the set-uid (the initial effective uid) is "root", and we
  391.      * change uid to the real uid, we can't change it back to "root" when
  392.      * we need the privilege, for the same reason as in 1). Thus, we can't
  393.      * handle programs that are set-user-id to "root" at all. The program
  394.      * should be halted.  Use some other uid. "root" is probably too
  395.      * privileged for such things, anyway.  (The uid is reverted to the
  396.      * real uid for the lifetime of the program.)
  397.      *
  398.      * These two exceptions have the effect that the "root" uid will never
  399.      * be one of the two uids that are being switched between, which also
  400.      * means we don't have to check for such cases in the switching
  401.      * functions.
  402.      *
  403.      * Note that exception 1) is handled by these routines (by constantly
  404.      * running with uid "root", while exception 2) is a serious error, and
  405.      * is not provided for at all in the switching functions.
  406.      */
  407.     if (realuid == privuid)
  408.     privuid = (UID_T) -1;        /* Not running set-user-id. */
  409.  
  410.     /* If running set-gid, go down to real gid, otherwise remember that
  411.      * no privileged gid is available.
  412.      *
  413.      * There are no exception like there is for the user id, since there
  414.      * is no group id that is privileged in the manner of uid "root".
  415.      * There could be equivalent problems for group changing if the
  416.      * program sometimes ran with uid "root" and sometimes not, but
  417.      * that is already avoided as explained above.
  418.      *
  419.      * Thus we can expect always to be able to switch to the "saved set-
  420.      * gid" when we want, and back to the real gid again. You may also
  421.      * draw the conclusion that set-gid provides for fewer hassles than
  422.      * set-uid.
  423.      */
  424.  
  425.     if (realgid == privgid)        /* If not running set-user-id, */
  426.       privgid = (GID_T) -1;        /*  remember it this way. */
  427.  
  428.     err = priv_off();            /* Turn off setuid privilege. */
  429.  
  430.     if (privuid == UID_ROOT)        /* If setuid to root, */
  431.       err |= 4;                /* return this error. */
  432.  
  433.     if (realuid == UID_ROOT)        /* If real id is root, */
  434.       privuid = (UID_T) -1;        /* stay root at all times. */
  435.  
  436.     return(err);
  437. }
  438.  
  439.  
  440. /* Macros for hiding the differences in UID/GID setting between various Unix
  441.  * systems. These macros should always be called with both the privileged ID
  442.  * and the non-privileged ID. The one in the second argument, will become the
  443.  * effective ID. The one in the first argument will be retained for later
  444.  * retrieval.
  445.  */
  446. #ifdef SETREUID
  447. #ifdef SAVEDUID
  448. /* On BSD systems with the saved-UID feature, we just juggle the effective
  449.  * UID back and forth, and leave the real UID at its true value.  The kernel
  450.  * allows switching to both the current real UID, the effective UID, and the
  451.  * UID which the program is set-UID to.  The saved set-UID always holds the
  452.  * privileged UID for us, and the real UID will always be the non-privileged,
  453.  * and we can freely choose one of them for the effective UID at any time.
  454.  */
  455. #define switchuid(hidden,active) setreuid( (UID_T) -1, active)
  456. #define switchgid(hidden,active) setregid( (GID_T) -1, active)
  457.  
  458. #else   /* SETREUID,!SAVEDUID */
  459.  
  460. /* On systems with setreXid() but without the saved-UID feature, notably
  461.  * BSD 4.2, we swap the real and effective UIDs each time.  It's
  462.  * the effective UID that we are interested in, but we have to retain the
  463.  * unused UID somewhere to enable us to restore it later, and we do this
  464.  * in the real UID.  The kernel only allows switching to either the current 
  465.  * real or the effective UID, unless you're "root".
  466.  */
  467. #define switchuid(hidden,active)    setreuid(hidden,active)
  468. #define switchgid(hidden,active)    setregid(hidden,active)
  469. #endif
  470.  
  471. #else /* !SETREUID, !SAVEDUID */
  472.  
  473. /* On System V and POSIX, the only thing we can change is the effective UID
  474.  * (unless the current effective UID is "root", but initsuid() avoids that for
  475.  * us).  The kernel allows switching to the current real UID or to the saved
  476.  * set-UID.  These are always set to the non-privileged UID and the privileged
  477.  * UID, respectively, and we only change the effective UID.  This breaks if
  478.  * the current effective UID is "root", though, because for "root" setuid/gid
  479.  * becomes more powerful, which is why initsuid() treats "root" specially.
  480.  * Note: That special treatment maybe could be ignored for BSD?  Note: For
  481.  * systems that don't fit any of these three cases, we simply can't support
  482.  * set-UID.
  483.  */
  484. #define switchuid(hidden,active)    setuid(active)
  485. #define switchgid(hidden,active)    setgid(active)
  486. #endif /* SETREUID */
  487.   
  488.  
  489. /* P R I V _ O N  --  Turn on the setuid and/or setgid */
  490.  
  491. /* Go to the privileged uid (gid) that the program is set-user-id
  492.  * (set-group-id) to, unless the program is running unprivileged.
  493.  * If setuid() fails, return value will be 1. If getuid() fails it
  494.  * will be 2.  Return immediately after first failure, and the function
  495.  * tries to restore any partial work done.  Returns 0 on success.
  496.  * Group id is changed first, since it is less serious than user id.
  497.  */
  498. int
  499. priv_on() {
  500.     if (privgid != (GID_T) -1)
  501.       if (switchgid(realgid,privgid))
  502.         return(2);
  503.  
  504.     if (privuid != (UID_T) -1)
  505.       if (switchuid(realuid,privuid)) {
  506.       if (privgid != (GID_T) -1)
  507.         switchgid(privgid,realgid);
  508.       return(1);
  509.       }
  510.     return(0);
  511. }
  512.  
  513. /* P R I V _ O F F  --  Turn on the real uid and gid */
  514.  
  515. /* Return to the unprivileged uid (gid) after an temporary visit to
  516.  * privileged status, unless the program is running without set-user-id
  517.  * (set-group-id). Returns 1 for failure in setuid() and 2 for failure
  518.  * in setgid() or:ed together. The functions tries to return both uid
  519.  * and gid to unprivileged state, regardless of errors. Returns 0 on
  520.  * success.
  521.  */
  522. int
  523. priv_off() {
  524.     int err = 0;
  525.  
  526.     if (privuid != (UID_T) -1)
  527.        if (switchuid(privuid,realuid))
  528.       err |= 1;
  529.  
  530.     if (privgid != (GID_T) -1)
  531.        if (switchgid(privgid,realgid))
  532.     err |= 2;
  533.  
  534.     return(err);
  535. }
  536.  
  537. /* Turn off privilege permanently.  No going back.  This is necessary before
  538.  * a fork() on BSD43 machines that don't save the setUID or setGID, because
  539.  * we swap the real and effective ids, and we don't want to let the forked
  540.  * process swap them again and get the privilege back. It will work on other
  541.  * machines too, such that you can rely on its effect always being the same,
  542.  * for instance, even when you're in priv_on() state when this is called.
  543.  * (Well, that part about "permanent" is on System V only true if you follow
  544.  * this with a call to exec(), but that's what we want it for anyway.)
  545.  * Added by Dean Long -- dlong@midgard.ucsc.edu
  546.  */
  547. int
  548. priv_can() {
  549.  
  550. #ifdef SETREUID
  551.     int err = 0;
  552.     if (privuid != (UID_T) -1)
  553.        if (setreuid(realuid,realuid))
  554.       err |= 1;
  555.  
  556.     if (privgid != (GID_T) -1)
  557.         if (setregid(realgid,realgid))
  558.        err |= 2;
  559.  
  560.     return(err);
  561.  
  562. #else
  563.     /* Easy way of using setuid()/setgid() instead of setreuid()/setregid().*/
  564.     return(priv_off());
  565.  
  566. #endif /* SETREUID */
  567. }
  568.  
  569. /*  P R I V _ C H K  --  Check privileges.  */
  570.  
  571. /*  Try to turn them off.  If turning them off did not succeed, cancel them */
  572.  
  573. int
  574. priv_chk() {
  575.     int x, y = 0;
  576.     x = priv_off();            /* Turn off privs. */
  577.     if (x != 0 || getuid() == privuid || geteuid() == privuid)
  578.       y = priv_can();
  579.     if (x != 0 || getgid() == privgid || getegid() == privgid)
  580.       y = y | priv_can();
  581.     return(y);
  582. }
  583.  
  584. usage() {
  585.     printf("usage: %s directory\n\n", myname);
  586.     printf(
  587. "please supply the name of a directory that you have write access to.\n");
  588.     exit(1);
  589. }
  590.