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

  1. Subject: chsh,chfn - Original contained security bugs.
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 3, Issue 99
  6. Submitted by: seismo!mcvax!htsa!jack (Jack Jansen)
  7.  
  8. [ moderator's note:  I have had SEVERAL people send mail indicating
  9.   security problems with the original chsh,chfn.  This is the only mail
  10.   I recieve that contained code fixes.  I have not tried to verify that
  11.   THIS version is secure - but it appears to be MUCH better than the
  12.   original.                         John Nelson, moderator
  13. ]
  14.  
  15. : 'This is a shell archive. Run with the real shell,'
  16. : 'not the seashell. It should extract the following:'
  17. : ' READ_ME Makefile chfn.1 chsh.1 chsh.c '
  18. echo x - READ_ME
  19. sed 's/^X//' <<'EndOfFile' >READ_ME
  20. XThis program was originally written by K. Richard Magill,
  21. Xand posted to mod.sources. Since it contained a few
  22. XSYS5 dependencies, a *serious* security bug, and no sanity
  23. Xchecks at all, I decided to hack it up a bit.
  24. X
  25. XIt now checks a given shell for existence and executability (only
  26. Xby looking at the mode, sorry), and it honors the standard
  27. X(as far as I know) algorithm for locking password files.
  28. X
  29. XThe serious security bug was the following: Imagine a user called
  30. Xhacky doing the following:
  31. Xchsh hacky '/bin/sh
  32. Xdummy::0:0::/:'
  33. X
  34. XSo, if you've installed it already, better remove it *fast*.
  35. X
  36. XAlso, some cosmetic changes were made: If no username is given, the
  37. Xcurrent user is assumed, and if no shell/realname is given, the
  38. Xold one is printed, and a new one asked.
  39. X
  40. XINSTALLATION:
  41. X
  42. XFirst, look at the defines at the top in chsh.c. If your system has
  43. Xputpwent(), remove the #define NOPUTPWENT.
  44. X
  45. XSecond, if you don't mind people playing with other people's
  46. Xshells and names, remove the #define SECURE.
  47. X
  48. XThird, KEEP YOUR HANDS OFF the #define DEBUG.
  49. X
  50. XI tried this on a mucho hacked up 11/34 running V7, so it is not
  51. Xmore than sensible that you test it before letting it anywhere near
  52. Xthe password file.
  53. X
  54. XNow, compile it, run it a few times (not as super-user), and,
  55. Xwhen you're satisfied, remove the #define DEBUG.
  56. X
  57. XNow you can type make install, to re-compile and install it.
  58. X(Don't forget to look at the Makefile to make sure all
  59. Xpaths are as you would like them).
  60. X
  61. X--
  62. X    Jack Jansen, jack@htsa.UUCP (or jack@mcvax.UUCP)
  63. X    ...!mcvax!vu44!htsa!jack
  64. X    The shell is my oyster.
  65. EndOfFile
  66. echo x - Makefile
  67. sed 's/^X//' <<'EndOfFile' >Makefile
  68. XCFLAGS=-O
  69. XDEST=/usr/local
  70. XDOC_DEST=/usr/man/man9
  71. X
  72. Xchfn chsh: chsh.o
  73. X    cc $(CFLAGS) chsh.o -o chsh
  74. X    -rm chfn
  75. X    ln chsh chfn
  76. X
  77. Xinstall: chsh chfn
  78. X    @echo Warning: you must be superuser to do this.
  79. X    -rm $(DEST)/chsh $(DEST)/chfn
  80. X    cp chsh $(DEST)/chsh
  81. X    ln $(DEST)/chsh $(DEST)/chfn
  82. X    chown root $(DEST)/chsh
  83. X    chmod 4711 $(DEST)/chsh
  84. X    cp chsh.1 $(DOC_DEST)/chsh.1
  85. X    cp chfn.1 $(DOC_DEST)/chfn.1
  86. EndOfFile
  87. echo x - chfn.1
  88. sed 's/^X//' <<'EndOfFile' >chfn.1
  89. X.TH CHFN 1 Local
  90. X.SH NAME
  91. Xchfn \- change user's real name
  92. X.SH SYNOPSIS
  93. X.B chfn
  94. X[ user [ realname ] ]
  95. X.SH DESCRIPTION
  96. X.I chfn
  97. Xallows the user to change her real name, as printed by
  98. X.I finger(1)
  99. Xand
  100. X.I who(1).
  101. XIf no
  102. X.B user
  103. Xargument is given, the real name is changed for the person
  104. Xcurrently logged in.
  105. X.PP
  106. XIf no
  107. X.B realname
  108. Xis given, the current name is printed, and a new one is asked.
  109. X.PP
  110. XDepending on choices made by the system administrator, it might
  111. Xor might not be possible to modify someone else's name. The program
  112. Xwill then ask for the correct password first.
  113. X.SH SEE ALSO
  114. Xchsh(1), finger(1), who(1)
  115. X.SH DIAGNOSTICS
  116. XAll kinds of problems with the password file, and locking it,
  117. Xare reported, and the program exits.
  118. X.SH AUTHOR
  119. XK. Richard Magill, rich@rexaco1.UUCP
  120. X.br
  121. XExtensively modified by Jack Jansen, jack@htsa.UUCP.
  122. EndOfFile
  123. echo x - chsh.1
  124. sed 's/^X//' <<'EndOfFile' >chsh.1
  125. X.TH CHSH 1 Local
  126. X.SH NAME
  127. Xchsh \- change login shell
  128. X.SH SYNOPSIS
  129. X.B chsh
  130. X[ user [ shell ] ]
  131. X.SH DESCRIPTION
  132. X.I chsh
  133. Xallows the user to change her login shell.
  134. XIf no
  135. X.B user
  136. Xargument is given, the login shell is changed for the person
  137. Xcurrently logged in.
  138. X.PP
  139. XIf no
  140. X.B shell
  141. Xis given, the current shell is printed, and a new one is asked.
  142. X.PP
  143. XDepending on choices made by the system administrator, it might
  144. Xor might not be possible to modify someone else's shell. The program
  145. Xwill then ask for the correct password first.
  146. X.SH SEE ALSO
  147. Xchfn(1), login(1)
  148. X.SH DIAGNOSTICS
  149. XThe
  150. X.B shell
  151. Xgiven is checked for existence, and executability.
  152. X.br
  153. XAlso, all kinds of problems with the password file, and locking it,
  154. Xare reported, and the program exits.
  155. X.SH BUGS
  156. XThe executability check only looks at the mode, so it doesn't
  157. Xguarantee that you will be able to log in with the given shell.
  158. X.SH AUTHOR
  159. XK. Richard Magill, rich@rexaco1.UUCP
  160. X.br
  161. XExtensively modified by Jack Jansen, jack@htsa.UUCP.
  162. EndOfFile
  163. echo x - chsh.c
  164. sed 's/^X//' <<'EndOfFile' >chsh.c
  165. X/*
  166. X *    This program was originally written by K. Richard Magill,
  167. X *    and posted to mod.sources. It has been extensively
  168. X *    modified by Jack Jansen. See below for details.
  169. X *
  170. X *    K. Richard Magill, 26-jan-86.
  171. X *    Last Mod 26-jan-86, rich.
  172. X *    Modified by Jack Jansen, 30-jan-86:
  173. X *    - It now runs under V7.
  174. X *    - It now uses (what I believe to be) standard
  175. X *      password file locking and backups.
  176. X *    - Check the size of the new passwd file, abort if
  177. X *      it looks funny.
  178. X *    - Check that there are no :colons: or \nnewlines\n in the
  179. X *      given string.
  180. X *    - Use name from getlogin() if not given, and ask for
  181. X *      parameters if not given.
  182. X *    - if SECURE is defined, don't let other people
  183. X *      muck finger/shell info.
  184. X */
  185. X#define NOPUTPWENT  1        /* Define this if you don't have putpwent */
  186. X#define SECURE    1        /* Only owner/root can change stuff */
  187. X#define DEBUG    1        /* ALWAYS DEFINE THIS AT FIRST */
  188. X#define void    int        /* If your compiler doesn't know void */
  189. X
  190. X#include <stdio.h>
  191. X#include <sys/types.h>
  192. X#include <sys/stat.h>
  193. X#include <pwd.h>
  194. X
  195. X#define WATCH(s,x)    if(x){perror(s);return(-1);}
  196. X
  197. Xchar *PASSWD = "/etc/passwd";
  198. X#ifndef DEBUG
  199. Xchar *BACKUP = "/etc/passwd.bak";
  200. Xchar *LOCK = "/etc/vipw.lock";
  201. Xchar *TEMP = "/etc/ptmp";
  202. Xchar *BAD_TEMP = "/etc/ptmp.bad";
  203. X#else
  204. Xchar *LOCK = "vipw.lock";
  205. Xchar *TEMP = "ptmp";
  206. Xchar *BAD_TEMP = "ptmp.bad";
  207. X#endif DEBUG
  208. Xchar ArgBuf[128];
  209. Xchar *Arg = ArgBuf;
  210. X
  211. Xvoid endpwent(), perror();
  212. Xchar *crypt(), *getpass(), *mktemp();
  213. Xstruct passwd *getpwent(), *getpwnam(), *fgetpwent();
  214. Xchar *index();
  215. X
  216. Xmain(argc, argv)
  217. Xint argc;
  218. Xchar **argv;
  219. X{
  220. X    register int i;
  221. X    register struct passwd *p;
  222. X    FILE *fout;
  223. X    int target_id;            /* Who are we changing? */
  224. X    struct stat stat_buf;
  225. X    long  OldLen, NewLen;        /* Old/New length of passwd */
  226. X    long LenDiff;            /* Expected length dif. */
  227. X    int ShellMode;            /* True if chsh */
  228. X    char *UserName;            /* Who are we working for */
  229. X
  230. X    if( strcmp(argv[0], "chsh") == 0 ) ShellMode = 1; else
  231. X    if( strcmp(argv[0], "chfn") != 0 ) {
  232. X        fprintf(stderr,"Sorry, program name should be 'chsh' or 'chfn'.\n");
  233. X        exit(1);
  234. X    }
  235. X
  236. X    if( argc >= 2 ) {    /* Login name given */
  237. X        UserName = argv[1];
  238. X    } else {
  239. X        UserName = getlogin();
  240. X        printf("Changing %s for %s\n", ShellMode ? "login shell":
  241. X            "real name", UserName);
  242. X    }
  243. X
  244. X    /* is this person real? */
  245. X
  246. X    if ((p = getpwnam(UserName)) == NULL) {
  247. X        (void) fprintf(stderr, "%s: don't know %s\n",
  248. X            argv[0], UserName);
  249. X        return(-1);
  250. X    }    /* if person isn't real */
  251. X
  252. X    /* do we have permission to do this? */
  253. X    target_id = p->pw_uid;
  254. X
  255. X    if ((i = getuid()) != 0 && i != target_id) {
  256. X#ifdef SECURE
  257. X        fprintf(stderr,"Sorry, you don't have permission to do that.\n");
  258. X        exit(1);
  259. X#else
  260. X        char salt[3];
  261. X
  262. X        salt[0] = p->pw_passwd[0];
  263. X        salt[1] = p->pw_passwd[1];
  264. X        salt[3] = '\0';
  265. X
  266. X        if (*p->pw_passwd != '\0'
  267. X            && strncmp(crypt(getpass("Password: "), salt),
  268. X            p->pw_passwd, 8)) {
  269. X            (void) fprintf(stderr, "Sorry.\n");
  270. X            return(-1);
  271. X        }    /* passwd didn't match */
  272. X#endif SECURE
  273. X    }    /* check for permission */
  274. X
  275. X    /* If in verbose mode, print old info */
  276. X    if( argc <= 2 ) {
  277. X        if( ShellMode ) {
  278. X        printf("Old shell: %s\n", p->pw_shell?p->pw_shell:"");
  279. X        printf("New shell: ");
  280. X        gets(Arg);
  281. X        } else {
  282. X        printf("Old name: %s\n", p->pw_gecos?p->pw_gecos:"");
  283. X        printf("New name: ");
  284. X        gets(Arg);
  285. X        }
  286. X    } else {
  287. X        Arg = argv[2];
  288. X    }
  289. X
  290. X    /* Check for dirty characters */
  291. X    if( index(Arg, '\n') || index(Arg, ':') ) {
  292. X        fprintf(stderr,"%s: Dirty characters in argument.\n",argv[0]);
  293. X        exit(1);
  294. X    }
  295. X
  296. X    /* Check that the shell sounds reasonable */
  297. X    if( ShellMode ) {
  298. X        if( *Arg != '/' ) {
  299. X        fprintf(stderr,"%s: shell name should be full path.\n",Arg);
  300. X        exit(1);
  301. X        }
  302. X        WATCH(Arg,stat(Arg,&stat_buf));
  303. X        if( (stat_buf.st_mode & 0111) == 0 ) {
  304. X        fprintf(stderr,"%s is not an executable.\n");
  305. X        exit(1);
  306. X        }
  307. X    }
  308. X
  309. X    /* set up files */
  310. X
  311. X    endpwent();    /* close passwd file */
  312. X
  313. X    setpwent();
  314. X
  315. X    /* Now, lock the password file */
  316. X    creat(LOCK,0600);    /* This might fail. No problem */
  317. X    if( link(LOCK,TEMP) < 0 ) {
  318. X        fprintf(stderr,"Sorry, password file busy.\n");
  319. X        exit(1);
  320. X    }
  321. X    WATCH(TEMP,(fout = fopen(TEMP, "w")) == NULL);
  322. X
  323. X    while ((p = getpwent()) != NULL) {
  324. X        if (p->pw_uid == target_id) {
  325. X            if (!ShellMode ) {
  326. X                LenDiff = strlen(Arg)-strlen(p->pw_gecos);
  327. X                p->pw_gecos = Arg;
  328. X            } else {
  329. X                LenDiff = (-strlen(p->pw_shell));
  330. X                p->pw_shell = Arg == NULL ? "/bin/sh"
  331. X                    : Arg;
  332. X                LenDiff += strlen(p->pw_shell);
  333. X            }
  334. X        }    /* if this is entry to be changed */
  335. X
  336. X        WATCH("putpwent",putpwent(p, fout));
  337. X    }    /* while not eof (we couldn't recognize an error) */
  338. X
  339. X    /* close files */
  340. X    endpwent();
  341. X    fclose(fout);
  342. X
  343. X    /* Check that sizes are as expected */
  344. X    WATCH(TEMP, stat(TEMP, &stat_buf) );
  345. X    NewLen = stat_buf.st_size;
  346. X    WATCH(PASSWD, stat(PASSWD, &stat_buf) );
  347. X    OldLen = stat_buf.st_size;
  348. X    if( OldLen + LenDiff != NewLen ) {
  349. X        fprintf(stderr,"Sorry, password file changed size: %ld, expected %ld.\n", NewLen, OldLen+LenDiff);
  350. X        fprintf(stderr,"Warn your system administrator, please.\n");
  351. X        WATCH(TEMP, link(TEMP,BAD_TEMP));
  352. X        WATCH(TEMP,unlink(TEMP));
  353. X        WATCH(LOCK,unlink(LOCK));
  354. X        exit(1);
  355. X    }
  356. X
  357. X#ifndef DEBUG
  358. X    /* remove old backup if it exists */
  359. X    WATCH(BACKUP,!stat(BACKUP, &stat_buf) && unlink(BACKUP));
  360. X
  361. X    /* make current passwd file backup */
  362. X    WATCH("linking passwd to passwd.bak",link(PASSWD, BACKUP) || unlink(PASSWD));
  363. X
  364. X    /* make new file passwd */
  365. X    WATCH("linking temp to passwd",link(TEMP, PASSWD) || unlink(TEMP));
  366. X    WATCH("chmod passwd", chmod(PASSWD, 0644));
  367. X#endif DEBUG
  368. X
  369. X    /* Remove lock */
  370. X    WATCH(LOCK,unlink(LOCK));
  371. X
  372. X#ifdef DEBUG
  373. X    printf("Now, check that %s looks reasonable.\n", TEMP);
  374. X#endif DEBUG
  375. X    /* must have succeeded */
  376. X    return(0);
  377. X}    /* main */
  378. X
  379. X#ifdef NOPUTPWENT
  380. Xputpwent(ent, fp)
  381. X    FILE *fp;
  382. X    struct passwd *ent;
  383. X    {
  384. X
  385. X    fprintf(fp,"%s:%s:%d:%d:%s:%s:%s\n", ent->pw_name, ent->pw_passwd,
  386. X    ent->pw_uid, ent->pw_gid, ent->pw_gecos, ent->pw_dir,
  387. X    ent->pw_shell);
  388. X    return(0);
  389. X}
  390. X#endif NOPUTPWENT
  391. EndOfFile
  392. exit
  393.  
  394.  
  395.