Wednesday, June 25, 2014

Wireshark on Android

The other day I found myself wishing I could run wireshark in realtime on an Android phone, but use the familiar GUI on my laptop. After a few minutes tinkering around, I was doing exactly that.

The phone belonged to an Android developer, so he'd already rooted it, enabled developer tools, etc... He'd also installed a packet capture application which worked, but didn't allow me to see things in real time.

The Android SDK bundle contains the adb binary, which is required for connecting to the phone. Extract adb and drop it somewhere in $PATH
 # run adb as root:  
 adb root  
 # connect to the phone over WiFi (the phone's owner had
 # already enabled this feature with 'adb tcpip' via USB):  
 adb connect <phone's wifi ip address>  
 # check that we get a root shell on the phone:  
 adb shell 'id'  

It turns out that the packet capture application included a tcpdump binary at /data/data/lv.n3o.shark/files/tcpdump, and invoking it from the adb shell worked normally. It produced the usual startup message, and then a one line summary of each packet.
 adb shell '/data/data/lv.n3o.shark/files/tcpdump -c 2'
 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
 listening on wlan0, link-type EN10MB (Ethernet), capture size 96 bytes
 12:37:04.053553 IP 192.168.1.8.rplay > 192.168.1.22.57182: P 2817938036:2817938134(98) ack 1887364010 win 1358 
 12:37:04.054244 IP 192.168.1.8.42742 > google-public-dns-a.google.com.domain: 12160+ PTR? 8.1.168.192.in-addr.arpa. (44)


Progress, but not wireshark. I expected that adding the '-w -' flag (write pcap data to STDOUT) to tcpdump would allow me to collect pcap data from adb's STDOUT on my macbook, and feed it into wireshark, but didn't give the result I wanted. There were two problems:
  1. Tcpdump's informational messages got mingled with the pcap data when the data came out of adb shell. This was a problem.
  2. Binary data pipelined through the adb shell got corrupted. The adb shell is a tty of some flavor, I guess, and this is not a safe way to pass binary data.
I solved the first problem by trashing tcpdump's informational messages. These go to STDERR on the phone, but were mingled with STDOUT when they reached the shell on the macbook. '2>/dev/null' (trash stderr) got rid of problem #1.

Problem #2 was resolved by base64 encoding the pcap data on the phone, before handing it to adb shell, and then removing the base64 encoding on the macbook. Fortunately there's a base64 binary in the root user's path on this Android phone.

The bits that go into the final solution are:
  • adb shell 'command' (run command on the phone)
  • tcpdump -w - 2>/dev/null | base64 (run tcpdump, trash STDERR, encode the output)
  • base64 -D (undo the base64 encoding - this runs on the mac)
  • wireshark -ki - (wireshark '-k' means start capture immediately, '-i -' means read from stdin)
Putting it all together we have:
 adb shell '/data/data/lv.n3o.shark/files/tcpdump -w - 2>/dev/null | base64' | base64 -D | wireshark -ki -  

Job done. The command above connects to the remote Android phone, fires up tcpdump on the phone, fires up wireshark on my laptop, and bolts the two together, making it possible to work with packets (as seen by the phone) in real time on a laptop.

It's not 100% real time, there's some batching and latency, but it was okay for my purposes. Adding tcpdump's '-U' flag might help here.

Tuesday, June 3, 2014

Relaying email with postfix + TLS through gmail

I needed to relay email from appliances in my house, and wanted to use my gmail domain + TLS to do it. Following are my notes from setting up a postfix server to do that job. All email relayed by this server appear to be sourced from the gmail account I created for it.

I wouldn't use this for anything customer-facing, but it's a reasonable way to get messages out of closed environments without worrying about how the messages were sourced, who they appear to be from, will SPF records screw things up, etc...

Create gmail account
I'm using an account named postfix-relay@marget.com. I set that guy up, and gave him a password.

Install Linux somewhere
I'm using a minimal installation of CentOS 6.5 for this project, installed with some automated nonsense I've long used for this sort of thing.

Tweak hostname
 sed -i 's/localhost.localdomain/postfix-relay.marget.com/' /etc/sysconfig/network  

NFS mount my CentOS repository
The next little bit uses automounter to hang my CentOS repository on /CentOS and configure it as a repository. Skip it.
 yum install -y nfs-utils wget tcpdump unzip autofs  
 service rpcbind start  
 service autofs restart  
 ln -s /net/my_nfs_server/path/to/CentOS/ /CentOS  
 cp /etc/yum.repos.d/CentOS-Media.repo /etc/yum.repos.d/CentOS-NFS.repo  
 sed -i -e 's/\/media//' /etc/yum.repos.d/CentOS-NFS.repo  
 sed -i -e 's/c6-media/c6-nfs/' /etc/yum.repos.d/CentOS-NFS.repo  

