home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume4 / chown < prev    next >
Text File  |  1986-11-30  |  7KB  |  246 lines

  1. Subject: Improved and expanded chown/chgrp
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 4, Issue 107
  6. Submitted by: condor!ihnp4!rosevax!rose3!merlyn!root
  7.  
  8. [ I have not tried to verify that this program does not open any protection
  9.   holes - I assume that it is intended to be installed as setuid to root
  10.   since most (some?) UNIX'es will not let you give files away unless you
  11.   are root.
  12.                       - John Nelson, moderator
  13. ]
  14.  
  15. /*
  16.  *    chown, chgrp, chto, chusr, chmine, chsame - improved chown/chgrp
  17.  *
  18.  *        This is a more flexible version of the chown/chgrp commands.
  19.  *    If REASONABLE is not #defined, chown and chgrp should be identical
  20.  *    to the originals (except for slight differences in the usage:
  21.  *    error message).  Otherwise, two subtle changes will occur:
  22.  *    suid and sgid bits will be preserved for non-superusers (if possible),
  23.  *    and 'changing' a file to be what it already is (like "chown root /")
  24.  *    will always succeed.  These slight changes were made to prevent
  25.  *    accidentally turning off suid/guid bits with global commands like
  26.  *    "chmine -s *" for non-superusers.  Of course, bozoid stuff like
  27.  *    "chown root myprog" will still disable any suid bit (since the
  28.  *    chmod call to restore the suid bit will fail).
  29.  *
  30.  *    Since all the programs are linked, the total size is about 40%
  31.  *    smaller than the original (separate) chown & chgrp.
  32.  *
  33.  *    The six flavors are:
  34.  *
  35.  *    chown  [-s] owner files ...
  36.  *    chgrp  [-s] group files ...
  37.  *    chto   [-s] owner group files ...
  38.  *    chusr  [-s] username files ...
  39.  *    chmine [-s] files ...
  40.  *    chsame [-s] template files ...
  41.  *
  42.  *        chown & chgrp act as they always have.  chto changes both owner
  43.  *    and group of the files (MUST be in this order, since "chto 1 2 foo"
  44.  *    would otherwise be ambiguous).  chusr changes the owner & group to
  45.  *    match the given user's login uid and gid (chusr must be given a login
  46.  *    name, not a number).  chmine changes the files to match your current
  47.  *    uid and gid.  chsame changes the files to match the first file given,
  48.  *    as in "chsame . *"
  49.  *
  50.  *        The -s option will not change the uid [gid] of files that have
  51.  *    the suid [sgid] bit set.  This will avoid problems with:
  52.  *        cd /bin
  53.  *        chusr -s bin *
  54.  *    ...which would disable su, ps, etc. without the -s option.
  55.  *
  56.  *    Flames to: ...ihnp4!umn-cs!rosevax!rose3!merlyn!brian (or root)
  57.  *        a.k.a Merlyn Leroy (back on the air!)
  58.  *
  59.  */
  60.  
  61. #include    <stdio.h>
  62. #include    <ctype.h>    /* for isdigit */
  63. #include    <pwd.h>        /* for getpwnam */
  64. #include    <grp.h>        /* for getgrnam */
  65. #include    <sys/types.h>    /* for stat */
  66. #include    <sys/stat.h>    /* for stat */
  67.  
  68. /* #define REASONABLE if you want a more reasonable chown/grp/etc */
  69. /* don't define it if you want slavish compatability with the old chown/grp */
  70.  
  71. #define    REASONABLE
  72.  
  73. #define    when        break;case    /* for convenience */
  74. #define    otherwise    break;default
  75.  
  76. #define    errchk(eval)    if (eval) { perror(*argv); exitval = 1; continue; }
  77.  
  78. #define    SET_UID    1
  79. #define    SET_GID    2
  80. #define    SET_ALL    (SET_UID | SET_GID)
  81.  
  82. #define    OWN    0
  83. #define    GRP    1
  84. #define    TO    2
  85. #define    USR    3
  86. #define    MINE    4
  87. #define    SAME    5
  88.  
  89. #define    FIRST    OWN    /* FIRST is also the default function */
  90. #define    LAST    SAME    /* if invoked under an unknown name */
  91.  
  92. extern chown();
  93. extern chmod();
  94. extern stat();
  95. extern struct passwd *getpwnam();
  96. extern struct group *getgrnam();
  97.  
  98. struct passwd *pw;    /* struct returned by getpwnam() */
  99. struct group *gr;    /* struct returned by getgrnam() */
  100. char *progname;        /* what is my name? */
  101. short ami;        /* what am i? (set to OWN, GRP, TO, etc) */
  102.  
  103. struct {
  104.     char *prog;        /* list of recognized program names */
  105.     short n,set;        /* minimum # of params needed; id's to set */
  106. } what[] =     {{ "chown", 2, SET_UID},
  107.         { "chgrp", 2, SET_GID},
  108.         { "chto",  3, SET_ALL},
  109.         { "chusr", 2, SET_ALL},
  110.         { "chmine",1, SET_ALL},
  111.         { "chsame",2, SET_ALL}};
  112.  
  113. /* to avoid the silly strrchr vs. rindex problem, here is strrchr */
  114.  
  115. char *strrchr(s,ch)
  116. register char *s,ch;
  117. {
  118.     register char *p;
  119.  
  120.     p = NULL;
  121.     do {
  122.     if (*s == ch) p = s;
  123.     } while (*s++);
  124.  
  125.     return (p);
  126. }
  127.  
  128. main(argc,argv)
  129. int argc;
  130. register char *argv[];
  131. {
  132.     struct stat status;        /* to hold the stat() structure returned */
  133.     int uid,gid;        /* uid and gid wanted */
  134.     int chuid,chgid;        /* uid and gid to change */
  135.     short nargs,toset;        /* minimum # of params; id's to set */
  136.     char saveset;        /* boolean option */
  137.     int exitval;        /* return value */
  138.  
  139.     if ((progname = strrchr(*argv,'/')) != NULL) progname++;
  140.     else progname = *argv;                /* find my name */
  141.     what[FIRST].prog = progname;            /* assign as default */
  142.  
  143.     ami = LAST+1;
  144.     while (strcmp(progname,what[--ami].prog));        /* find name in list */
  145.     nargs = what[ami].n;
  146.     toset = what[ami].set;
  147.  
  148.     if (saveset = !strcmp(*++argv,"-s")) argv++, argc--;
  149.  
  150.     if (argc <= nargs) {
  151.     fprintf(stderr,"usage: %s [-s] ",progname);
  152.     switch (ami) {
  153.     when  USR: fprintf(stderr,"username ");
  154.     when SAME: fprintf(stderr,"template ");
  155.     when MINE:        /* no parameters */
  156.     otherwise: if (toset & SET_UID) fprintf(stderr,"owner ");
  157.            if (toset & SET_GID) fprintf(stderr,"group ");
  158.     }
  159.     fprintf(stderr,"files ...\n");
  160.     exit(4);
  161.     }
  162.  
  163.     switch (ami) {
  164.     when  OWN: uid = finduid(*argv++);
  165.     when  GRP: gid = findgid(*argv++);
  166.     when   TO: uid = finduid(*argv++);    gid = findgid(*argv++);
  167.     when  USR: uid = finduid(*argv++);    gid = pw->pw_gid; /* from getpwnam */
  168.     when MINE: uid = getuid();        gid = getgid();
  169.     when SAME: if (stat(*argv,&status)) {
  170.             perror(*argv);
  171.             exit(4);
  172.         }
  173.         uid = status.st_uid;    gid = status.st_gid;
  174.         argv++;
  175.     }
  176.  
  177.     exitval = 0;
  178.     for (argc -= nargs; argc--; argv++) {
  179.     errchk(stat(*argv,&status));            /* stat the file */
  180.     switch (toset) {
  181.     when SET_UID: gid = status.st_gid;        /* don't change gid */
  182.     when SET_GID: uid = status.st_uid;        /* don't change uid */
  183.     }
  184.     chuid = (saveset && (status.st_mode & S_ISUID)) ? status.st_uid : uid;
  185.     chgid = (saveset && (status.st_mode & S_ISGID)) ? status.st_gid : gid;
  186.  
  187. #ifdef REASONABLE
  188.     /*
  189.      * Only change files that need it, to be nice to non-superusers.
  190.      * Otherwise, suid & sgid bits may be removed accidentally.
  191.      */
  192.     if (chuid != status.st_uid || chgid != status.st_gid) {
  193.         errchk(chown(*argv,chuid,chgid));
  194.         if (status.st_mode & (S_ISUID | S_ISGID))
  195.         chmod(*argv,status.st_mode);  /* try to preserve suid & sgid */
  196.     }
  197. #else
  198.     errchk(chown(*argv,chuid,chgid));
  199. #endif /* REASONABLE */
  200.     }
  201.     return (exitval);
  202. }
  203.  
  204. /* find uid of user, or literal uid value */
  205. /* chusr must find username only */
  206. finduid(name)
  207. char *name;
  208. {
  209.     if ((pw = getpwnam(name)) == NULL)
  210.     if (nondigit(name)) {
  211.         fprintf(stderr,"%s: unknown user id %s\n",progname,name);
  212.         exit(4);
  213.     }
  214.     else if (ami == USR) {
  215.         fprintf(stderr,
  216.         "usage: %s [-s] username (NOT uid number) files ...\n",progname);
  217.         exit(4);
  218.     }
  219.     else return (atoi(name));
  220.     else return (pw->pw_uid);
  221. }
  222.  
  223. /* find gid of group, or literal gid value */
  224. findgid(name)
  225. char *name;
  226. {
  227.     if ((gr = getgrnam(name)) == NULL)
  228.     if (nondigit(name)) {
  229.         fprintf(stderr,"%s: unknown group id %s\n",progname,name);
  230.         exit(4);
  231.     }
  232.     else return (atoi(name));
  233.     else return (gr->gr_gid);
  234. }
  235.  
  236. /* return true (nonzero) if string contains a non-digit */
  237. nondigit(s)
  238. register char *s;
  239. {
  240.     while (*s && isdigit(*s)) s++;
  241.     return *s;
  242. }
  243.  
  244.  
  245.  
  246.