home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2127 < prev    next >
Internet Message Format  |  1990-12-28  |  15KB

  1. From: df@death.cert.sei.cmu.edu (Dan Farmer)
  2. Newsgroups: alt.sources
  3. Subject: (anonymous) ftp checker, cops, etc.
  4. Message-ID: <DF.90Nov20174708@death.cert.sei.cmu.edu>
  5. Date: 20 Nov 90 22:47:08 GMT
  6.  
  7.  
  8.   Thought I'd toss this out to the sharks.  This is a shar file with a shell
  9. script and an C program that attempts to check for proper anonymous ftp
  10. setup.  Comments are encouraged; after a bit, I'll put out another patch to
  11. COPS with this and other stuff in it (to forstall questions, cops is a
  12. security checking thingee that is available via anon-ftp from cert.sei.cmu.edu,
  13. and uunet.uu.net, or other fine archive sites near you.)
  14.  
  15.  -- dan
  16.     df@cert.sei.cmu.edu
  17.  
  18. (More or less what the man page will say:)
  19.  
  20.   This shell script checks to see if you've set up (mainly anonymous)
  21. ftp correctly.  The "-a" option checks your anon-ftp setup; without that,
  22. this script doesn't do a whole lot -- just check to see if your ftpusers
  23. file doesn't have any root accounts in it.
  24.  
  25.   There is no "right" way to set up ftp, but there are lots of wrong
  26. ways :-)   I suggest everything be owned by either root or ftp, and nothing
  27. be world writable, with the exception of a ~ftp/incoming directory or
  28. something like that.  You can change the owners via the $primary and
  29. $secondary variables (default root & ftp, respectively), and the publically
  30. writable directory is $incoming (default ~ftp/incoming).  Do not make
  31. ~ftp/pub world writable, if you are storing data or programs for people
  32. to use; you're inviting intruders to write all over the files and programs,
  33. and leave all kinds of nasties...
  34.  
  35.   Here are the assumptions I made for anon-ftp:
  36.  
  37. o  User "ftp" should have a non-valid password ("*", whatever) and a invalid
  38.   shell, but a valid home directory -- this is where all the anonymous
  39.   stuff gets stashed.  This checks for the passwd and valid home dir only.
  40.   I would suggest a .rhosts file of 0 size, owned by root, but that's
  41.   personal preference.  This will complain if a .rhosts file exists, and
  42.   is either non-0 or non-root owned.
  43.  
  44. o  All root equivalent accounts (uid=0) with valid passwords should be in
  45.   /etc/ftpusers 
  46.  
  47. o  The home dir for ftp is in /etc/passwd, should be a valid directory, and
  48.   should not be "/" (if the dir is invalid, ftpd should choke.)
  49.  
  50. o  The ~ftp/etc/{passwd|group} files should be different than their
  51.   counterparts in /etc (don't want password files available via anon-ftp.)
  52.   In addition, it seems as though the entries in ~ftp/etc/{passwd|group}
  53.   files don't do a whole lot -- you might see something like:
  54.      With the entries:
  55.         drwxr-xr-x  8 cert    ftp           512 Nov  7 16:56 pub/
  56.      Without:
  57.         drwxr-xr-x  8 8001    105           512 Nov  7 16:56 pub/
  58.   Some versions of ftpd allow you to leave the files off entirely; that
  59.   is the preferred method, IMHO; else, you might try putting a null file
  60.   there.  Experiment...
  61.  
  62. o  ~ftp, ~ftp/bin, ~/ftp/etc should all be non-world-writeable, and owned
  63.   by either root or ftp.  The ls command should be mode 111, the password
  64.   and group files 444.
  65.  
  66.  
  67.  
  68.  Finally, the shar:
  69.  
  70. ================Cut here with pointy sharp things=============================
  71. #!/bin/sh
  72. # This is a shell archive (shar 3.10)
  73. # made 11/20/1990 20:09 UTC by df@death.cert.sei.cmu.edu
  74. # Source directory /tmp/junk
  75. #
  76. # existing files WILL be overwritten
  77. #
  78. # This shar contains:
  79. # length  mode       name
  80. # ------ ---------- ------------------------------------------
  81. #   5930 -rwx------ ftp.chk
  82. #   4196 -rw------- is_able.c
  83. #
  84. touch 2>&1 | fgrep '[-amc]' > /tmp/s3_touch$$
  85. if [ -s /tmp/s3_touch$$ ]
  86. then
  87.     TOUCH=can
  88. else
  89.     TOUCH=cannot
  90. fi
  91. rm -f /tmp/s3_touch$$
  92. # ============= ftp.chk ==============
  93. sed 's/^X//' << 'SHAR_EOF' > ftp.chk &&
  94. X:
  95. X#!/bin/sh
  96. X#
  97. X#  Usage: ftp.chk [-a]
  98. X#
  99. X#   This shell script checks to see if you've set up (mainly anonymous)
  100. X# ftp correctly.  The "-a" option checks your anon-ftp setup; without that,
  101. X# this script doesn't do a whole lot -- just check to see if your ftpusers
  102. X# file doesn't have any root accounts in it.
  103. X#
  104. X#   See the man page for a more detailed description, but here's what this
  105. X# checks for:
  106. X#
  107. X# - User ftp exists in the password file.
  108. X# - root (or all root equivalents) are in ftpusers file.
  109. X# - Home directory for ftp should exist, and not be /
  110. X# - The ~ftp/etc/{passwd|group} should not be the same as the real ones.
  111. X# - Various critical files/directories should exist, and have correct
  112. X#   permissions and owners:
  113. X#
  114. X#  File/Dir          Perms           Owner      Other
  115. X#  =========         ======          ======     ======
  116. X#  ~ftp              555             ftp
  117. X#           or
  118. X#  ~ftp              non-w.w.        root
  119. X#
  120. X#  ~ftp/bin          non-w.w.        root/ftp
  121. X#  ~ftp/bin/ls       111             root/ftp
  122. X#  ~ftp/etc          non-w.w.        root/ftp     non-w.w.        root       0 size, is optional
  123. X#  ~ftp/*            non-w.w.                   other dirs/files in ~ftp
  124. X#
  125. X
  126. X# Where is everyone?
  127. XECHO=/bin/echo
  128. XTEST=/bin/test
  129. XAWK=/bin/awk
  130. XGREP=/bin/grep
  131. XLS=/bin/ls
  132. XCMP=/bin/cmp
  133. X
  134. X#  If an argument is present, it should be an "a"
  135. Xif $TEST $# -gt 1 ; then
  136. X    $ECHO Usage: $0 [-a]
  137. X    exit 1
  138. X    fi
  139. Xif $TEST $# -eq 1 ; then
  140. X    if $TEST $1 = "-a" ; then
  141. X            anonymous=yes
  142. X    else
  143. X        $ECHO Usage: $0 [-a]
  144. X        exit 1
  145. X        fi
  146. X    fi
  147. X
  148. X#
  149. X# some might have this as ftpd; is the account in /etc/passwd
  150. Xftpuid=ftp
  151. X
  152. X# system files
  153. Xftpusers=/etc/ftpusers
  154. Xpasswd=/etc/passwd
  155. Xgroup=/etc/group
  156. X
  157. X#   ftp's files:
  158. Xftproot=`$AWK -F: '/^'"$ftpuid"':/{print $6}' $passwd`
  159. X
  160. X#   if the user ftp doesn't exist, no-anon stuff....
  161. Xif $TEST -z $ftproot -a "$anonymous" = "yes" ; then
  162. X    $ECHO Warning!  Need user $ftp for anonymous ftp to work!
  163. X    exit
  164. X    fi
  165. X
  166. Xftprhosts=$ftproot/.rhosts
  167. Xftpbin=$ftproot"/bin"
  168. Xftpls=$ftpbin"/ls"
  169. Xftpetc=$ftproot"/etc"
  170. Xftppasswd=$ftpetc"/passwd"
  171. Xftpgroup=$ftpetc"/group"
  172. X
  173. X#   the pub/incoming stuff; by default, pub is *not* world writable, incoming
  174. X# is; if you want pub to be world writable, just change incoming to "pub"
  175. Xincoming=incoming
  176. Xftpincoming=$ftproot"/"$incoming
  177. Xftppub=$ftproot"/pub"
  178. X
  179. Xcrit_files="$ftpgroup $ftppasswd $ftpls"
  180. X
  181. X# primary and secondary owners...
  182. Xprimary=root
  183. Xsecondary=ftp
  184. X
  185. Xif $TEST -s $ftpusers
  186. X    then
  187. X    # check to see if root (or root equivalents) is in ftpusers file
  188. X    all_roots=`$AWK -F: '{if ($3==0 && length($2)==13) printf("%s ", $1)}' $passwd`
  189. X    if $TEST -n "$all_roots" ; then
  190. X        for i in $all_roots
  191. X            do
  192. X            if $TEST ! "`$GREP $i $ftpusers`"
  193. X                then
  194. X                $ECHO Warning!  $i should be in $ftpusers!
  195. X                fi
  196. X            done
  197. X        fi
  198. X    fi
  199. X
  200. X#  do the anonymous ftp checking stuff now
  201. Xif $TEST -n "$anonymous" ; then
  202. X    #
  203. X    #  ftp's home dir checking
  204. X    if $TEST ! -d "$ftproot" -o -z "$ftproot"; then
  205. X        $ECHO Warning!  Home directory for ftp doesn\'t exist!
  206. X        fi
  207. X    if $TEST "$ftproot" = "/" ; then
  208. X        $ECHO Warning!  $ftproot ftp\'s home directory should not be \"/\"!
  209. X        fi
  210. X    #
  211. X    #  Don't want the passwd and group files to be the real ones!
  212. X    if $TEST "`$CMP $passwd $ftppasswd 2> /dev/null`" ; then
  213. X        :
  214. X    else $ECHO Warning!  $ftppasswd and $passwd are the same!
  215. X        fi
  216. X    if $TEST "`$CMP $group $ftpgroup 2> /dev/null`" ; then
  217. X        :
  218. X        else $ECHO Warning!  $ftpgroup and $group are the same!
  219. X        fi
  220. X
  221. X    #   want to check all the critical files and directories for correct
  222. X    # ownership.
  223. X    #
  224. X    #  This is what a "/bin/ls -l" of a file should look like:
  225. X    # ---x--x--x  1 root        81920 Dec 31  1999 /bin/ls
  226. X    #  So in awk, $3 is the owner, $1 is the permission.
  227. X    #
  228. X    crit_files=$crit_files" "$ftpbin" "$ftpetc
  229. X    for i in $crit_files
  230. X        do
  231. X        if $TEST ! -f $i -a ! -d $i; then
  232. X            $ECHO Warning!  File $i is missing!
  233. X            fi
  234. X
  235. X        owner=`$LS -ld $i | $AWK '{print $3}'`
  236. X        if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
  237. X            :
  238. X        else
  239. X            $ECHO Warning!  $i should be owned by $primary or $secondary!
  240. X            fi
  241. X        done
  242. X
  243. X    #   ftproot is special; if owned by root; should be !world writable;
  244. X    # if owned by ftp, should be mode 555
  245. X    owner=`$LS -ld $ftproot | $AWK '{print $3}'`
  246. X    perms=`$LS -ld $ftproot | $AWK '{print $1}'`
  247. X    if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
  248. X        :
  249. X    else
  250. X        $ECHO Warning!  $i should be owned by $primary or $secondary!
  251. X    fi
  252. X
  253. X    if $TEST "$owner" = "$primary" ; then
  254. X        ./is_able $ftproot w w
  255. X    elif $TEST "$owner" != "$secondary" ; then
  256. X        $ECHO Warning!  $ftproot should be owned by $primary or $secondary!
  257. X    elif $TEST "$perms" != "dr-xr-xr-x" ; then
  258. X        $ECHO Warning!  $ftproot should be mode 555!
  259. X        fi
  260. X
  261. X    #
  262. X    # check the .rhosts file:
  263. X    if $TEST -f $ftprhosts ; then
  264. X        if $TEST -s $ftprhosts ; then
  265. X            $ECHO Warning!  $ftprhosts should be be empty!
  266. X            fi
  267. X        owner=`$LS -ld $ftprhosts | $AWK '{print $3}'`
  268. X        if $TEST "$owner" = "$primary" -o "$owner" = "$secondary" ; then
  269. X            :
  270. X        else
  271. X            $ECHO Warning!  $ftprhosts should be owned by $primary or $secondary!
  272. X            fi
  273. X        fi
  274. X
  275. X    #
  276. X    # finally, some permissions of miscellaneous files:
  277. X    perms=`$LS -ld $ftpls | $AWK '{print $1}'`
  278. X    if $TEST "$perms" != "---x--x--x" ; then
  279. X        $ECHO Warning!  Incorrect permissions on \"ls\" in $ftpbin!
  280. X        fi
  281. X
  282. X    perms=`$LS -ld $ftppasswd | $AWK '{print $1}'`
  283. X    if $TEST "$perms" != "-r--r--r--" ; then
  284. X        $ECHO Warning!  Incorrect permissions on \"passwd\" in $ftpetc!
  285. X        fi
  286. X
  287. X    perms=`$LS -ld $ftpgroup | $AWK '{print $1}'`
  288. X    if $TEST "$perms" != "-r--r--r--" ; then
  289. X        $ECHO Warning!  Incorrect permissions on \"group\" in $ftpetc!
  290. X        fi
  291. X
  292. X    #   Finally, the ~ftp/{pub|incoming|whatever} stuff; don't want the
  293. X    # the number of blocks, or the "." and ".." entries:
  294. X    all_dirs=`$LS -al $ftproot | $AWK '{if (NF >= 8) if ($NF!="." && $NF!="..") print $NF}'`
  295. X    for i in $all_dirs
  296. X        do
  297. X        if $TEST -n "`is_able $ftproot/$i w w`" -a $i != "$ftpincoming" ; then
  298. X            $ECHO Warning!  Anon-ftp directory $i is World Writable!
  299. X            fi
  300. X        done
  301. X    fi
  302. X
  303. X# end of script
  304. SHAR_EOF
  305. chmod 0700 ftp.chk || echo "restore of ftp.chk fails"
  306. if [ $TOUCH = can ]
  307. then
  308.     touch -am 1120145290 ftp.chk
  309. fi
  310. # ============= is_able.c ==============
  311. sed 's/^X//' << 'SHAR_EOF' > is_able.c &&
  312. X/*
  313. X    Usage: is_able filename {W|w|G|g}     {R|r|W|w|B|b}
  314. X                            world/group   read/write/both
  315. X
  316. X    This checks determines whether a file is (group or world)
  317. X  writable or readable, and prints out a short message to
  318. X  that effect.
  319. X
  320. X      Permissions bits:          vvv--- Permission bits
  321. X               1 = execute        00000
  322. X               2 = writable         ^
  323. X               4 = readable         + Setuid bits
  324. X
  325. X      Setuid bits:
  326. X               1 = sticky
  327. X               2 = set group id
  328. X               4 = set user od
  329. X
  330. X    Pete Shipley (shipley@mica.berkeley.edu) gutted my original code,
  331. X  made in cleaner and smarter, and combined everything into one compact
  332. X  file.  What a deal, huh?  Then I came along and beat up his code and
  333. X  made it look ugly again (I changed the is_writeable option to return
  334. X  true if _any_ parent directories are writable, not just the target.  So
  335. X  you can blame me if you want.  Better yet, just send me a patch if I
  336. X  blew it.)
  337. X
  338. X*/
  339. X
  340. X#include <sys/types.h>
  341. X#include <sys/stat.h>
  342. X#include <ctype.h>
  343. X#include <stdio.h>
  344. X
  345. X#define G_READ_TEST 00044    /* group (or world) readable */
  346. X#define W_READ_TEST 00004    /* world readable */
  347. X#define G_READ_STRING    "Warning!  %s is group readable!\n"
  348. X#define W_READ_STRING    "Warning!  %s is _World_ readable!\n"
  349. X
  350. X#define G_WRITE_TEST 00022    /* group (or world) writable */
  351. X#define W_WRITE_TEST 00002    /* world writable */
  352. X#define G_WRITE_STRING    "Warning!  %s is group writable!\n"
  353. X#define W_WRITE_STRING    "Warning!  %s is _World_ writable!\n"
  354. X
  355. Xmain(argc,argv)
  356. Xint argc;
  357. Xchar **argv;
  358. X{
  359. Xchar file[256], wg, rwb, gstring[35],wstring[35];
  360. Xregister int group, read, write, both, verbose, xmode;
  361. Xstatic struct stat statb;
  362. X
  363. Xgroup=read=write=both=verbose=xmode=0;
  364. X
  365. X/* check out arguments */
  366. Xif (argc != 4) {
  367. X    fprintf(stderr, "Usage: %s file {W|w|G|g} {R|r|W|w|B|b}\n",argv[0]);
  368. X    exit(1);
  369. X    }
  370. X
  371. X/* parse arguments */
  372. Xstrcpy(file, argv[1]);
  373. X
  374. X/* get stats on file in question -- if doesn't exist, exit */
  375. Xif (stat(file,&statb) < 0) {
  376. X    perror(file);
  377. X    exit(2);
  378. X    }
  379. X
  380. Xif (isupper(argv[2][0]))
  381. X    wg = tolower(argv[2][0]);    /* world or group */
  382. Xelse    wg = argv[2][0];        /* world or group */
  383. X
  384. Xif (isupper(argv[3][0]))
  385. X    rwb  = tolower(argv[3][0]);    /* read/write/both */
  386. Xelse    rwb  = argv[3][0];        /* read/write/both */
  387. X
  388. X/* set the report string and some flags */
  389. Xif (wg == 'g') group = 1;
  390. X
  391. Xif (rwb == 'r') {
  392. X    if (group) strcpy(gstring, G_READ_STRING);
  393. X    else strcpy(wstring, W_READ_STRING);
  394. X    read = 1;
  395. X    }
  396. Xelse if (rwb == 'w') {
  397. X    if (group) strcpy(gstring, G_WRITE_STRING);
  398. X    else strcpy(wstring, W_WRITE_STRING);
  399. X    write = 1;
  400. X    }
  401. Xelse if (rwb == 'b') {
  402. X    /* do the write first, then read check */
  403. X    if (group) strcpy(gstring, G_WRITE_STRING);
  404. X    else strcpy(wstring, W_WRITE_STRING);
  405. X    both = read = write = 1;
  406. X    }
  407. X
  408. X/*
  409. X *         the write stuff, so to speak...
  410. X *   What I'm doing in this mess is to parse the file in question, check out
  411. X * whole path; 'cause if anything is world writable, you can compromise.
  412. X * For instance, if /usr is world writable, then /usr/spool/mail is
  413. X * compromisable, no matter what its permissions are.
  414. X *
  415. X*/
  416. Xif (write) {
  417. X    /* 256 levels of dirs, max len each 256 chars */
  418. X    char foo_dirs[256][256];
  419. X    char *foo_file;
  420. X    int i = 0, j;
  421. X
  422. X    foo_file = file;
  423. X    strcpy(foo_dirs[i++], foo_file);
  424. X
  425. X    j=strlen(foo_file) - 1;
  426. X    do {
  427. X        if (foo_file[j] == '/')
  428. X            strncpy(foo_dirs[i++], foo_file, j);
  429. X    } while (--j > 0);
  430. X
  431. X    for (j = 0; j < i; j++) {
  432. X        if (stat(foo_dirs[j],&statb) < 0)
  433. X            continue;
  434. X        xmode=statb.st_mode;
  435. X        if (!group) {
  436. X            if (xmode & W_WRITE_TEST) {
  437. X                printf( wstring, file);
  438. X                if (both) goto bboth;
  439. X                exit(!xmode);
  440. X                }
  441. X            }
  442. X        else if (xmode & G_WRITE_TEST) {
  443. X            printf(gstring, file);
  444. X            if (both) goto bboth;
  445. X            exit(!xmode);
  446. X            }
  447. X        }
  448. X
  449. Xif (!both) exit(!xmode);
  450. X}
  451. X
  452. Xbboth: if (rwb == 'b') {
  453. X    /* do the read now */
  454. X    if (group) strcpy(gstring, G_READ_STRING);
  455. X    else strcpy(wstring, W_READ_STRING);
  456. X    }
  457. X
  458. X/*
  459. X * For the rest -- only flag if the file/dir in question passes test; you
  460. X * don't care about the rest of it's tree
  461. X *
  462. X*/
  463. X
  464. X/* test premissions on file in question */
  465. Xif (group)
  466. X    xmode = statb.st_mode & G_READ_TEST;
  467. Xelse 
  468. X    xmode = statb.st_mode & W_READ_TEST;
  469. X
  470. X/* report finding */
  471. Xif (xmode) printf( (group ? gstring : wstring), file);
  472. X
  473. Xexit(!xmode);
  474. X}
  475. SHAR_EOF
  476. chmod 0600 is_able.c || echo "restore of is_able.c fails"
  477. if [ $TOUCH = can ]
  478. then
  479.     touch -am 1120144490 is_able.c
  480. fi
  481. exit 0
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.  
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496. --
  497.   -- dan
  498.  
  499.   df@cert.sei.cmu.edu
  500.