Update and install packages
 yum update -y  
 yum install -y ntp ntpdate wget unzip openssl-perl setools-console  
 yum install -y telnet cyrus-sasl-md5 cyrus-sasl-plain  

Install and configure a user, sudo and ssh.
 useradd chris -u <myUID>  
 groupmems -g wheel -a chris  
 sed -i 's/# \(\%wheel.*NOPASSWD.*$\)/\1/' /etc/sudoers  
 (echo;echo) | su -c 'ssh-keygen -t rsa' chris  
 wget -O ~chris/.ssh/authorized_keys http://server.where/i_keep_my_public_key.pub  
 chmod 600 ~chris/.ssh/authorized_keys  
 chown chris:chris ~chris/.ssh/authorized_keys  
 sed -i 's/^#*PermitRootLogin.*$/PermitRootLogin no/' /etc/ssh/sshd_config  
 service sshd restart  

NTP
The first couple of lines here edit the init script so that service ntpd status dumps peer synchronization info rather than just ntpd is running.
 export NTPQ='[ -x /usr/sbin/ntpq ] \&\& /usr/sbin/ntpq -c peers'
 sed -i "s|[[:space:]]status \$prog$|&\n\t$NTPQ|" /etc/init.d/ntpd
 chkconfig ntpdate on
 chkconfig ntpd on
 service ntpdate start
 service ntpd start
 sleep 5; service ntpd status

