Sunday, April 20, 2008

Fedora Firewall Management 101

On Fedora Core, the iptables rules are stored in /etc/sysconfig/iptables. To change the rules, the iptables(8) utility is used. When done, store the updated rules in /etc/sysconfig/iptables by running the following as root:
# /sbin/iptables-save > /etc/sysconfig/iptables
I'm sure I'll need this again, and I'm sure I'll have forgotten all of it by then.

Thursday, April 17, 2008

D'oh! Stepping over functions in gdb

I feel really dumb at the moment. While debugging, I frequently find myself in need of a "step over function call" functionality. It has always bothered me that the gdb command "step" steps into functions, while I wasn't interested at all in what was happening inside the function. It turns out that gdb has the command I've always wanted, and it is:
(gdb) next

Friday, April 11, 2008

The GNU Debugger (gdb) and Files loaded at Runtime

I'm working on a project where one program loads an ELF file at runtime and then transfers control to that dynamically loaded file. The file will be loaded at an address obtained by malloc(3), so the address will be arbitrary (from a user perspective).

Normally, if a crash occurs while a program is being run under a debugger, the debugger automatically shows you where in the source code the crash occurred. This works only if the debugged program includes debug information. That debug information is stored in the executable file itself. It is ignored by the operating system, but the debugger can use it to resolve addresses to symbols, i.e. to variable and function names.

In my case, if a crash is caused by the dynamically loaded file, then all I see is the register contents and maybe a plain stack trace. That's because the debugger does not know where to find the debug information. Seeing the addresses is useful as it can be used to look up the problematic instruction with objdump. However, debugging would be a lot easier if the addresses could be automatically resolved to symbols by the debugger.

The GNU Debugger (gdb), my debugger of choice, has a add-symbol-file command that instructs the debugger to load symbol information from a file on disk. This command can be issued before the program to be debugged is started, while it's running or even after it has crashed. There are two parameters required by the command. The first one will tell it where to load the debug information from, i.e. the path to the ELF file on disk. The other one will tell it at what address the ELF .text section of the ELF file has been loaded. So the syntax is:
(gdb) add-symbol-file file.elf <address>
For my project, I'm expecting frequent crashes in the dynamically loaded program. So in order to make debugging less time consuming, I created a little .gdbinit file in my home directory that does these things:
  • Tell gdb which program is being debugged. This is the program that will load another one at runtime.
  • Set a breakpoint in that first stage program. The breakpoint needs to be somewhere where the address of the .text section of the dynamically loaded file can be determined.
  • Then start the program.
  • When the breakpoint is hit, print the address of the .text section of the dynamically loaded ELF file.
My .gdbinit file that does the above looks like this:
file primary_stage
break jump_to_second_stage
run
p/x entry_point
Now anytime the debugger is started, it will automatically load the primary stage ELF file, set a breakpoint and start the program. As soon as the breakpoint is hit, the address of the .text section will be printed. Since that's the first print instruction in the debug session, it can now be accessed through the use of a placeholder: $1.

Unfortunately, automatically loading the second stage ELF program from the .gdbinit file does not work. So I then need to manually enter this command:
(gdb) add-symbol-file second_stage $1
Well, it turns out that you can add the above command to the .gdbinit and it will work just fine. I have no idea why that did not work the last time I tried it. Now, a crash in the second_stage program will be reported with full symbol information available to the debugger.

Friday, April 4, 2008

DHCP, DNS and dynamic updates

Today I updated my router, a Soekris net4801 (I think), to OpenBSD 4.2. I know it's dumb to upgrade to OpenBSD 4.2 about three weeks before OpenBSD 4.3 is officially released, but today I actually had time and the box was in desperate need of an update. Also, I have recently moved and the network changed. I used to have a designated server running FreeBSD that also handled DNS and DHCP. When I moved, I shut the server down, and so for the last few weeks I had only very basic DHCP services running and no local DNS at all. Anyways, to make the long story short, I needed a DNS server that would resolve local names as well as a DHCP server that does dynamic DNS upates.

