diff --git a/en_US.ISO8859-1/books/developers-handbook/kerneldebug/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/kerneldebug/chapter.sgml index 53f1637ef3..eaf5be9553 100644 --- a/en_US.ISO8859-1/books/developers-handbook/kerneldebug/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/kerneldebug/chapter.sgml @@ -1,655 +1,655 @@ Kernel Debugging Contributed by &a.paul; and &a.joerg; Debugging a Kernel Crash Dump with <command>gdb</command> Here are some instructions for getting kernel debugging working on a crash dump. They assume that you have enough swap space for a crash dump. Typically you want to specify one of the swap devices specified in /etc/fstab. Dumps to non-swap devices, tapes for example, are currently not supported. Use the &man.dumpon.8; command to tell the kernel where to save crash dumps. The dumpon program must be called after the swap partition has been configured with &man.swapon.8;. This is normally arranged by setting the dumpdev variable in &man.rc.conf.5;. If this variable is set, then the &man.savecore.8; program will automatically be called on the first multi-user boot after the crash. This program will save the kernel crash dump to the directory specified in the rc.conf dumpdir variable. The default directory for crash dumps is /var/crash. Alternatively, you can hard-code the dump device via the dump clause in the config line of your kernel configuration file. This approach is deprecated and should be used only if you want a crash dump from a kernel that crashes during booting. In the following, the term gdb refers to the debugger gdb run in kernel debug mode. This can be accomplished by starting the gdb with the option . In kernel debug mode, gdb changes its prompt to (kgdb). If you are using FreeBSD 3 or earlier, you should make a stripped copy of the debug kernel, rather than installing the large debug kernel itself: &prompt.root; cp kernel kernel.debug &prompt.root; strip -g kernel This stage is not necessary, but it is recommended. (In FreeBSD 4 and later releases this step is performed automatically at the end of the kernel make process.) When the kernel has been stripped, either automatically or by using the commands above, you may install it as usual by typing make install. Note that older releases of FreeBSD (up to but not including 3.1) used a.out kernels by default, which must have their symbol tables permanently resident in physical memory. With the larger symbol table in an unstripped debug kernel, this is wasteful. Recent FreeBSD releases use ELF kernels where this is no longer a problem. If you are testing a new kernel, for example by typing the new kernel's name at the boot prompt, but need to boot a different one in order to get your system up and running again, boot it only into single user state using the flag at the boot prompt, and then perform the following steps: &prompt.root; fsck -p &prompt.root; mount -a -t ufs # so your filesystem for /var/crash is writable &prompt.root; savecore -N /kernel.panicked /var/crash &prompt.root; exit # ...to multi-user This instructs &man.savecore.8; to use another kernel for symbol name extraction. It would otherwise default to the currently running kernel and most likely not do anything at all since the crash dump and the kernel symbols differ. Now, after a crash dump, go to /sys/compile/WHATEVER and run gdb . From gdb do: symbol-file kernel.debug exec-file /var/crash/kernel.0 core-file /var/crash/vmcore.0 and voila, you can debug the crash dump using the kernel sources just like you can for any other program. Here is a script log of a gdb session illustrating the procedure. Long lines have been folded to improve readability, and the lines are numbered for reference. Despite this, it is a real-world error trace taken during the development of the pcvt console driver. 1:Script started on Fri Dec 30 23:15:22 1994 2:&prompt.root; cd /sys/compile/URIAH 3:&prompt.root; gdb -k kernel /var/crash/vmcore.1 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel ...done. 5:IdlePTD 1f3000 6:panic: because you said to! 7:current pcb at 1e3f70 8:Reading in symbols for ../../i386/i386/machdep.c...done. 9:(kgdb) where 10:#0 boot (arghowto=256) (../../i386/i386/machdep.c line 767) 11:#1 0xf0115159 in panic () 12:#2 0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698) 13:#3 0xf010185e in db_fncall () 14:#4 0xf0101586 in db_command (-266509132, -266509516, -267381073) 15:#5 0xf0101711 in db_command_loop () 16:#6 0xf01040a0 in db_trap () 17:#7 0xf0192976 in kdb_trap (12, 0, -272630436, -266743723) 18:#8 0xf019d2eb in trap_fatal (...) 19:#9 0xf019ce60 in trap_pfault (...) 20:#10 0xf019cb2f in trap (...) 21:#11 0xf01932a1 in exception:calltrap () 22:#12 0xf0191503 in cnopen (...) 23:#13 0xf0132c34 in spec_open () 24:#14 0xf012d014 in vn_open () 25:#15 0xf012a183 in open () 26:#16 0xf019d4eb in syscall (...) 27:(kgdb) up 10 28:Reading in symbols for ../../i386/i386/trap.c...done. 29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\ 30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\ 31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\ 32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\ 33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\ 34:ss = -266427884}) (../../i386/i386/trap.c line 283) 35:283 (void) trap_pfault(&frame, FALSE); 36:(kgdb) frame frame->tf_ebp frame->tf_eip 37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done. 38:#0 0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\ 39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403) 40:403 return ((*linesw[tp->t_line].l_open)(dev, tp)); 41:(kgdb) list 42:398 43:399 tp->t_state |= TS_CARR_ON; 44:400 tp->t_cflag |= CLOCAL; /* cannot be a modem (:-) */ 45:401 46:402 #if PCVT_NETBSD || (PCVT_FREEBSD >= 200) 47:403 return ((*linesw[tp->t_line].l_open)(dev, tp)); 48:404 #else 49:405 return ((*linesw[tp->t_line].l_open)(dev, tp, flag)); 50:406 #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */ 51:407 } 52:(kgdb) print tp 53:Reading in symbols for ../../i386/i386/cons.c...done. 54:$1 = (struct tty *) 0x1bae 55:(kgdb) print tp->t_line 56:$2 = 1767990816 57:(kgdb) up 58:#1 0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\ 59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126) 60: return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p)); 61:(kgdb) up 62:#2 0xf0132c34 in spec_open () 63:(kgdb) up 64:#3 0xf012d014 in vn_open () 65:(kgdb) up 66:#4 0xf012a183 in open () 67:(kgdb) up 68:#5 0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\ 69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\ 70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \ 71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \ 72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673) 73:673 error = (*callp->sy_call)(p, args, rval); 74:(kgdb) up 75:Initial frame selected; you cannot go up. 76:(kgdb) quit 77:&prompt.root; exit 78:exit 79: 80:Script done on Fri Dec 30 23:18:04 1994 Comments to the above script: line 6: This is a dump taken from within DDB (see below), hence the panic comment because you said to!, and a rather long stack trace; the initial reason for going into DDB has been a page fault trap though. line 20: This is the location of function trap() in the stack trace. line 36: Force usage of a new stack frame; this is no longer necessary. The stack frames are supposed to point to the right locations now, even in case of a trap. From looking at the code in source line 403, there is a high probability that either the pointer access for tp was messed up, or the array access was out of bounds. line 52: The pointer looks suspicious, but happens to be a valid address. line 56: However, it obviously points to garbage, so we have found our error! (For those unfamiliar with that particular piece of code: tp->t_line refers to the line discipline of the console device here, which must be a rather small integer number.) Debugging a Crash Dump with DDD Examining a kernel crash dump with a graphical debugger like ddd is also possible (you will need to install the devel/ddd port in order to use the ddd debugger). Add the option to the ddd command line you would use normally. For example; &prompt.root; ddd -k /var/crash/kernel.0 /var/crash/vmcore.0 You should then be able to go about looking at the crash dump using ddd's graphical interface. Post-Mortem Analysis of a Dump What do you do if a kernel dumped core but you did not expect it, and it is therefore not compiled using config -g? Not everything is lost here. Do not panic! Of course, you still need to enable crash dumps. See above for the options you have to specify in order to do this. Go to your kernel config directory (/usr/src/sys/arch/conf) and edit your configuration file. Uncomment (or add, if it does not exist) the following line: makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols Rebuild the kernel. Due to the time stamp change on the Makefile, some other object files will be rebuilt, for example trap.o. With a bit of luck, the added option will not change anything for the generated code, so you will finally get a new kernel with similar code to the faulting one but some debugging symbols. You should at least verify the old and new sizes with the &man.size.1; command. If there is a mismatch, you probably need to give up here. Go and examine the dump as described above. The debugging symbols might be incomplete for some places, as can be seen in the stack trace in the example above where some functions are displayed without line numbers and argument lists. If you need more debugging symbols, remove the appropriate object files, recompile the kernel again and repeat the gdb session until you know enough. All this is not guaranteed to work, but it will do it fine in most cases. On-Line Kernel Debugging Using DDB While gdb as an off-line debugger provides a very high level of user interface, there are some things it cannot do. The most important ones being breakpointing and single-stepping kernel code. If you need to do low-level debugging on your kernel, there is an on-line debugger available called DDB. It allows setting of breakpoints, single-stepping kernel functions, examining and changing kernel variables, etc. However, it cannot access kernel source files, and only has access to the global and static symbols, not to the full debug information like gdb does. To configure your kernel to include DDB, add the option line options DDB to your config file, and rebuild. (See The FreeBSD Handbook for details on configuring the FreeBSD kernel). If you have an older version of the boot blocks, your debugger symbols might not be loaded at all. Update the boot blocks; - the recent ones load the DDB symbols automagically. + the recent ones load the DDB symbols automatically. Once your DDB kernel is running, there are several ways to enter DDB. The first, and earliest way is to type the boot flag right at the boot prompt. The kernel will start up in debug mode and enter DDB prior to any device probing. Hence you can even debug the device probe/attach functions. The second scenario is to drop to the debugger once the system has booted. There are two simple ways to accomplish this. If you would like to break to the debugger from the command prompt, simply type the command: &prompt.root; sysctl debug.enter_debugger=ddb Alternatively, if you are at the system console, you may use a hot-key on the keyboard. The default break-to-debugger sequence is Ctrl AltESC. For syscons, this sequence can be remapped and some of the distributed maps out there do this, so check to make sure you know the right sequence to use. There is an option available for serial consoles that allows the use of a serial line BREAK on the console line to enter DDB (options BREAK_TO_DEBUGGER in the kernel config file). It is not the default since there are a lot of serial adapters around that gratuitously generate a BREAK condition, for example when pulling the cable. The third way is that any panic condition will branch to DDB if the kernel is configured to use it. For this reason, it is not wise to configure a kernel with DDB for a machine running unattended. The DDB commands roughly resemble some gdb commands. The first thing you probably need to do is to set a breakpoint: b function-name b address Numbers are taken hexadecimal by default, but to make them distinct from symbol names; hexadecimal numbers starting with the letters a-f need to be preceded with 0x (this is optional for other numbers). Simple expressions are allowed, for example: function-name + 0x103. To continue the operation of an interrupted kernel, simply type: c To get a stack trace, use: trace Note that when entering DDB via a hot-key, the kernel is currently servicing an interrupt, so the stack trace might be not of much use to you. If you want to remove a breakpoint, use del del address-expression The first form will be accepted immediately after a breakpoint hit, and deletes the current breakpoint. The second form can remove any breakpoint, but you need to specify the exact address; this can be obtained from: show b To single-step the kernel, try: s This will step into functions, but you can make DDB trace them until the matching return statement is reached by: n This is different from gdb's next statement; it is like gdb's finish. To examine data from memory, use (for example): x/wx 0xf0133fe0,40 x/hd db_symtab_space x/bc termbuf,10 x/s stringbuf for word/halfword/byte access, and hexadecimal/decimal/character/ string display. The number after the comma is the object count. To display the next 0x10 items, simply use: x ,10 Similarly, use x/ia foofunc,10 to disassemble the first 0x10 instructions of foofunc, and display them along with their offset from the beginning of foofunc. To modify memory, use the write command: w/b termbuf 0xa 0xb 0 w/w 0xf0010030 0 0 The command modifier (b/h/w) specifies the size of the data to be written, the first following expression is the address to write to and the remainder is interpreted as data to write to successive memory locations. If you need to know the current registers, use: show reg Alternatively, you can display a single register value by e.g. p $eax and modify it by: set $eax new-value Should you need to call some kernel functions from DDB, simply say: call func(arg1, arg2, ...) The return value will be printed. For a &man.ps.1; style summary of all running processes, use: ps Now you have examined why your kernel failed, and you wish to reboot. Remember that, depending on the severity of previous malfunctioning, not all parts of the kernel might still be working as expected. Perform one of the following actions to shut down and reboot your system: panic This will cause your kernel to dump core and reboot, so you can later analyze the core on a higher level with gdb. This command usually must be followed by another continue statement. call boot(0) Which might be a good way to cleanly shut down the running system, sync() all disks, and finally reboot. As long as the disk and filesystem interfaces of the kernel are not damaged, this might be a good way for an almost clean shutdown. call cpu_reset() This is the final way out of disaster and almost the same as hitting the Big Red Button. If you need a short command summary, simply type: help However, it is highly recommended to have a printed copy of the &man.ddb.4; manual page ready for a debugging session. Remember that it is hard to read the on-line manual while single-stepping the kernel. On-Line Kernel Debugging Using Remote GDB This feature has been supported since FreeBSD 2.2, and it is actually a very neat one. GDB has already supported remote debugging for a long time. This is done using a very simple protocol along a serial line. Unlike the other methods described above, you will need two machines for doing this. One is the host providing the debugging environment, including all the sources, and a copy of the kernel binary with all the symbols in it, and the other one is the target machine that simply runs a similar copy of the very same kernel (but stripped of the debugging information). You should configure the kernel in question with config -g, include into the configuration, and compile it as usual. This gives a large binary, due to the debugging information. Copy this kernel to the target machine, strip the debugging symbols off with strip -x, and boot it using the boot option. Connect the serial line of the target machine that has "flags 080" set on its sio device to any serial line of the debugging host. Now, on the debugging machine, go to the compile directory of the target kernel, and start gdb: &prompt.user; gdb -k kernel GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.16 (i386-unknown-freebsd), Copyright 1996 Free Software Foundation, Inc... (kgdb) Initialize the remote debugging session (assuming the first serial port is being used) by: (kgdb) target remote /dev/cuaa0 Now, on the target host (the one that entered DDB right before even starting the device probe), type: Debugger("Boot flags requested debugger") Stopped at Debugger+0x35: movb $0, edata+0x51bc db> gdb DDB will respond with: Next trap will enter GDB remote protocol mode Every time you type gdb, the mode will be toggled between remote GDB and local DDB. In order to force a next trap immediately, simply type s (step). Your hosting GDB will now gain control over the target kernel: Remote debugging using /dev/cuaa0 Debugger (msg=0xf01b0383 "Boot flags requested debugger") at ../../i386/i386/db_interface.c:257 (kgdb) You can use this session almost as any other GDB session, including full access to the source, running it in gud-mode inside an Emacs window (which gives you an automatic source code display in another Emacs window), etc. Debugging Loadable Modules Using GDB When debugging a panic that occurred within a module, or using remote GDB against a machine that uses dynamic modules, you need to tell GDB how to obtain symbol information for those modules. First, you need to build the module(s) with debugging information: &prompt.root; cd /sys/modules/linux &prompt.root; make clean; make COPTS=-g If you are using remote GDB, you can run kldstat on the target machine to find out where the module was loaded: &prompt.root; kldstat Id Refs Address Size Name 1 4 0xc0100000 1c1678 kernel 2 1 0xc0a9e000 6000 linprocfs.ko 3 1 0xc0ad7000 2000 warp_saver.ko 4 1 0xc0adc000 11000 linux.ko If you are debugging a crash dump, you will need to walk the linker_files list, starting at linker_files->tqh_first and following the link.tqe_next pointers until you find the entry with the filename you are looking for. The address member of that entry is the load address of the module. Next, you need to find out the offset of the text section within the module: &prompt.root; objdump --section-headers /sys/modules/linux/linux.ko | grep text 3 .rel.text 000016e0 000038e0 000038e0 000038e0 2**2 10 .text 00007f34 000062d0 000062d0 000062d0 2**2 The one you want is the .text section, section 10 in the above example. The fourth hexadecimal field (sixth field overall) is the offset of the text section within the file. Add this offset to the load address of the module to obtain the relocation address for the module's code. In our example, we get 0xc0adc000 + 0x62d0 = 0xc0ae22d0. Use the add-symbol-file command in GDB to tell the debugger about the module: (kgdb) add-symbol-file /sys/modules/linux/linux.ko 0xc0ae22d0 add symbol table from file "/sys/modules/linux/linux.ko" at text_addr = 0xc0ae22d0? (y or n) y Reading symbols from /sys/modules/linux/linux.ko...done. (kgdb) You should now have access to all the symbols in the module. Debugging a Console Driver Since you need a console driver to run DDB on, things are more complicated if the console driver itself is failing. You might remember the use of a serial console (either with modified boot blocks, or by specifying at the Boot: prompt), and hook up a standard terminal onto your first serial port. DDB works on any configured console driver, including a serial console. diff --git a/ru_RU.KOI8-R/books/handbook/security/chapter.sgml b/ru_RU.KOI8-R/books/handbook/security/chapter.sgml index f9371cd49c..701531abab 100644 --- a/ru_RU.KOI8-R/books/handbook/security/chapter.sgml +++ b/ru_RU.KOI8-R/books/handbook/security/chapter.sgml @@ -1,2747 +1,2747 @@ Безопасность Большинство нижеизложенной информации повторяет страницы &man.security.7; справочника, изначально написанного &a.dillon;. Краткий обзор В этой главе мы расскажем об основных сведениях, которые необходимо знать, затрагивающие общую безопасность системы, о том, как подбирать хорошие пароли, и о таких вещах как S/Key, OpenSSL, Kerberos и других. Введение Несомненно, одна из важнейших задач системного администратора – обеспечивать безопасность системы. Многопользовательские системы, такие как BSD UNIX, имеют некоторый базовый набор средств безопасности, однако обычно этого бывает недостаточно, и, чтобы построить и поддерживать безопасность системы на должном уровне, администратору приходится прикладывать некоторые усилия. Безопасность системы целиком и полностью находится в руках системного администратора. UNIX системы способны выполнять огромное число процессов одновременно, и многие из них являются серверами, то есть к ним можно обращаться извне. Сегодня уже никого не удивишь компьютером на рабочем столе, а с распространением и повсеместным внедрением в нашу жизнь сетевых технологий проблемы безопасности становятся первоочередными. Лучше всего строить модель безопасности, основываясь на уровневом подходе. В двух словах, это означает создание неких эшелонов, колец защиты, и бдительное наблюдение за состоянием каждого уровня. Имейте в виду, что слишком перегруженная система безопасности может скорее помещать, нежели помочь предотвращению атаки. Например, не стоит устанавливать schg-флаги (см. &man.chflags.1;) на каждый выполняемый системный файл, так как это, возможно, и предотвратит несанкционированные модификации Ваших файлов, но, в то же время, может лишить Вас легко детектируемых следов пребывания хакера, что ему только на руку. Пречислим ключевые действия, которые имеют непосредственное отношение к безопасности любой компьютерной системы: Атаки типа отказ от обслуживания (D.o.S.). Получение пользовательского аккаунта. Получение прав суперпользователя (root) через доступные сервисы. Получение прав суперпользователя (root) через пользовательские аккаунты. Создание люков (задних дверей, черных ходов). При атаке на отказ от обслуживания машина лишается необходимых ресурсов для выполнения требуемых запросов. Наиболее типичными атаками этого типа являются механизмы так называемой грубой силы (brute-force), которые направлены на выведение из строя отдельных серверов или сети в целом. Иногда для достижения цели используются обнаруженые ошибки в сетевом стеке, когда даже одного специальным образом составленного пакета достаточно, чтобы вывести машину из строя. Обычно ошибки такого рода исправляются путем прикладывания соответствующих патчей к ядру. Разумно также тщательно подбирать сетевые опции, чтобы не допустить перегрузки серверов при работе с очень высоким потоком данных (например, каким-либо образом ограничивать этот поток). Если же Вашу систему атакуют методом грубой силы, бороться с этим сложнее. Например, если машину забрасывают специальным образом сгенерированными пакетами, то часто бывает, что практически невозможно остановить атакующего, кроме как полностью отключив компьютер от сети. Даже в этом случае, хотя Ваша машина не будет затронута, сетевой трафик будет сильно нарушен. Атаки, нацеленные на аккаунты рядовых пользователей распространены еще больше, чем атаки на отказ от обслуживания. Многие системные администраторы все еще достаточно часто используют стандартные сервисы, такие как telnetd, rlogind, rshd и ftpd. Эти демоны, по умолчанию, не используют шифрование передаваемых по сети данных при своей работе. В результате, если у Вас достаточно большое число пользователей системы, весьма вероятно, что кто-нибудь из них рано или поздно засветит свой пароль. Поэтому внимательный администратор всегда должен анализировать файлы системных журналов на предмет попыток зайти на машину с необычных адресов, и тем более в случае успеха таких попыток. Всегда важно помнить, что если атакующий имеет доступ к пользовательскомуц аккаунт, то потенциально он может получить и права суперпользователя. Однако, на самом деле, на правильно и умело сконфигурированной и регулярно проверяемой машине, наличие пользовательского аккаунта вовсе не означает, что атакующий сможет сломать ее. Разница между типичным пользовательским аккаунтом и аккаунтом суперпользователя в том, что во втором случае атакующий может сделать с системой что угодно, в то же время, без привилегий суперпользователя, можно лишь модифицировать или удалить файлы, принадлежащие данному пользователю, или в худшем случае завесить машину. Случаи, когда атакующий получает (неавторизованно) пользовательские права, весьма нередки, так как обычно рядовые пользователи не принимают всех необходимых мер предосторожности, и часто в задачу системного администратора входит принятие этих мер. Системный администратор всегда должен иметь ввиду, что существует множество потенциальных путей, которыми атакующих может проникнуть в систему. Например, он может знать пароль суперпользователя, воспользоваться ошибкой в программном обеспечении, выполняющимся с повышеными привилегиями, сетевом или локальном. Если атакующий нашел способ проникнуть в систему (получить привелегии суперпользователя), то чаще всего он попытается оставить так называемый люк, чтобы в следующий раз ему не пришлось проделывать всю грязную работу заново (и затем убирать за собой). Это дает Вам удобную возможность обнаружить взломщика (так как ему придется произвести кое-какие изменения в системе). Если же хакер смог установить люк, это может губительно сказаться на безопасности Вашей системы, так как сильно снижаются шансы, что кто-нибудь еще попытаеся взломать ее тем же способом, что и в первый раз, и, таким образом, дырка в системе останется открытой. Как уже было сказано ранее, лучше всего строить модель безопасности, основываясь на уровневом подходе. Перечислим основные моменты: Обеспечивание безопасности служебных аккаунтов и аккаунта суперпользователя. Безопасность серверов, выполняющихся с повышенными привилегиями, и исполнимых файлов, использующих SUID/SGID механизм. Обеспечивание безопасности пользовательских аккаунтов. Защита файлов, содержащих пароли. Защита ядра операционной системы, raw устройств и файловых систем. Быстрое обнаружение подозрительных изменений в системе. Паранойя. В следующей секции все перечисленные пункты будут рассмотрены в подробностях. Обеспечиваем безопасность FreeBSD В этой секции мы расскажем об основных методах защиты и обеспечивания безопасности Вашей системы FreeBSD, которые были упомянуты в предыдущей секции этой главы. Защита аккаутнов суперпользователя и служебного персонала В первую очередь нужно обеспечить безопасность суперпользовательского аккаунта, и уже после принимать аналочичные меры в отношении прочих привелигированных аккаунтов. Во многих системах аккаунт суперпользователя защищен паролем. Имейте в виду, что этот пароль крайне важен, а поэтому нелишним будет относиться к нему с очень большой осторожностью. Прежде всего, желательно не использовать его кроме как за консолью, даже применяя команду &man.su.1;. В частности, удостоверьтесь, что терминалы, перечисленные в файле /etc/ttys, помечены как insecure (небезопасные), чтобы запретить прямой доступ посредством команд telnet или rlogin. При использовании других сервисов, таких, например, как sshd, также следует запретить непосредсвенный вход в систему с правами суперпользователя. Проверьте все возможные подступы к системе – сервисы типа FTP чато оказываются подверженными атакам. Непосредственный вход в систему с правами суперпользователя следует разрешить только с консоли. Конечно, Вам как системному администратору необходимо иметь возжожность получить права суперпользователя, поэтому несколько путей все же есть. Однако, очень важно защитить их использование дополнительными паролями. Один из способов дать какому-либо пользователю повышенные привилегии – это перечислить его в группе wheel (в файле /etc/group). Пользователь, входящий в эту группу, может вызвать su для получения прав суперпользователя. Не следует включать всех членов служебного персонала в группу wheel, задавая ее как основную группу пользователя в файле паролей. Вместо этого, нужно включить их в специальную группу staff, и затем, только в случае необходимости, некоторых из них включить в группу wheel, сделав соответсвующую запись в файле /etc/group. Также возможно, при использовании методов аутентикации типа kerberos, использовать файл .k5login для того, чтобы разрешить получение привилегий супервозователя посредствеом команды &man.ksu.1; без необходимости включать кого-либо в группу wheel. Это может оказаться лучшим решением, так как предыдущий метод позволяет хакеру получить права суперпользователя, если ему удастся получить доступ к служебному акканту. Тем не менее, все это лучше, чем ничего. Аккаунт суперпользователя можно защитить косвенным путем, посредством использования альтернативных методов аутентикации и замены хэшированных паролей служебных аккаунтов на символ * в файле паролей (/etc/passwd). В этом случае, даже если взломщик получит этот файл, он не сможет извлечь оттуда пароли, включая пароль суперпользователя. Сотрудники служебного персонала будут сходить в систему при помощи защищенного механизма аутентикации, например, &man.kerberos.1; или &man.ssh.1;, используя специальную пару ключей (приватный/публичный). При использовании систем типа kerberos, Вам потребуется обеспечить безопасность kerberos-серверов и Вашей рабочей станции. При использовании механизма публичного/приватного ключей, например, при работе с ssh, Вам необходимо обеспечить безопасность машины, с которой Вы будете заходить в систему (обычно это Ваша рабочая станция), но можно также обеспечить себе дополнительную безопасность, защитив пару ключей паролем в момент ее генерации (&man.ssh-keygen.1;). Возможность заменять реальные хэши паролей на символ * в файле паролей (это особенно важно для аккаунтов служебного персонала, обладающего повышенными привилегиями по сравнению с обычными пользователями) также гарантирует, что они будут пользоваться только безопасными методами аутентикации, которые Вы предварительно настроили. Это хорошо тем, что закрывает наиболее часто используемую взломщиками дыру – прослушивание (сниффинг) сети с менее защищенной машины (на которой у него уже есть все необходимые привилегии). Еще стоит обратить внимание на следующие моменты: если Ваш основной сервер предоставляет всевозможные сетевые сервисы, то на Вашей рабочей станции по возможности максимальное число сетевых служб должно быть отключено (а в идеале – все), и нелишним будет использование хранителя экрана, защищенного паролем. Конечно, если у злоумышленника есть физический доступ к машине, то практически никакие методы защиты не помогут (хотя и могут значительно увеличить время взлома), однако большинство атак все же происходят снаружи, по сети, когда у атакующего нет непосредственного доступа к системе. При использовании систем аутентикации типа kerberos, у Вас есть возможность централизованно менять пароли или блокировать доступ пользователей. Это очень полезно при подозрении, что пароль какого-либо (например, администраторского) аккаунта стал известен постороннему лицу – в этом случае можно очень быстро запретить вход на все машины, где был заведен данный пользователь. Представьте себе, насколько труднее и дольше было бы менять пароли на каждой из N машин отдельно! Kerberos предосталяет и другие возможности (принудительная смена пароля по истечении определенного промежутка времени, например). Безопасность серверов, выполняющихся с повышенными привилегиями, и исполнимых файлов, использующих SUID/SGID механизм Осторожный системный администратор конфигурирует систему так, чтобы запущены были только самые необходимые сервисы. Не больше, не меньше. Стоит с особой осторожностью отновиться tHE prudent sysadmin only runs the servers he needs to, no more, no less. Be aware that third party servers are often the most bug-prone. For example, running an old version of imapd or popper is like giving a universal root ticket out to the entire world. Never run a server that you have not checked out carefully. Many servers do not need to be run as root. For example, the ntalk, comsat, and finger daemons can be run in special user sandboxes. A sandbox isn't perfect unless you go to a large amount of trouble, but the onion approach to security still stands: If someone is able to break in through a server running in a sandbox, they still have to break out of the sandbox. The more layers the attacker must break through, the lower the likelihood of his success. Root holes have historically been found in virtually every server ever run as root, including basic system servers. If you are running a machine through which people only login via sshd and never login via telnetd or rshd or rlogind, then turn off those services! FreeBSD now defaults to running ntalkd, comsat, and finger in a sandbox. Another program which may be a candidate for running in a sandbox is &man.named.8;. The default rc.conf includes the arguments necessary to run namedin a sandbox in a commented-out form. Depending on whether you are installing a new system or upgrading an existing system, the special user accounts used by these sandboxes may not be installed. The prudent sysadmin would research and implement sandboxes for servers whenever possible. There are a number of other servers that typically do not run in sandboxes: sendmail, popper, imapd, ftpd, and others. There are alternatives to some of these, but installing them may require more work then you are willing to perform (the convenience factor strikes again). You may have to run these servers as root and rely on other mechanisms to detect break-ins that might occur through them. The other big potential root hole in a system are the suid-root and sgid binaries installed on the system. Most of these binaries, such as rlogin, reside in /bin, /sbin, /usr/bin, or /usr/sbin. While nothing is 100% safe, the system-default suid and sgid binaries can be considered reasonably safe. Still, root holes are occasionally found in these binaries. A root hole was found in Xlib in 1998 that made xterm (which is typically suid) vulnerable. It is better to be safe then sorry and the prudent sysadmin will restrict suid binaries that only staff should run to a special group that only staff can access, and get rid of (chmod 000) any suid binaries that nobody uses. A server with no display generally does not need an xterm binary. Sgid binaries can be almost as dangerous. If an intruder can break an sgid-kmem binary the intruder might be able to read /dev/kmem and thus read the crypted password file, potentially compromising any passworded account. Alternatively an intruder who breaks group kmem can monitor keystrokes sent through pty's, including pty's used by users who login through secure methods. An intruder that breaks the tty group can write to almost any user's tty. If a user is running a terminal program or emulator with a keyboard-simulation feature, the intruder can potentially generate a data stream that causes the user's terminal to echo a command, which is then run as that user. Securing User Accounts User accounts are usually the most difficult to secure. While you can impose Draconian access restrictions on your staff and * out their passwords, you may not be able to do so with any general user accounts you might have. If you do have sufficient control then you may win out and be able to secure the user accounts properly. If not, you simply have to be more vigilant in your monitoring of those accounts. Use of ssh and kerberos for user accounts is more problematic due to the extra administration and technical support required, but still a very good solution compared to a crypted password file. Securing the Password File The only sure fire way is to * out as many passwords as you can and use ssh or kerberos for access to those accounts. Even though the crypted password file (/etc/spwd.db) can only be read by root, it may be possible for an intruder to obtain read access to that file even if the attacker cannot obtain root-write access. Your security scripts should always check for and report changes to the password file (see Checking file integrity below). Securing the Kernel Core, Raw Devices, and Filesystems If an attacker breaks root he can do just about anything, but there are certain conveniences. For example, most modern kernels have a packet sniffing device driver built in. Under FreeBSD it is called the bpf device. An intruder will commonly attempt to run a packet sniffer on a compromised machine. You do not need to give the intruder the capability and most systems should not have the bpf device compiled in. But even if you turn off the bpf device, you still have /dev/mem and /dev/kmem to worry about. For that matter, the intruder can still write to raw disk devices. Also, there is another kernel feature called the module loader, &man.kldload.8;. An enterprising intruder can use a KLD module to install his own bpf device or other sniffing device on a running kernel. To avoid these problems you have to run the kernel at a higher secure level, at least securelevel 1. The securelevel can be set with a sysctl on the kern.securelevel variable. Once you have set the securelevel to 1, write access to raw devices will be denied and special chflags flags, such as schg, will be enforced. You must also ensure that the schg flag is set on critical startup binaries, directories, and script files – everything that gets run up to the point where the securelevel is set. This might be overdoing it, and upgrading the system is much more difficult when you operate at a higher secure level. You may compromise and run the system at a higher secure level but not set the schg flag for every system file and directory under the sun. Another possibility is to simply mount / and /usr read-only. It should be noted that being too draconian in what you attempt to protect may prevent the all-important detection of an intrusion. Checking File Integrity: Binaires, Configuration Files, Etc. When it comes right down to it, you can only protect your core system configuration and control files so much before the convenience factor rears its ugly head. For example, using chflags to set the schg bit on most of the files in / and /usr is probably counterproductive because while it may protect the files, it also closes a detection window. The last layer of your security onion is perhaps the most important – detection. The rest of your security is pretty much useless (or, worse, presents you with a false sense of safety) if you cannot detect potential incursions. Half the job of the onion is to slow down the attacker rather then stop him in order to give the detection side of the equation a chance to catch him in the act. The best way to detect an incursion is to look for modified, missing, or unexpected files. The best way to look for modified files is from another (often centralized) limited-access system. Writing your security scripts on the extra-secure limited-access system makes them mostly invisible to potential hackers, and this is important. In order to take maximum advantage you generally have to give the limited-access box significant access to the other machines in the business, usually either by doing a read-only NFS export of the other machines to the limited-access box, or by setting up ssh keypairs to allow the limit-access box to ssh to the other machines. Except for its network traffic, NFS is the least visible method – allowing you to monitor the filesystems on each client box virtually undetected. If your limited-access server is connected to the client boxes through a switch, the NFS method is often the better choice. If your limited-access server is connected to the client boxes through a hub or through several layers of routing, the NFS method may be too insecure (network-wise) and using ssh may be the better choice even with the audit-trail tracks that ssh lays. Once you give a limit-access box at least read access to the client systems it is supposed to monitor, you must write scripts to do the actual monitoring. Given an NFS mount, you can write scripts out of simple system utilities such as &man.find.1; and &man.md5.1;. It is best to physically md5 the client-box files boxes at least once a day, and to test control files such as those found in /etc and /usr/local/etc even more often. When mismatches are found relative to the base md5 information the limited-access machine knows is valid, it should scream at a sysadmin to go check it out. A good security script will also check for inappropriate suid binaries and for new or deleted files on system partitions such as / and /usr. When using ssh rather then NFS, writing the security script is much more difficult. You essentially have to scp the scripts to the client box in order to run them, making them visible, and for safety you also need to scp the binaries (such as find) that those scripts use. The ssh daemon on the client box may already be compromised. All in all, using ssh may be necessary when running over unsecure links, but it's also a lot harder to deal with. A good security script will also check for changes to user and staff members access configuration files: .rhosts, .shosts, .ssh/authorized_keys and so forth… files that might fall outside the purview of the MD5 check. If you have a huge amount of user disk space it may take too long to run through every file on those partitions. In this case, setting mount flags to disallow suid binaries and devices on those partitions is a good idea. The nodev and nosuid options (see &man.mount.8;) are what you want to look into. I would scan them anyway at least once a week, since the object of this layer is to detect a break-in whether or not the break-in is effective. Process accounting (see &man.accton.8;) is a relatively low-overhead feature of the operating system which I recommend using as a post-break-in evaluation mechanism. It is especially useful in tracking down how an intruder has actually broken into a system, assuming the file is still intact after the break-in occurs. Finally, security scripts should process the log files and the logs themselves should be generated in as secure a manner as possible – remote syslog can be very useful. An intruder tries to cover his tracks, and log files are critical to the sysadmin trying to track down the time and method of the initial break-in. One way to keep a permanent record of the log files is to run the system console to a serial port and collect the information on a continuing basis through a secure machine monitoring the consoles. Паранойя A little paranoia never hurts. As a rule, a sysadmin can add any number of security features as long as they do not effect convenience, and can add security features that do effect convenience with some added thought. Even more importantly, a security administrator should mix it up a bit – if you use recommendations such as those given by this document verbatim, you give away your methodologies to the prospective hacker who also has access to this document. Denial of Service Attacks This section covers Denial of Service attacks. A DOS attack is typically a packet attack. While there is not much you can do about modern spoofed packet attacks that saturate your network, you can generally limit the damage by ensuring that the attacks cannot take down your servers. Limiting server forks. Limiting springboard attacks (ICMP response attacks, ping broadcast, etc.). Kernel Route Cache. A common DOS attack is against a forking server that attempts to cause the server to eat processes, file descriptors, and memory until the machine dies. Inetd (see &man.inetd.8;) has several options to limit this sort of attack. It should be noted that while it is possible to prevent a machine from going down it is not generally possible to prevent a service from being disrupted by the attack. Read the inetd manual page carefully and pay specific attention to the , , and options. Note that spoofed-IP attacks will circumvent the option to inetd, so typically a combination of options must be used. Some standalone servers have self-fork-limitation parameters. Sendmail has its option which tends to work much better than trying to use sendmail's load limiting options due to the load lag. You should specify a MaxDaemonChildren parameter when you start sendmail high enough to handle your expected load but no so high that the computer cannot handle that number of sendmails without falling on its face. It is also prudent to run sendmail in queued mode () and to run the daemon (sendmail -bd) separate from the queue-runs (sendmail -q15m). If you still want realtime delivery you can run the queue at a much lower interval, such as , but be sure to specify a reasonable MaxDaemonChildren option for that sendmail to prevent cascade failures. Syslogd can be attacked directly and it is strongly recommended that you use the option whenever possible, and the option otherwise. You should also be fairly careful with connect-back services such as tcpwrapper's reverse-identd, which can be attacked directly. You generally do not want to use the reverse-ident feature of tcpwrappers for this reason. It is a very good idea to protect internal services from external access by firewalling them off at your border routers. The idea here is to prevent saturation attacks from outside your LAN, not so much to protect internal services from network-based root compromise. Always configure an exclusive firewall, i.e., firewall everything except ports A, B, C, D, and M-Z. This way you can firewall off all of your low ports except for certain specific services such as named (if you are primary for a zone), ntalkd, sendmail, and other internet-accessible services. If you try to configure the firewall the other way – as an inclusive or permissive firewall, there is a good chance that you will forget to close a couple of services or that you will add a new internal service and forget to update the firewall. You can still open up the high-numbered port range on the firewall to allow permissive-like operation without compromising your low ports. Also take note that FreeBSD allows you to control the range of port numbers used for dynamic binding via the various net.inet.ip.portrange sysctl's (sysctl -a | fgrep portrange), which can also ease the complexity of your firewall's configuration. I usually use a normal first/last range of 4000 to 5000, and a hiport range of 49152 to 65535, then block everything under 4000 off in my firewall (except for certain specific internet-accessible ports, of course). Another common DOS attack is called a springboard attack – to attack a server in a manner that causes the server to generate responses which then overload the server, the local network, or some other machine. The most common attack of this nature is the ICMP ping broadcast attack. The attacker spoofs ping packets sent to your LAN's broadcast address with the source IP address set to the actual machine they wish to attack. If your border routers are not configured to stomp on ping's to broadcast addresses, your LAN winds up generating sufficient responses to the spoofed source address to saturate the victim, especially when the attacker uses the same trick on several dozen broadcast addresses over several dozen different networks at once. Broadcast attacks of over a hundred and twenty megabits have been measured. A second common springboard attack is against the ICMP error reporting system. By constructing packets that generate ICMP error responses, an attacker can saturate a server's incoming network and cause the server to saturate its outgoing network with ICMP responses. This type of attack can also crash the server by running it out of mbuf's, especially if the server cannot drain the ICMP responses it generates fast enough. The FreeBSD kernel has a new kernel compile option called ICMP_BANDLIM which limits the effectiveness of these sorts of attacks. The last major class of springboard attacks is related to certain internal inetd services such as the udp echo service. An attacker simply spoofs a UDP packet with the source address being server A's echo port, and the destination address being server B's echo port, where server A and B are both on your LAN. The two servers then bounce this one packet back and forth between each other. The attacker can overload both servers and their LANs simply by injecting a few packets in this manner. Similar problems exist with the internal chargen port. A competent sysadmin will turn off all of these inetd-internal test services. Spoofed packet attacks may also be used to overload the kernel route cache. Refer to the net.inet.ip.rtexpire, rtminexpire, and rtmaxcache sysctl parameters. A spoofed packet attack that uses a random source IP will cause the kernel to generate a temporary cached route in the route table, viewable with netstat -rna | fgrep W3. These routes typically timeout in 1600 seconds or so. If the kernel detects that the cached route table has gotten too big it will dynamically reduce the rtexpire but will never decrease it to less then rtminexpire. There are two problems: The kernel does not react quickly enough when a lightly loaded server is suddenly attacked. The rtminexpire is not low enough for the kernel to survive a sustained attack. If your servers are connected to the internet via a T3 or better it may be prudent to manually override both rtexpire and rtminexpire via &man.sysctl.8;. Never set either parameter to zero (unless you want to crash the machine :-). Setting both parameters to 2 seconds should be sufficient to protect the route table from attack. Access Issues with Kerberos and SSH There are a few issues with both kerberos and ssh that need to be addressed if you intend to use them. Kerberos V is an excellent authentication protocol but the kerberized telnet and rlogin suck rocks. There are bugs that make them unsuitable for dealing with binary streams. Also, by default kerberos does not encrypt a session unless you use the option. ssh encrypts everything by default. ssh works quite well in every respect except that it forwards encryption keys by default. What this means is that if you have a secure workstation holding keys that give you access to the rest of the system, and you ssh to an unsecure machine, your keys becomes exposed. The actual keys themselves are not exposed, but ssh installs a forwarding port for the duration of your login and if a hacker has broken root on the unsecure machine he can utilize that port to use your keys to gain access to any other machine that your keys unlock. We recommend that you use ssh in combination with kerberos whenever possible for staff logins. ssh can be compiled with kerberos support. This reduces your reliance on potentially exposable ssh keys while at the same time protecting passwords via kerberos. ssh keys should only be used for automated tasks from secure machines (something that kerberos is unsuited to). We also recommend that you either turn off key-forwarding in the ssh configuration, or that you make use of the from=IP/DOMAIN option that ssh allows in its authorized_keys file to make the key only useable to entities logging in from specific machines. DES, MD5, and Crypt Parts rewritten and updated by &a.unfurl;, 21 March 2000. Every user on a UNIX system has a password associated with their account, obviously these passwords need to be known only to the user and the actual operating system. In order to keep these passwords secret, they are encrypted with what is known as a 'one-way hash', that is, they can only be easily encrypted but not decrypted. The only way to get the password is by brute force searching the space of possible passwords. Unfortunately the only secure way to encrypt passwords when UNIX came into being was based on DES, the Data Encryption Standard. This is not such a problem for users that live in the US, but since the source code for DES cannot be exported outside the US, FreeBSD had to find a way to both comply with US law and retain compatibility with all the other UNIX variants that still use DES. The solution was to divide up the encryption libraries so that US users could install the DES libraries and use DES but international users still had an encryption method that could be exported abroad. This is how FreeBSD came to use MD5 as it's default encryption method. Recognizing your crypt mechanism It is pretty easy to identify which encryption method FreeBSD is set up to use. Examining the encrypted passwords in the /etc/master.passwd file is one way. Passwords encrypted with the MD5 hash are longer than those with encrypted with the DES hash and also begin with the characters $1$. DES password strings do not have any particular identifying characteristics, but they are shorter than MD5 passwords, and are coded in a 64-character alphabet which does not include the $ character, so a relatively short string which does not begin with a dollar sign is very likely a DES password. Identifying which library is being used by the programs on your system is easy as well. Any program that uses crypt is linked against libcrypt which for each type of library is a symbolic link to the appropriate implementation. For example, on a system using the DES versions: &prompt.user; ls -l /usr/lib/libcrypt* lrwxr-xr-x 1 root wheel 13 Mar 19 06:56 libcrypt.a -> libdescrypt.a lrwxr-xr-x 1 root wheel 18 Mar 19 06:56 libcrypt.so.2.0 -> libdescrypt.so.2.0 lrwxr-xr-x 1 root wheel 15 Mar 19 06:56 libcrypt_p.a -> libdescrypt_p.a On a system using the MD5-based libraries, the same links will be present, but the target will be libscrypt rather than libdescrypt. S/Key S/Key is a one-time password scheme based on a one-way hash function. FreeBSD uses the MD4 hash for compatibility but other systems have used MD5 and DES-MAC. S/Key has been part of the FreeBSD base system since version 1.1.5 and is also used on a growing number of other operating systems. S/Key is a registered trademark of Bell Communications Research, Inc. There are three different sorts of passwords which we will talk about in the discussion below. The first is your usual UNIX-style or Kerberos password; we will call this a UNIX password. The second sort is the one-time password which is generated by the S/Key key program and accepted by the keyinit program and the login prompt; we will call this a one-time password. The final sort of password is the secret password which you give to the key program (and sometimes the keyinit program) which it uses to generate one-time passwords; we will call it a secret password or just unqualified password. The secret password does not have anything to do with your UNIX password; they can be the same but this is not reccomended. S/Key secret passwords are not limted to 8 characters like UNIX passwords, they can be as long as you like. Passwords of six or seven word long phrases are fairly common. For the most part, the S/Key system operates completely independently of the UNIX password system. Besides the password, there are two other pieces of data that are important to S/Key. One is what is known as the seed or key and consists of two letters and five digits. The other is what is called the iteration count and is a number between 1 and 100. S/Key creates the one-time password by concatenating the seed and the secret password, then applying the MD4 hash as many times as specified by the iteration count and turning the result into six short English words. These six English words are your one-time password. The login and su programs keep track of the last one-time password used, and the user is authenticated if the hash of the user-provided password is equal to the previous password. Because a one-way hash is used it is impossible to generate future one-time passwords if a sucessfully used password is captured; the interation count is decremented after each sucessfull login to keep the user and the login program in sync. When the iteration count gets down to 1 S/Key must be reinitialized. There are four programs involved in the S/Key system which we will discuss below. The key program accepts an iteration count, a seed, and a secret password, and generates a one-time password. The keyinit program is used to initialized S/Key, and to change passwords, iteration counts, or seeds; it takes either a secret password, or an iteration count, seed, and one-time password. The keyinfo program examines the /etc/skeykeys file and prints out the invoking user's current iteration count and seed. Finally, the login and su programs contain the necessary logic to accept S/Key one-time passwords for authentication. The login program is also capable of disallowing the use of UNIX passwords on connections coming from specified addresses. There are four different sorts of operations we will cover. The first is using the keyinit program over a secure connection to set up S/Key for the first time, or to change your password or seed. The second operation is using the keyinit program over an insecure connection, in conjunction with the key program over a secure connection, to do the same. The third is using the key program to log in over an insecure connection. The fourth is using the key program to generate a number of keys which can be written down or printed out to carry with you when going to some location without secure connections to anywhere. Secure connection initialization To initialize S/Key for the first time, change your password, or change your seed while logged in over a secure connection (e.g., on the console of a machine or via ssh), use the keyinit command without any parameters while logged in as yourself: &prompt.user; keyinit Adding unfurl: Reminder - Only use this method if you are directly connected. If you are using telnet or rlogin exit with no password and use keyinit -s. Enter secret password: Again secret password: ID unfurl s/key is 99 to17757 DEFY CLUB PRO NASH LACE SOFT At the Enter secret password: prompt you should enter a password or phrase. Remember, this is not the password that you will use to login with, this is used to generate your one-time login keys. The ID line gives the parameters of your particular S/Key instance; your login name, the iteration count, and seed. When logging in with S/Key, the system will remember these parameters and present them back to you so you do not have to remember them. The last line gives the particular one-time password which corresponds to those parameters and your secret password; if you were to re-login immediately, this one-time password is the one you would use. Insecure connection initialization To initialize S/Key or change your secret password over an insecure connection, you will need to already have a secure connection to some place where you can run the key program; this might be in the form of a desk accessory on a Macintosh, or a shell prompt on a machine you trust. You will also need to make up an iteration count (100 is probably a good value), and you may make up your own seed or use a randomly-generated one. Over on the insecure connection (to the machine you are initializing), use the keyinit -s command: &prompt.user; keyinit -s Updating unfurl: Old key: to17758 Reminder you need the 6 english words from the key command. Enter sequence count from 1 to 9999: 100 Enter new key [default to17759]: s/key 100 to 17759 s/key access password: To accept the default seed (which the keyinit program confusingly calls a key), press return. Then before entering an access password, move over to your secure connection or S/Key desk accessory, and give it the same parameters: &prompt.user; key 100 to17759 Reminder - Do not use this program while logged in via telnet or rlogin. Enter secret password: <secret password> CURE MIKE BANE HIM RACY GORE Now switch back over to the insecure connection, and copy the one-time password generated by key over to the keyinit program: s/key access password:CURE MIKE BANE HIM RACY GORE ID unfurl s/key is 100 to17759 CURE MIKE BANE HIM RACY GORE The rest of the description from the previous section applies here as well. Generating a single one-time password Once you've initialized S/Key, when you login you will be presented with a prompt like this: &prompt.user; telnet example.com Trying 10.0.0.1... Connected to example.com Escape character is '^]'. FreeBSD/i386 (example.com) (ttypa) login: <username> s/key 97 fw13894 Password: As a side note, the S/Key prompt has a useful feature (not shown here): if you press return at the password prompt, the login program will turn echo on, so you can see what you are typing. This can be extremely useful if you are attempting to type in an S/Key by hand, such as from a printout. Also, if this machine were configured to disallow UNIX passwords over a connection from my machine, the prompt would have also included the annotation (s/key required), indicating that only S/Key one-time passwords will be accepted. At this point you need to generate your one-time password to answer this login prompt. This must be done on a trusted system that you can run the key command on. (There are versions of the key program from DOS, Windows and MacOS as well.) The key program needs both the iteration count and the seed as command line options. You can cut-and-paste these right from the login prompt on the machine that you are logging in to. On the trusted system: &prompt.user; key 97 fw13894 Reminder - Do not use this program while logged in via telnet or rlogin. Enter secret password: WELD LIP ACTS ENDS ME HAAG Now that you have your one-time password you can continue logging in: login: <username> s/key 97 fw13894 Password: <return to enable echo> s/key 97 fw13894 Password [echo on]: WELD LIP ACTS ENDS ME HAAG Last login: Tue Mar 21 11:56:41 from 10.0.0.2 ... This is the easiest mechanism if you have a trusted machine. There is a Java S/Key key applet, The Java OTP Calculator, that you can download and run locally on any Java supporting browser. Generating multiple one-time passwords Sometimes you have have to go places where you do not have access to a trusted machine or secure connection. In this case, it is possible to use the key command to generate a number of one-time passwords before hand to be printed out and taken with you. For example: &prompt.user; key -n 5 30 zz99999 Reminder - Do not use this program while logged in via telnet or rlogin. Enter secret password: <secret password> 26: SODA RUDE LEA LIND BUDD SILT 27: JILT SPY DUTY GLOW COWL ROT 28: THEM OW COLA RUNT BONG SCOT 29: COT MASH BARR BRIM NAN FLAG 30: CAN KNEE CAST NAME FOLK BILK The requests five keys in sequence, the specifies what the last iteration number should be. Note that these are printed out in reverse order of eventual use. If you are really paranoid, you might want to write the results down by hand; otherwise you can cut-and-paste into lpr. Note that each line shows both the iteration count and the one-time password; you may still find it handy to scratch off passwords as you use them. Restricting use of UNIX passwords Restrictions can be placed on the use of UNIX passwords based on the host name, user name, terminal port, or IP address of a login session. These restrictions can be found in the configuration file /etc/skey.access. The &man.skey.access.5; manual page has more info on the complete format of the file and also details some security cautions to be aware of before depending on this file for security. If there is no /etc/skey.access file (this is the FreeBSD default), then all users will be allowed to use UNIX passwords. If the file exists, however, then all users will be required to use S/Key unless explicitly permitted to do otherwise by configuration statements in the skey.access file. In all cases, UNIX passwords are permitted on the console. Here is a sample configuration file which illustrates the three most common sorts of configuration statements: permit internet 192.168.0.0 255.255.0.0 permit user fnord permit port ttyd0 The first line (permit internet) allows users whose IP source address (which is vulnerable to spoofing) matches the specified value and mask, to use UNIX passwords. This should not be considered a security mechanism, but rather, a means to remind authorized users that they are using an insecure network and need to use S/Key for authentication. The second line (permit user) allows the specified username, in this case fnord, to use UNIX passwords at any time. Generally speaking, this should only be used for people who are either unable to use the key program, like those with dumb terminals, or those who are uneducable. The third line (permit port) allows all users logging in on the specified terminal line to use UNIX passwords; this would be used for dial-ups. Kerberos Contributed by &a.markm; (based on contribution by &a.md;). Kerberos is a network add-on system/protocol that allows users to authenticate themselves through the services of a secure server. Services such as remote login, remote copy, secure inter-system file copying and other high-risk tasks are made considerably safer and more controllable. The following instructions can be used as a guide on how to set up Kerberos as distributed for FreeBSD. However, you should refer to the relevant manual pages for a complete description. In FreeBSD, the Kerberos is not that from the original 4.4BSD-Lite, distribution, but eBones, which had been previously ported to FreeBSD 1.1.5.1, and was sourced from outside the USA/Canada, and is thus available to system owners outside those countries. For those needing to get a legal foreign distribution of this software, please do not get it from a USA or Canada site. You will get that site in big trouble! A legal copy of this is available from ftp.internat.FreeBSD.org, which is in South Africa and an official FreeBSD mirror site. Creating the initial database This is done on the Kerberos server only. First make sure that you do not have any old Kerberos databases around. You should change to the directory /etc/kerberosIV and check that only the following files are present: &prompt.root; cd /etc/kerberosIV &prompt.root; ls README krb.conf krb.realms If any additional files (such as principal.* or master_key) exist, then use the kdb_destroy command to destroy the old Kerberos database, of if Kerberos is not running, simply delete the extra files. You should now edit the krb.conf and krb.realms files to define your Kerberos realm. In this case the realm will be GRONDAR.ZA and the server is grunt.grondar.za. We edit or create the krb.conf file: &prompt.root; cat krb.conf GRONDAR.ZA GRONDAR.ZA grunt.grondar.za admin server CS.BERKELEY.EDU okeeffe.berkeley.edu ATHENA.MIT.EDU kerberos.mit.edu ATHENA.MIT.EDU kerberos-1.mit.edu ATHENA.MIT.EDU kerberos-2.mit.edu ATHENA.MIT.EDU kerberos-3.mit.edu LCS.MIT.EDU kerberos.lcs.mit.edu TELECOM.MIT.EDU bitsy.mit.edu ARC.NASA.GOV trident.arc.nasa.gov In this case, the other realms do not need to be there. They are here as an example of how a machine may be made aware of multiple realms. You may wish to not include them for simplicity. The first line names the realm in which this system works. The other lines contain realm/host entries. The first item on a line is a realm, and the second is a host in that realm that is acting as a key distribution centre. The words admin server following a hosts name means that host also provides an administrative database server. For further explanation of these terms, please consult the Kerberos man pages. Now we have to add grunt.grondar.za to the GRONDAR.ZA realm and also add an entry to put all hosts in the .grondar.za domain in the GRONDAR.ZA realm. The krb.realms file would be updated as follows: &prompt.root; cat krb.realms grunt.grondar.za GRONDAR.ZA .grondar.za GRONDAR.ZA .berkeley.edu CS.BERKELEY.EDU .MIT.EDU ATHENA.MIT.EDU .mit.edu ATHENA.MIT.EDU Again, the other realms do not need to be there. They are here as an example of how a machine may be made aware of multiple realms. You may wish to remove them to simplify things. The first line puts the specific system into the named realm. The rest of the lines show how to default systems of a particular subdomain to a named realm. Now we are ready to create the database. This only needs to run on the Kerberos server (or Key Distribution Centre). Issue the kdb_init command to do this: &prompt.root; kdb_init Realm name [default ATHENA.MIT.EDU ]: GRONDAR.ZA You will be prompted for the database Master Password. It is important that you NOT FORGET this password. Enter Kerberos master key: Now we have to save the key so that servers on the local machine can pick it up. Use the kstash command to do this. &prompt.root; kstash Enter Kerberos master key: Current Kerberos master key version is 1. Master key entered. BEWARE! This saves the encrypted master password in /etc/kerberosIV/master_key. Making it all run Two principals need to be added to the database for each system that will be secured with Kerberos. Their names are kpasswd and rcmd These two principals are made for each system, with the instance being the name of the individual system. These daemons, kpasswd and rcmd allow other systems to change Kerberos passwords and run commands like rcp, rlogin and rsh. Now let's add these entries: &prompt.root; kdb_edit Opening database... Enter Kerberos master key: Current Kerberos master key version is 1. Master key entered. BEWARE! Previous or default values are in [brackets] , enter return to leave the same, or new value. Principal name: passwd Instance: grunt <Not found>, Create [y] ? y Principal: passwd, Instance: grunt, kdc_key_ver: 1 New Password: <---- enter RANDOM here Verifying password New Password: <---- enter RANDOM here Random password [y] ? y Principal's new key version = 1 Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ? Max ticket lifetime (*5 minutes) [ 255 ] ? Attributes [ 0 ] ? Edit O.K. Principal name: rcmd Instance: grunt <Not found>, Create [y] ? Principal: rcmd, Instance: grunt, kdc_key_ver: 1 New Password: <---- enter RANDOM here Verifying password New Password: <---- enter RANDOM here Random password [y] ? Principal's new key version = 1 Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ? Max ticket lifetime (*5 minutes) [ 255 ] ? Attributes [ 0 ] ? Edit O.K. Principal name: <---- null entry here will cause an exit Creating the server file We now have to extract all the instances which define the services on each machine. For this we use the ext_srvtab command. This will create a file which must be copied or moved by secure means to each Kerberos client's /etc/kerberosIV directory. This file must be present on each server and client, and is crucial to the operation of Kerberos. &prompt.root; ext_srvtab grunt Enter Kerberos master key: Current Kerberos master key version is 1. Master key entered. BEWARE! Generating 'grunt-new-srvtab'.... Now, this command only generates a temporary file which must be renamed to srvtab so that all the server can pick it up. Use the mv command to move it into place on the original system: &prompt.root; mv grunt-new-srvtab srvtab If the file is for a client system, and the network is not deemed safe, then copy the client-new-srvtab to removable media and transport it by secure physical means. Be sure to rename it to srvtab in the client's /etc/kerberosIV directory, and make sure it is mode 600: &prompt.root; mv grumble-new-srvtab srvtab &prompt.root; chmod 600 srvtab Populating the database We now have to add some user entries into the database. First let's create an entry for the user jane. Use the kdb_edit command to do this: &prompt.root; kdb_edit Opening database... Enter Kerberos master key: Current Kerberos master key version is 1. Master key entered. BEWARE! Previous or default values are in [brackets] , enter return to leave the same, or new value. Principal name: jane Instance: <Not found>, Create [y] ? y Principal: jane, Instance: , kdc_key_ver: 1 New Password: <---- enter a secure password here Verifying password New Password: <---- re-enter the password here Principal's new key version = 1 Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ? Max ticket lifetime (*5 minutes) [ 255 ] ? Attributes [ 0 ] ? Edit O.K. Principal name: <---- null entry here will cause an exit Testing it all out First we have to start the Kerberos daemons. NOTE that if you have correctly edited your /etc/rc.conf then this will happen automatically when you reboot. This is only necessary on - the Kerberos server. Kerberos clients will automagically get what + the Kerberos server. Kerberos clients will automatically get what they need from the /etc/kerberosIV directory. &prompt.root; kerberos & Kerberos server starting Sleep forever on error Log file is /var/log/kerberos.log Current Kerberos master key version is 1. Master key entered. BEWARE! Current Kerberos master key version is 1 Local realm: GRONDAR.ZA &prompt.root; kadmind -n & KADM Server KADM0.0A initializing Please do not use 'kill -9' to kill this job, use a regular kill instead Current Kerberos master key version is 1. Master key entered. BEWARE! Now we can try using the kinit command to get a ticket for the id jane that we created above: &prompt.user; kinit jane MIT Project Athena (grunt.grondar.za) Kerberos Initialization for "jane" Password: Try listing the tokens using klist to see if we really have them: &prompt.user; klist Ticket file: /tmp/tkt245 Principal: jane@GRONDAR.ZA Issued Expires Principal Apr 30 11:23:22 Apr 30 19:23:22 krbtgt.GRONDAR.ZA@GRONDAR.ZA Now try changing the password using passwd to check if the kpasswd daemon can get authorization to the Kerberos database: &prompt.user; passwd realm GRONDAR.ZA Old password for jane: New Password for jane: Verifying password New Password for jane: Password changed. Adding <command>su</command> privileges Kerberos allows us to give each user who needs root privileges their own separate supassword. We could now add an id which is authorized to su to root. This is controlled by having an instance of root associated with a principal. Using kdb_edit we can create the entry jane.root in the Kerberos database: &prompt.root; kdb_edit Opening database... Enter Kerberos master key: Current Kerberos master key version is 1. Master key entered. BEWARE! Previous or default values are in [brackets] , enter return to leave the same, or new value. Principal name: jane Instance: root <Not found>, Create [y] ? y Principal: jane, Instance: root, kdc_key_ver: 1 New Password: <---- enter a SECURE password here Verifying password New Password: <---- re-enter the password here Principal's new key version = 1 Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ? Max ticket lifetime (*5 minutes) [ 255 ] ? 12 <--- Keep this short! Attributes [ 0 ] ? Edit O.K. Principal name: <---- null entry here will cause an exit Now try getting tokens for it to make sure it works: &prompt.root; kinit jane.root MIT Project Athena (grunt.grondar.za) Kerberos Initialization for "jane.root" Password: Now we need to add the user to root's .klogin file: &prompt.root; cat /root/.klogin jane.root@GRONDAR.ZA Now try doing the su: &prompt.user; su Password: and take a look at what tokens we have: &prompt.root; klist Ticket file: /tmp/tkt_root_245 Principal: jane.root@GRONDAR.ZA Issued Expires Principal May 2 20:43:12 May 3 04:43:12 krbtgt.GRONDAR.ZA@GRONDAR.ZA Using other commands In an earlier example, we created a principal called jane with an instance root. This was based on a user with the same name as the principal, and this is a Kerberos default; that a <principal>.<instance> of the form <username>.root will allow that <username> to su to root if the necessary entries are in the .klogin file in root's home directory: &prompt.root; cat /root/.klogin jane.root@GRONDAR.ZA Likewise, if a user has in their own home directory lines of the form: &prompt.user; cat ~/.klogin jane@GRONDAR.ZA jack@GRONDAR.ZA This allows anyone in the GRONDAR.ZA realm who has authenticated themselves to jane or jack (via kinit, see above) access to rlogin to jane's account or files on this system (grunt) via rlogin, rsh or rcp. For example, Jane now logs into another system, using Kerberos: &prompt.user; kinit MIT Project Athena (grunt.grondar.za) Password: %prompt.user; rlogin grunt Last login: Mon May 1 21:14:47 from grumble Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD BUILT-19950429 (GR386) #0: Sat Apr 29 17:50:09 SAT 1995 Or Jack logs into Jane's account on the same machine (Jane having set up the .klogin file as above, and the person in charge of Kerberos having set up principal jack with a null instance: &prompt.user; kinit &prompt.user; rlogin grunt -l jane MIT Project Athena (grunt.grondar.za) Password: Last login: Mon May 1 21:16:55 from grumble Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD BUILT-19950429 (GR386) #0: Sat Apr 29 17:50:09 SAT 1995 Firewalls Contributed by &a.gpalmer; and &a.alex;. Firewalls are an area of increasing interest for people who are connected to the Internet, and are even finding applications on private networks to provide enhanced security. This section will hopefully explain what firewalls are, how to use them, and how to use the facilities provided in the FreeBSD kernel to implement them. People often think that having a firewall between your internal network and the Big Bad Internet will solve all your security problems. It may help, but a poorly setup firewall system is more of a security risk than not having one at all. A firewall can add another layer of security to your systems, but it cannot stop a really determined cracker from penetrating your internal network. If you let internal security lapse because you believe your firewall to be impenetrable, you have just made the crackers job that much easier. What is a firewall? There are currently two distinct types of firewalls in common use on the Internet today. The first type is more properly called a packet filtering router, where the kernel on a multi-homed machine chooses whether to forward or block packets based on a set of rules. The second type, known as a proxy server, relies on daemons to provide authentication and to forward packets, possibly on a multi-homed machine which has kernel packet forwarding disabled. Sometimes sites combine the two types of firewalls, so that only a certain machine (known as a bastion host) is allowed to send packets through a packet filtering router onto an internal network. Proxy services are run on the bastion host, which are generally more secure than normal authentication mechanisms. FreeBSD comes with a kernel packet filter (known as IPFW), which is what the rest of this section will concentrate on. Proxy servers can be built on FreeBSD from third party software, but there is such a variety of proxy servers available that it would be impossible to cover them in this document. Packet filtering routers A router is a machine which forwards packets between two or more networks. A packet filtering router has an extra piece of code in its kernel which compares each packet to a list of rules before deciding if it should be forwarded or not. Most modern IP routing software has packet filtering code within it that defaults to forwarding all packets. To enable the filters, you need to define a set of rules for the filtering code so it can decide if the packet should be allowed to pass or not. To decide whether a packet should be passed on, the code looks through its set of rules for a rule which matches the contents of this packets headers. Once a match is found, the rule action is obeyed. The rule action could be to drop the packet, to forward the packet, or even to send an ICMP message back to the originator. Only the first match counts, as the rules are searched in order. Hence, the list of rules can be referred to as a rule chain. The packet matching criteria varies depending on the software used, but typically you can specify rules which depend on the source IP address of the packet, the destination IP address, the source port number, the destination port number (for protocols which support ports), or even the packet type (UDP, TCP, ICMP, etc). Proxy servers Proxy servers are machines which have had the normal system daemons (telnetd, ftpd, etc) replaced with special servers. These servers are called proxy servers as they normally only allow onward connections to be made. This enables you to run (for example) a proxy telnet server on your firewall host, and people can telnet in to your firewall from the outside, go through some authentication mechanism, and then gain access to the internal network (alternatively, proxy servers can be used for signals coming from the internal network and heading out). Proxy servers are normally more secure than normal servers, and often have a wider variety of authentication mechanisms available, including one-shot password systems so that even if someone manages to discover what password you used, they will not be able to use it to gain access to your systems as the password instantly expires. As they do not actually give users access to the host machine, it becomes a lot more difficult for someone to install backdoors around your security system. Proxy servers often have ways of restricting access further, so that only certain hosts can gain access to the servers, and often they can be set up so that you can limit which users can talk to which destination machine. Again, what facilities are available depends largely on what proxy software you choose. What does IPFW allow me to do? IPFW, the software supplied with FreeBSD, is a packet filtering and accounting system which resides in the kernel, and has a user-land control utility, &man.ipfw.8;. Together, they allow you to define and query the rules currently used by the kernel in its routing decisions. There are two related parts to IPFW. The firewall section allows you to perform packet filtering. There is also an IP accounting section which allows you to track usage of your router, based on similar rules to the firewall section. This allows you to see (for example) how much traffic your router is getting from a certain machine, or how much WWW (World Wide Web) traffic it is forwarding. As a result of the way that IPFW is designed, you can use IPFW on non-router machines to perform packet filtering on incoming and outgoing connections. This is a special case of the more general use of IPFW, and the same commands and techniques should be used in this situation. Enabling IPFW on FreeBSD As the main part of the IPFW system lives in the kernel, you will need to add one or more options to your kernel configuration file, depending on what facilities you want, and recompile your kernel. See reconfiguring the kernel for more details on how to recompile your kernel. There are currently three kernel configuration options relevant to IPFW: options IPFIREWALL Compiles into the kernel the code for packet filtering. options IPFIREWALL_VERBOSE Enables code to allow logging of packets through &man.syslogd.8;. Without this option, even if you specify that packets should be logged in the filter rules, nothing will happen. options IPFIREWALL_VERBOSE_LIMIT=10 Limits the number of packets logged through &man.syslogd.8; on a per entry basis. You may wish to use this option in hostile environments in which you want to log firewall activity, but do not want to be open to a denial of service attack via syslog flooding. When a chain entry reaches the packet limit specified, logging is turned off for that particular entry. To resume logging, you will need to reset the associated counter using the &man.ipfw.8; utility: &prompt.root; ipfw zero 4500 Where 4500 is the chain entry you wish to continue logging. Previous versions of FreeBSD contained an IPFIREWALL_ACCT option. This is now obsolete as the firewall code automatically includes accounting facilities. Configuring IPFW The configuration of the IPFW software is done through the &man.ipfw.8; utility. The syntax for this command looks quite complicated, but it is relatively simple once you understand its structure. There are currently four different command categories used by the utility: addition/deletion, listing, flushing, and clearing. Addition/deletion is used to build the rules that control how packets are accepted, rejected, and logged. Listing is used to examine the contents of your rule set (otherwise known as the chain) and packet counters (accounting). Flushing is used to remove all entries from the chain. Clearing is used to zero out one or more accounting entries. Altering the IPFW rules The syntax for this form of the command is: ipfw -N command index action log protocol addresses options There is one valid flag when using this form of the command: -N Resolve addresses and service names in output. The command given can be shortened to the shortest unique form. The valid commands are: add Add an entry to the firewall/accounting rule list delete Delete an entry from the firewall/accounting rule list Previous versions of IPFW used separate firewall and accounting entries. The present version provides packet accounting with each firewall entry. If an index value is supplied, it used to place the entry at a specific point in the chain. Otherwise, the entry is placed at the end of the chain at an index 100 greater than the last chain entry (this does not include the default policy, rule 65535, deny). The log option causes matching rules to be output to the system console if the kernel was compiled with IPFIREWALL_VERBOSE. Valid actions are: reject Drop the packet, and send an ICMP host or port unreachable (as appropriate) packet to the source. allow Pass the packet on as normal. (aliases: pass and accept) deny Drop the packet. The source is not notified via an ICMP message (thus it appears that the packet never arrived at the destination). count Update packet counters but do not allow/deny the packet based on this rule. The search continues with the next chain entry. Each action will be recognized by the shortest unambiguous prefix. The protocols which can be specified are: all Matches any IP packet icmp Matches ICMP packets tcp Matches TCP packets udp Matches UDP packets The address specification is: from address/maskport to address/maskport via interface You can only specify port in conjunction with protocols which support ports (UDP and TCP). The is optional and may specify the IP address or domain name of a local IP interface, or an interface name (e.g. ed0) to match only packets coming through this interface. Interface unit numbers can be specified with an optional wildcard. For example, ppp* would match all kernel PPP interfaces. The syntax used to specify an address/mask is: address or address/mask-bits or address:mask-pattern A valid hostname may be specified in place of the IP address. is a decimal number representing how many bits in the address mask should be set. e.g. specifying 192.216.222.1/24 will create a mask which will allow any address in a class C subnet (in this case, 192.216.222) to be matched. is an IP address which will be logically AND'ed with the address given. The keyword any may be used to specify any IP address. The port numbers to be blocked are specified as: port,port,port to specify either a single port or a list of ports, or port-port to specify a range of ports. You may also combine a single range with a list, but the range must always be specified first. The options available are: frag Matches if the packet is not the first fragment of the datagram. in Matches if the packet is on the way in. out Matches if the packet is on the way out. ipoptions spec Matches if the IP header contains the comma separated list of options specified in spec. The supported list of IP options are: ssrr (strict source route), lsrr (loose source route), rr (record packet route), and ts (timestamp). The absence of a particular option may be denoted with a leading !. established Matches if the packet is part of an already established TCP connection (i.e. it has the RST or ACK bits set). You can optimize the performance of the firewall by placing established rules early in the chain. setup Matches if the packet is an attempt to establish a TCP connection (the SYN bit set is set but the ACK bit is not). tcpflags flags Matches if the TCP header contains the comma separated list of flags. The supported flags are fin, syn, rst, psh, ack, and urg. The absence of a particular flag may be indicated by a leading !. icmptypes types Matches if the ICMP type is present in the list types. The list may be specified as any combination of ranges and/or individual types separated by commas. Commonly used ICMP types are: 0 echo reply (ping reply), 3 destination unreachable, 5 redirect, 8 echo request (ping request), and 11 time exceeded (used to indicate TTL expiration as with &man.traceroute.8;). Listing the IPFW rules The syntax for this form of the command is: ipfw -a -t -N l There are three valid flags when using this form of the command: -a While listing, show counter values. This option is the only way to see accounting counters. -t Display the last match times for each chain entry. The time listing is incompatible with the input syntax used by the &man.ipfw.8; utility. -N Attempt to resolve given addresses and service names. Flushing the IPFW rules The syntax for flushing the chain is: ipfw flush This causes all entries in the firewall chain to be removed except the fixed default policy enforced by the kernel (index 65535). Use caution when flushing rules, the default deny policy will leave your system cut off from the network until allow entries are added to the chain. Clearing the IPFW packet counters The syntax for clearing one or more packet counters is: ipfw zero index When used without an index argument, all packet counters are cleared. If an index is supplied, the clearing operation only affects a specific chain entry. Example commands for ipfw This command will deny all packets from the host evil.crackers.org to the telnet port of the host nice.people.org by being forwarded by the router: &prompt.root ipfw add deny tcp from evil.crackers.org to nice.people.org 23 The next example denies and logs any TCP traffic from the entire crackers.org network (a class C) to the nice.people.org machine (any port). &prompt.root; ipfw add deny log tcp from evil.crackers.org/24 to nice.people.org If you do not want people sending X sessions to your internal network (a subnet of a class C), the following command will do the necessary filtering: &prompt.root; ipfw add deny tcp from any to my.org/28 6000 setup To see the accounting records: &prompt.root; ipfw -a list or in the short form &prompt.root; ipfw -a l You can also see the last time a chain entry was matched with: &prompt.root; ipfw -at l Building a packet filtering firewall The following suggestions are just that: suggestions. The requirements of each firewall are different and I cannot tell you how to build a firewall to meet your particular requirements. When initially setting up your firewall, unless you have a test bench setup where you can configure your firewall host in a controlled environment, I strongly recommend you use the logging version of the commands and enable logging in the kernel. This will allow you to quickly identify problem areas and cure them without too much disruption. Even after the initial setup phase is complete, I recommend using the logging for of `deny' as it allows tracing of possible attacks and also modification of the firewall rules if your requirements alter. If you use the logging versions of the accept command, it can generate large amounts of log data as one log line will be generated for every packet that passes through the firewall, so large ftp/http transfers, etc, will really slow the system down. It also increases the latencies on those packets as it requires more work to be done by the kernel before the packet can be passed on. syslogd with also start using up a lot more processor time as it logs all the extra data to disk, and it could quite easily fill the partition /var/log is located on. You should enable your firewall from /etc/rc.conf.local or /etc/rc.conf. The associated manpage explains which knobs to fiddle and lists some preset firewall configurations. If you do not use a preset configuration, ipfw list will output the current ruleset into a file that you can pass to rc.conf. If you do not use /etc/rc.conf.local or /etc/rc.conf to enable your firewall, it is important to make sure your firewall is enabled before any IP interfaces are configured. The next problem is what your firewall should actually do! This is largely dependent on what access to your network you want to allow from the outside, and how much access to the outside world you want to allow from the inside. Some general rules are: Block all incoming access to ports below 1024 for TCP. This is where most of the security sensitive services are, like finger, SMTP (mail) and telnet. Block all incoming UDP traffic. There are very few useful services that travel over UDP, and what useful traffic there is is normally a security threat (e.g. Suns RPC and NFS protocols). This has its disadvantages also, since UDP is a connectionless protocol, denying incoming UDP traffic also blocks the replies to outgoing UDP traffic. This can cause a problem for people (on the inside) using external archie (prospero) servers. If you want to allow access to archie, you'll have to allow packets coming from ports 191 and 1525 to any internal UDP port through the firewall. ntp is another service you may consider allowing through, which comes from port 123. Block traffic to port 6000 from the outside. Port 6000 is the port used for access to X11 servers, and can be a security threat (especially if people are in the habit of doing xhost + on their workstations). X11 can actually use a range of ports starting at 6000, the upper limit being how many X displays you can run on the machine. The upper limit as defined by RFC 1700 (Assigned Numbers) is 6063. Check what ports any internal servers use (e.g. SQL servers, etc). It is probably a good idea to block those as well, as they normally fall outside the 1-1024 range specified above. Another checklist for firewall configuration is available from CERT at ftp://ftp.cert.org/pub/tech_tips/packet_filtering As I said above, these are only guidelines. You will have to decide what filter rules you want to use on your firewall yourself. I cannot accept ANY responsibility if someone breaks into your network, even if you follow the advice given above. OpenSSL As of FreeBSD 4.0, the OpenSSL toolkit is a part of the base system. OpenSSL provides a general-purpose cryptography library, as well as the Secure Sockets Layer v2/v3 (SSLv2/SSLv3) and Transport Layer Security v1 (TLSv1) network security protocols. However, some of the algorithms (specifically, RSA and IDEA) included in OpenSSL are protected by patents in the USA and elsewhere, and are not available for unrestricted use (in particular, IDEA is not available at all in FreeBSD's version of OpenSSL). As a result, FreeBSD has available two different versions of the OpenSSL RSA libraries depending on geographical location (USA/non-USA). Source Code Installations OpenSSL is part of the src-crypto and src-securecvsup collections. See the Obtaining FreeBSD section for more information about obtaining and updating FreeBSD source code. International (Non-USA) Users People who are located outside the USA, and who obtain their crypto sources from internat.FreeBSD.org (the International Crypto Repository) or an international mirror site, will build a version of OpenSSL which includes the native OpenSSL implementation of RSA, but does not include IDEA, because the latter is restricted in certain locations elsewhere in the world. In the future a more flexible geographical identification system may allow building of IDEA in countries for which it is not restricted. Please be aware of any local restrictions on the import, use and redistribution of cryptography which may exist in your country. USA Users As noted above, RSA is patented in the USA, with terms preventing general use without an appropriate license. Therefore the standard OpenSSL RSA code may not be used in the USA, and has been removed from the version of OpenSSL carried on USA mirror sites. The RSA patent is due to expire on September 20, 2000, at which time it is intended to add the full RSA code back to the USA version of OpenSSL. However (and fortunately), the RSA patent holder (RSA Security, has provided a RSA reference implementation toolkit (RSAREF) which is available for certain classes of use, including non-commercial use (see the RSAREF license for their definition of non-commercial). If you meet the conditions of the RSAREF license and wish to use it in conjunction with OpenSSL to provide RSA support, you can install the rsaref port, which is located in /usr/ports/security/rsaref, or the rsaref-2.0 package. The OpenSSL library will then automatically detect and use the RSAREF libraries. Please obtain legal advice if you are unsure of your compliance with the license terms. The RSAREF implementation is inferior to the native OpenSSL implementation (it is much slower, and cannot be used with keys larger than 1024 bits). If you are not located in the USA then you are doing yourself a disadvantage by using RSAREF. Users who have purchased an appropriate RSA source code license from RSA Security may use the International version of OpenSSL described above to obtain native RSA support. IDEA code is also removed from the USA version of OpenSSL for patent reasons. Binary Installations If your FreeBSD installation was a binary installation (e.g., installed from the Walnut Creek CDROM, or from a snapshot downloaded from ftp.FreeBSD.org) and you selected to install the crypto collection, then the sysinstall utility will automatically select the correct version to install during the installation process. If the international version was selected but could not be installed during sysinstall (e.g. you have not configured network access, and the version must be downloaded from a FTP site) then you can add the international RSA library after installation as a package. The librsaintl package contains the RSA code for International (non-USA) users. This is not legal for use in the USA, but international users should use this version because the RSA implementation is faster and more flexible. It is available from ftp.internat.FreeBSD.org and does not require RSAREF. IPsec Contributed by &a.shin;, 5 March 2000. IPsec mechanism provides secure communication either for IP layer and socket layer communication. This section should explain how to use them. About IPsec implementation, please refer section 23.5.4. The current IPsec implementation supports both transport mode and tunnel mode. However, tunnel mode comes with some restrictions. http://www.kame.net/newsletter/ has more comprehensive examples. Transport mode example with IPv4 Let's setup security association to deploy a secure channel between HOST A (10.2.3.4) and HOST B (10.6.7.8). Here we show a little complicated example. From HOST A to HOST B, only old AH is used. From HOST B to HOST A, new AH and new ESP are combined. Now we should choose algorithm to be used corresponding to "AH"/"new AH"/"ESP"/"new ESP". Please refer to the &man.setkey.8; man page to know algorithm names. Our choice is MD5 for AH, new-HMAC-SHA1 for new AH, and new-DES-expIV with 8 byte IV for new ESP. Key length highly depends on each algorithm. For example, key length must be equal to 16 bytes for MD5, 20 for new-HMAC-SHA1, and 8 for new-DES-expIV. Now we choose "MYSECRETMYSECRET", "KAMEKAMEKAMEKAMEKAME", "PASSWORD", respectively. OK, let's assign SPI (Security Parameter Index) for each protocol. Please note that we need 3 SPIs for this secure channel since three security headers are produced (one for from HOST A to HOST B, two for from HOST B to HOST A). Please also note that SPI MUST be greater than or equal to 256. We choose, 1000, 2000, and 3000, respectively. (1) HOST A ------> HOST B (1)PROTO=AH ALG=MD5(RFC1826) KEY=MYSECRETMYSECRET SPI=1000 (2.1) HOST A <------ HOST B <------ (2.2) (2.1) PROTO=AH ALG=new-HMAC-SHA1(new AH) KEY=KAMEKAMEKAMEKAMEKAME SPI=2000 (2.2) PROTO=ESP ALG=new-DES-expIV(new ESP) IV length = 8 KEY=PASSWORD SPI=3000 Now, let's setup security association. Execute &man.setkey.8; on both HOST A and B: &prompt.root; setkey -c add 10.2.3.4 10.6.7.8 ah-old 1000 -m transport -A keyed-md5 "MYSECRETMYSECRET" ; add 10.6.7.8 10.2.3.4 ah 2000 -m transport -A hmac-sha1 "KAMEKAMEKAMEKAMEKAME" ; add 10.6.7.8 10.2.3.4 esp 3000 -m transport -E des-cbc "PASSWORD" ; ^D Actually, IPsec communication doesn't process until security policy entries will be defined. In this case, you must setup each host. At A: &prompt.root; setkey -c spdadd 10.2.3.4 10.6.7.8 any -P out ipsec ah/transport/10.2.3.4-10.6.7.8/require ; ^D At B: &prompt.root; setkey -c spdadd 10.6.7.8 10.2.3.4 any -P out ipsec esp/transport/10.6.7.8-10.2.3.4/require ; spdadd 10.6.7.8 10.2.3.4 any -P out ipsec ah/transport/10.6.7.8-10.2.3.4/require ; ^D HOST A --------------------------------------> HOST E 10.2.3.4 10.6.7.8 | | ========== old AH keyed-md5 ==========> <========= new AH hmac-sha1 =========== <========= new ESP des-cbc ============ Transport mode example with IPv6 Another example using IPv6. ESP transport mode is recommended for TCP port number 110 between Host-A and Host-B. ============ ESP ============ | | Host-A Host-B fec0::10 -------------------- fec0::11 Encryption algorithm is blowfish-cbc whose key is "kamekame", and authentication algorithm is hmac-sha1 whose key is "this is the test key". Configuration at Host-A: &prompt.root; setkey -c <<EOF spdadd fec0::10[any] fec0::11[110] tcp -P out ipsec esp/transport/fec0::10-fec0::11/use ; spdadd fec0::11[110] fec0::10[any] tcp -P in ipsec esp/transport/fec0::11-fec0::10/use ; add fec0::10 fec0::11 esp 0x10001 -m transport -E blowfish-cbc "kamekame" -A hmac-sha1 "this is the test key" ; add fec0::11 fec0::10 esp 0x10002 -m transport -E blowfish-cbc "kamekame" -A hmac-sha1 "this is the test key" ; EOF and at Host-B: &prompt.root; setkey -c <<EOF spdadd fec0::11[110] fec0::10[any] tcp -P out ipsec esp/transport/fec0::11-fec0::10/use ; spdadd fec0::10[any] fec0::11[110] tcp -P in ipsec esp/transport/fec0::10-fec0::11/use ; add fec0::10 fec0::11 esp 0x10001 -m transport -E blowfish-cbc "kamekame" -A hmac-sha1 "this is the test key" ; add fec0::11 fec0::10 esp 0x10002 -m transport -E blowfish-cbc "kamekame" -A hmac-sha1 "this is the test key" ; EOF Note the direction of SP. Tunnel mode example with IPv4 Tunnel mode between two security gateways Security protocol is old AH tunnel mode, i.e. specified by RFC1826, with keyed-md5 whose key is "this is the test" as authentication algorithm. ======= AH ======= | | Network-A Gateway-A Gateway-B Network-B 10.0.1.0/24 ---- 172.16.0.1 ----- 172.16.0.2 ---- 10.0.2.0/24 Configuration at Gateway-A: &prompt.root; setkey -c <<EOF spdadd 10.0.1.0/24 10.0.2.0/24 any -P out ipsec ah/tunnel/172.16.0.1-172.16.0.2/require ; spdadd 10.0.2.0/24 10.0.1.0/24 any -P in ipsec ah/tunnel/172.16.0.2-172.16.0.1/require ; add 172.16.0.1 172.16.0.2 ah-old 0x10003 -m any -A keyed-md5 "this is the test" ; add 172.16.0.2 172.16.0.1 ah-old 0x10004 -m any -A keyed-md5 "this is the test" ; EOF If port number field is omitted such above then "[any]" is employed. `-m' specifies the mode of SA to be used. "-m any" means wild-card of mode of security protocol. You can use this SA for both tunnel and transport mode. and at Gateway-B: &prompt.root; setkey -c <<EOF spdadd 10.0.2.0/24 10.0.1.0/24 any -P out ipsec ah/tunnel/172.16.0.2-172.16.0.1/require ; spdadd 10.0.1.0/24 10.0.2.0/24 any -P in ipsec ah/tunnel/172.16.0.1-172.16.0.2/require ; add 172.16.0.1 172.16.0.2 ah-old 0x10003 -m any -A keyed-md5 "this is the test" ; add 172.16.0.2 172.16.0.1 ah-old 0x10004 -m any -A keyed-md5 "this is the test" ; EOF Making SA bundle between two security gateways AH transport mode and ESP tunnel mode is required between Gateway-A and Gateway-B. In this case, ESP tunnel mode is applied first, and AH transport mode is next. ========== AH ========= | ======= ESP ===== | | | | | Network-A Gateway-A Gateway-B Network-B fec0:0:0:1::/64 --- fec0:0:0:1::1 ---- fec0:0:0:2::1 --- fec0:0:0:2::/64 Tunnel mode example with IPv6 Encryption algorithm is 3des-cbc, and authentication algorithm for ESP is hmac-sha1. Authentication algorithm for AH is hmac-md5. Configuration at Gateway-A: &prompt.root; setkey -c <<EOF spdadd fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out ipsec esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ah/transport/fec0:0:0:1::1-fec0:0:0:2::1/require ; spdadd fec0:0:0:2::/64 fec0:0:0:1::/64 any -P in ipsec esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ah/transport/fec0:0:0:2::1-fec0:0:0:1::1/require ; add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10001 -m tunnel -E 3des-cbc "kamekame12341234kame1234" -A hmac-sha1 "this is the test key" ; add fec0:0:0:1::1 fec0:0:0:2::1 ah 0x10001 -m transport -A hmac-md5 "this is the test" ; add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10001 -m tunnel -E 3des-cbc "kamekame12341234kame1234" -A hmac-sha1 "this is the test key" ; add fec0:0:0:2::1 fec0:0:0:1::1 ah 0x10001 -m transport -A hmac-md5 "this is the test" ; EOF Making SAs with the different end ESP tunnel mode is required between Host-A and Gateway-A. Encryption algorithm is cast128-cbc, and authentication algorithm for ESP is hmac-sha1. ESP transport mode is recommended between Host-A and Host-B. Encryption algorithm is rc5-cbc, and authentication algorithm for ESP is hmac-md5. ================== ESP ================= | ======= ESP ======= | | | | | Host-A Gateway-A Host-B fec0:0:0:1::1 ---- fec0:0:0:2::1 ---- fec0:0:0:2::2 Configuration at Host-A: &prompt.root; setkey -c <<EOF spdadd fec0:0:0:1::1[any] fec0:0:0:2::2[80] tcp -P out ipsec esp/transport/fec0:0:0:1::1-fec0:0:0:2::2/use esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ; spdadd fec0:0:0:2::1[80] fec0:0:0:1::1[any] tcp -P in ipsec esp/transport/fec0:0:0:2::2-fec0:0:0:l::1/use esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ; add fec0:0:0:1::1 fec0:0:0:2::2 esp 0x10001 -m transport -E cast128-cbc "12341234" -A hmac-sha1 "this is the test key" ; add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10002 -E rc5-cbc "kamekame" -A hmac-md5 "this is the test" ; add fec0:0:0:2::2 fec0:0:0:1::1 esp 0x10003 -m transport -E cast128-cbc "12341234" -A hmac-sha1 "this is the test key" ; add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10004 -E rc5-cbc "kamekame" -A hmac-md5 "this is the test" ; EOF