Certificate Authority
Postfix needs a certificate for this TLS business to work, but it doesn't need to be from a CA that gmail recognizes. I'm creating a CA just to sign one certificate (well, two: the CA's own certificate, and the certificate postfix will be using). The CA protects its key with a PEM passphrase. I like to use openssl rand -base64 40 to create PEM passphrases. The passphrase will be needed only 4 times (creation/confirmation/CA cert signing/postfix cert signing). Because this is a single-use CA, it's okay to forget the passphrase after we're done here. If you make a mistake in this section and want to start over with the CA stuff, just rm -rf /etc/pki/CA and start over.

The 'challenge password' doesn't matter. These exist so that a certificate holder may prove her identity when requesting revocation of a lost certificate. They prevent spoofing of revocation requests, and don't make sense as far as I'm aware in the context of a root CA certificate.
 # /etc/pki/tls/misc/CA.pl -newca  
 CA certificate filename (or enter to create)  <enter>
   
 Making CA certificate ...  
 Generating a 2048 bit RSA private key  
 ........+++  
 ....+++  
 writing new private key to '/etc/pki/CA/private/cakey.pem'  
 Enter PEM pass phrase:  ThisIsMyCApassphrase
 Verifying - Enter PEM pass phrase:   ThisIsMyCApassphrase 
 -----  
 You are about to be asked to enter information that will be incorporated  
 into your certificate request.  
 What you are about to enter is what is called a Distinguished Name or a DN.  
 There are quite a few fields but you can leave some blank  
 For some fields there will be a default value,  
 If you enter '.', the field will be left blank.  
 -----  
 Country Name (2 letter code) [XX]:US  
 State or Province Name (full name) []:New Hampshire  
 Locality Name (eg, city) [Default City]:Brookline  
 Organization Name (eg, company) [Default Company Ltd]:Marget Labs  
 Organizational Unit Name (eg, section) []:Basement  
 Common Name (eg, your name or your server's hostname) []:postfix  
 Email Address []:postfix-relay-admin@marget.com  
   
 Please enter the following 'extra' attributes  
 to be sent with your certificate request  
 A challenge password []: blahblahblah  
 An optional company name []:  <enter>
 Using configuration from /etc/pki/tls/openssl.cnf  
 Enter pass phrase for /etc/pki/CA/private/cakey.pem:   ThisIsMyCApassphrase 
 Check that the request matches the signature  
 Signature ok  
 Certificate Details:  
 <snip>  

Display the CA Certificate
Not necessary, just taking a look. /etc/pki/CA/cacert.pem is the CA certificate.
 openssl x509 -in /etc/pki/CA/cacert.pem -noout -text  

Copy the CA certificate into the postfix directory
 cp /etc/pki/CA/cacert.pem /etc/pki/tls/certs/POSTFIX-TRUST.pem  

Create a Certificate Signing Request (CSR)
Input to the CSR creation is the subject name (string below) and a public/private pair. This command creates the keys and the CSR. The keypair (both parts) will be stored in POSTFIX-key.pem, and the CSR will be stored in POSTFIX-req.pem
openssl req -new -nodes -subj '/CN=postfix/O=Marget Labs/C=US/ST=New Hampshire/L=Brookline/emailAddress=postfix@home.marget.com' -keyout POSTFIX-key.pem -out POSTFIX-req.pem  

Display the CSR
Just because we're curious.
openssl req -text -noout -in POSTFIX-req.pem  
Sign the Certificate
The CA, using his private key, which is protected with the PEM passphrase, signs the CSR and creates the certificate postfix will be using. This command will spit out some details from the CSR (what are we being asked to sign?) and prompt for the CA's PEM passphrase (4th use of the passphrase here). The new certificate will land in POSTFIX-cert.pem
openssl ca -days 1093 -out POSTFIX-cert.pem -infiles POSTFIX-req.pem  
Display the Certificate
Just because we're curious.
openssl x509 -in POSTFIX-cert.pem -noout -text  

Delete the CSR
Don't need it anymore.
rm -f POSTFIX-req.pem  

Set permissions on the keys and certificate, move them into place.
 chmod 400 POSTFIX-key.pem  
 chmod 644 POSTFIX-cert.pem  
 mv POSTFIX-key.pem /etc/pki/tls/private  
 mv POSTFIX-cert.pem /etc/pki/tls/certs  

Another trusted CA
Google will also be presenting a certificate. In order to trust google, we need to trust the CA that signed their certificate. Trusting that CA suggests we've seen the CA's certificate and public key. So far, we have not. Get Thawte's CA certificate (with public key inside) and append it to our existing file with trusted CA certificates.
 wget https://www.thawte.com/roots/thawte_Premium_Server_CA.pem  
 cat thawte_Premium_Server_CA.pem >> /etc/pki/tls/certs/POSTFIX-TRUST.pem  
 rm -f thawte_Premium_Server_CA.pem  

What have we accomplished so far?
Surprisingly little, actually. Three files have been contributed to the postfix environment:
  • POSTFIX-TRUST.pem < root certificates belonging to both trusted CAs (ours and thawte's)
  • POSTFIX-key.pem < our private key
  • POSTFIX-cert.pem < our signed certificate
But, those three files make us ready to...

Configure postfix main.cf
This is the first of three postfix config files needing attention. In this file we're making use of the 3 files listed above. Some other bits of interest:
  • The mynetworks directive specifies the list of prefixes from which postfix will accept mail for relaying.
  • The inet_interfaces directive specifies the interfaces on which postfix will listen for mail. It might be appropriate to be specific here if the box is multihomed.
  • The smtp_sasl_password_maps directive specifies the file where we keep the password lookup table (not the plain text file - note the postmap command below.
 cat >> /etc/postfix/main.cf << EOF  
 relayhost = [smtp.gmail.com]:587  
 smtp_use_tls = yes  
 smtp_tls_CAfile = /etc/pki/tls/certs/POSTFIX-TRUST.pem  
 smtp_tls_cert_file = /etc/pki/tls/certs/POSTFIX-cert.pem  
 smtp_tls_key_file = /etc/pki/tls/private/POSTFIX-key.pem  
 smtp_tls_session_cache_database = btree:/var/run/smtp_tls_session_cache  
 smtp_tls_security_level = secure  
 smtp_tls_mandatory_protocols = TLSv1  
 smtp_tls_mandatory_ciphers = high  
 smtp_tls_secure_cert_match = nexthop  
 tls_random_source = dev:/dev/urandom  
 smtp_sasl_auth_enable = yes  
 smtp_sasl_security_options = noanonymous  
 smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd  
 inet_interfaces = all  
 mynetworks = 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8 [::1]/128  
 EOF  

 Configure postfix transport file
This file directs postfix about how to deliver certain kinds of messages. In this case we want to send everything to gmail's message submission engine.
 cat >> /etc/postfix/transport << EOF  
 * smtp:smtp.gmail.com:587  
 EOF  

Passwords!
In order to log into gmail, postfix needs to know the password for its account. We enter the password in /etc/postfix/sasl_password, and the postmap command writes it to the lookup table in /etc/postfix/sasl_password.db. Re-run postmap whenever the sasl_password file is edited.
 cat > /etc/postfix/sasl_passwd << EOF  
 [smtp.gmail.com]:587 postfix-relay@marget.com:MyGMailPassword  
 EOF  
   
 chown root:postfix /etc/postfix/sasl_passwd  
 chmod 640 /etc/postfix/sasl_passwd  
 postmap /etc/postfix/sasl_passwd  

Done!
postfix reload will handle most postfix reconfiguration without restarting the whole process. Oh, is postfix even running? I don't know. Heck, lets just do everything:
 postfix reload  
 chkconfig postfix on  
 /etc/init.d/postfix restart  

At this point, postfix should be listening on port 25, and deliver messages to their destination via gmail. And the first bit (sending to gmail) will be secure.