First, I installed OpenBSD 4.2 by netbooting the Soekris box from another OpenBSD "box" running inside Parallels. The instructions for that can be found in the OpenBSD FAQ. There is one thing to remember, though: The Soekris doesn't have a VGA console but only serial, so the PXE-booted kernel needs to be told that it should only use the serial console for output. So the boot.conf file the FAQ mentions needs to look like this:
set tty com0
set stty 9600
boot bsd.rd
Now on to the DHCP and DNS installation. The DHCP server included with OpenBSD will not do dynamic DNS updates, so the ISC DHCP Server is needed. It can be installed by running (as root):
# ftp ftp://ftp.de.openbsd.org/pub/OpenBSD/4.2/packages/i386/isc-dhcp-server-3.0.4p0.tgz
# pkg_add -v isc-dhcp-server-3.0.4p0.tgz
The dhcpd binary will be installed in /usr/local/sbin. Be aware that the base dhcpd included in OpenBSD lives in /usr/sbin, so simply typing "dhcpd" at the command line will most certainly start the OpenBSD DHCP Server! In order to automatically start the DHCP server on boot, these lines need to be added to /etc/rc.local:
if [ -x /usr/local/sbin/dhcpd ] ; then
        echo -n ' dhcpd' ; /usr/local/sbin/dhcpd
fi
Next, before the Server can be fully configured, a key that will be shared between DHCP and DNS server needs to be created:
$ dnssec-keygen -a HMAC-MD5 -b 128 -n USER <name>
This command generates to files in the current working directory. The file with the extension *.private will include the key required later. Wherever the configuration files include a "secret" statement, that value needs to be inserted. The parameter <name> determines the name of the key. This will be used later, but I don't know if the name actually has to be reused. For the rest of this posting, we'll use key_name to represent the generated key's name.

So now on to the DHCP Server configuration. My /etc/dhcpd.conf now looks like this:
option  domain-name "local.deadc0.de";

ddns-update-style ad-hoc;

key key_name {
        algorithm       hmac-md5;
        secret          "...";
}

zone local.deadc0.de. {
        primary 127.0.0.1;
        key key_name;
}

zone 1.168.192.in-addr.arpa. {
        primary 127.0.0.1;
        key key_name;
}

zone 2.168.192.in-addr.arpa. {
        primary 127.0.0.1;
        key key_name;
}

subnet 192.168.1.0 netmask 255.255.255.0 {
        range 192.168.1.1 192.168.1.127;
        
        option domain-name-servers 192.168.1.254;
        option routers 192.168.1.254;
}
        
subnet 192.168.2.0 netmask 255.255.255.0 {
        range 192.168.2.1 192.168.2.127;

        option domain-name-servers 192.168.2.254;
        option routers 192.168.2.254;
}
Now that the DHCP server is configured, the DNS server needs configuration. In OpenBSD, the DNS Server is BIND, but it's started in a chroot environment, so its configuration files live under /var/named. The server configuration file is /var/named/etc/named.conf and looks like this on my system:
include "etc/rndc.key";

controls {
        inet 127.0.0.1 allow {
                localhost;
        } keys {
                key_name;
        };
};

acl clients {
        localnets;
        ::1;
};

options {
        listen-on    { any; };
        listen-on-v6 { any; };

        allow-recursion { clients; };
};

// Standard zones ommited.

zone "local.deadc0.de" {
        type master;
        file "master/local.deadc0.de";
        
        allow-update {
                key     "key_name";
        };
};

zone "1.168.192.in-addr.arpa" {
        type master;
        file "master/1.168.192.in-addr.arpa";

        allow-update {
                key     "key_name";
        };
};

zone "2.168.192.in-addr.arpa" {
        type master;
        file "master/2.168.192.in-addr.arpa";

        allow-update {
                key     "key_name";
        };
};
The actual zone files will not be posted, they are just standard zone declarations, nothing special. However, notice the include statement at top of the file. It includes the key declaration file /var/named/etc/rndc.key that looks like this:
key key_name {
        algorithm hmac-md5;
        secret "...";
};
In order to supress some warning when the DNS server starts, the file /var/named/etc/rndc.conf needs to be created. It should look like this:
options {
        default-server  localhost;
        default-key     "key_name";
};

server localhost {
        key     "key_name";
};

include "rndc.key";
Finally, everything under /var/named/etc and /var/named/master needs to be owned by the user "named", so as root run this:
# chown -R named:named /var/named/etc
# chown -R named:named /var/named/master
Now make sure that the DNS server is enabled by including this line in /etc/rc.conf.local:
named_flags=""
Then reboot the box and that should be it.