Wednesday, December 17, 2008

Compiling Wine 0.9.*

Just a short note.
If you have to compile oldest versions of Wine (0.9.49,0.9.50 and similar), you could encounter a compiler error in signal_i386.c.
If gcc says that VIF_MASK and VIP_MASK are undefined, you can easily solve your problem by editing signal_i386.c and adding the following two lines at the beginning (next to the others define):

#define VIF_MASK 0x00080000
#define VIP_MASK 0x00100000

Put them, save it, and make again.
If your are lucky (like me, of course), you'll end with your hot wine ready to use.
That's all folks!

Tuesday, December 2, 2008

ASP.NET 2 with Mono, a success story

Sorry for the long delay from the last post, but I've been very busy doing my first commercial web site. Not so complicated, only a reserved portal where customers and agents can control
their situation by viewing invoices, orders and so on...
It took me two weeks to have that portal ready, but now I can say that I'm almost satisfied.

Three weeks ago, after a lot of study on many frameworks (including Zope and Plone of course), I've decided to take a look at ASP.NET. Mono 2.0 was released and now it supports ASP.NET 2, so ASP.NET 2 could be an open choice.
Unfortunately, the Mono website does not provide too much documentation so I've browsed the Microsoft's web pages where I've found a lot of videos with good tutorials on many aspects of this framework. The next step was: ok, it seems to be the right choice, now, what system I have to use to develop? What IDE?
MonoDevelop does not provide a visual designer for asp pages: bad point. Finally I've opted for a mixed solution using the free Microsoft IDE called Visual Web Developer 2005
(no, not the 2008 one, because it supports .NET 3.5, which isn't officially supported by Mono: first stages, first experience, my little brain fiend suggested me to reduce troubles with an older IDE ).
After viewing a dozen of video tutorials I've installed Visual Web Developer 2005 and opened many sample projects downloaded from the ASP.NET site.

Two weeks later I've finished my first web site using this technique:
  1. Develop on Windows with Visual Web Developer 2005.
  2. Test it immediately with Mono (installing the Mono framework on the same Windows machine).
  3. Deploy the whole thing on a Debian test server where I can see it in action with a real Apache web server environment.
Now I can confirm that this is a good solution that let you rapidly write good quality websites: ASP.NET is a well done framework and you can finish with a structured project that works in an open source environment.

Tuesday, October 7, 2008

Mounting a remote folder with ssh

Mounting a remote folder via ssh is not simple and clear as it should be, so I wrote down the main points to get work the whole thing.
- install sshfs ( with debian/ubuntu: apt-get install sshfs )
- add the user that will do the mount to the fuse group ( ex: 'fuse:x:106:simone' in /etc/group )
- create the folder where you want to mount (ex: mkdir /mnt/ssh_folder )
- remember to set correct permissions to the folder you've created
- mount the remote folder using sshfs ( ex: sshfs root@10.1.1.1:/var/opt/shared /mnt/ssh_folder -o allow_other )

In order to have a permanent connection you can use fstab:
- Add the ssh key of your user to the remote machine, in order to remove password request during the connection. The key must be generated with ssh-keygen and the id_rsa.pub file (normally located in the ~/.ssh/ folder) must be copied to the remote machine and added under the home dir of the remote user used for the connection: ~/.ssh/authorized_keys ( ex: 'cat id_rsa.pub >> /home/john/.ssh/authorized_keys' if you connect to the remote machine with john@machine )
- Add a new line to fstab, for example:
sshfs#root@10.1.1.1:/var/opt/shared /mnt/ssh_folder fuse defaults,allow_other 0 0

You can try fstab without restarting using 'mount -a'

That's all folks! ... maybe...

Thursday, September 18, 2008

Log ssh accesses

After that our mail server was hacked I've worked in order to increase security and controls of all our public servers.
One of the ideas that popped into my head was an alert that could notifies me every time someone successfully login into a public server. Searching the web I'd found a useful idea to put a line in bashrc that sends an email every time someone opens a session.
I've improved that line a bit and the result is a script that reads from an external file the patterns of addresses for which not to send emails (such as the IP of your desktop, for example).

The script is something like that:
#!/bin/sh
# Send login alert
#
# This script is useful to send an alert email when someone log in with
# the monitored user
# Remember to alias the root account to a valid mail box
#
# Thinked by Simone Tregnago (simonetregnago@grivaonline.com)

#this file provides the patterns of hosts for which you don't want to send messages
acl_patterns="./acl_patterns"
whologged=$(who -m | cut -d"(" -f2 | cut -d")" -f1)

if [ -z "$(echo $whologged | grep -f $acl_patterns )" ]; then
(echo "ALERT - Access to "$(hostname -f)" on:" $(date);echo;echo "who output:
";echo $(who -T)) | mail -s "Subject: Alert: access from "$whologged root
fi

The acl_patterns is the file that contains the patterns parsed by grep. If the pattern matches the script exits without the alert.

For example:
myadress.com
"^10.1"
will match for accesses made from myaddress.com or from ip under 10.1.x.x , so it doesn't sends alerts for this origins.

With those two files, following those simple steps, you can have emails alerts for every user:
- Put the script and the patterns files in a place accessible by the user that you want to monitor
- Set the script as executable
- Add a line in the .bashrc of the monitored user. This line will call the script, nothing else:
for example: ./sendalert.sh
- Be sure that emails sent to root are forwarded to a valid user

Enjoy

Monday, September 8, 2008

Bacula with USB disks, vchanger altered

Last month I've successfully implemented a new backup solution using Bacula. The old backup consist in a combination of Linux and Windows scripts; we had abandoned tapes two years ago, in favor of USB removable disks.
Now I've wanted to re implement everything using Bacula for all the backup process: starting from db dump scripts and similar, ending with the writing of the backup on the USB disks. I've also wanted Bacula to alert someone every day, in order to regularly rotate disks with daily frequency.
Bacula doesn't natively support a system to cycle USB disks, which is a lack when removable disks with big capacities are cheap and they're lowering their price every day. Anyway: it can use tapes autochangers, and someone started to write fake scripts that uses USB disks as tapes , simulating the autochanger.
It was difficult to find a script that works well: after a few study and research I've found one written by Josh Fisher in 2006. It seemed to me that was the best one so I'd started to implement it. Unfortunately I've found something wrong with this script, so I've altered 2 or 3 lines of code in order to make it work.
I've started to test it two weeks ago and it seems to works very well.
Below is the altered script, you can find other useful informations with the Joe's howto posted there.
Enjoy:

#!/bin/sh
#
# Bacula interface to virtual autochanger using removable disk drives
#
# Based (somewhat) on the "disk-changer" script from bacula 1.39.26
#
# Vchanger is a Bacula autochanger script that emulates a conventional
# magazine-based tape library device using removable disk drives.
# Partitions on the removable drives are used as virtual magazines,
# where each "magazine" contains the same number of virtual slots. Each
# "slot" holds one virtual tape, where a "tape" is a regular file that
# Bacula treats as a "Device Type = File" volume.
#
# This script will be invoked by Bacula using the Bacula Autochanger
# Interface and will be passed the following arguments:
#
# vchanger "changer-device" "command" "slot" "archive-device" "drive-index"
# $1 $2 $3 #4 #5
#
# See the Bacula documentation for Autochanger Interface details
#
# Copyright (C) 2006 Josh Fisher
#
# Modified by Simone Tregnago on 07-20-2008
#
# Permission to use, copy, modify, distribute, and sell this software
# and its documentation for any purpose is hereby granted without fee,
# provided that the above copyright notice appears in all copies. This
# software is provided "as is" without express or implied warranty.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#

#
# log whats done
#
dbgfile="/var/bacula/vchanger.log"
# to turn on logging, uncomment the following line
touch $dbgfile
#

#
# Write to a log file
# To log debugging info, create file /var/bacula/vchanger.log
# with write permission for bacula-sd user. To stop logging,
# delete file /var/bacula/vchanger.log
#
function debug()
{
if test -e $dbgfile; then
echo "`date +\"%Y%m%d-%H:%M:%S\"` $*" >> $dbgfile
fi
}

#
# Return length of string $1
#
function strlen ()
{
expr length $1
}

#
# Prepend zeros to $1 and return a string that is $2 characters long
#
function mklen ()
{
o1=$1
while [ `eval strlen ${o1}` -lt ${2} ]; do
o1="0${o1}"
done
echo $o1
}

#
# Initialize autochanger's state directory if not already initialized
#
function init_statedir() {
debug "Initializing $statedir"
# Create state dir if needed
if [ ! -d "${statedir}" ]; then
mkdir "${statedir}"
if [ $? -ne 0 ]; then
echo "Could not create ${statedir}"
exit 1
fi
fi
chmod 770 "${statedir}"
if [ $? -ne 0 ]; then
echo "Could not chmod ${statedir}"
exit 1
fi
# Create nextmag file to hold max magazine index used
if [ ! -f "${statedir}/nextmag" ]; then
echo 1 >"${statedir}/nextmag"
if [ $? -ne 0 ]; then
echo "Could not create ${statedir}/nextmag"
exit 1
fi
fi
chmod 660 "${statedir}/nextmag"
if [ $? -ne 0 ]; then
echo "Could not chmod ${statedir}/nextmag"
exit 1
fi
# Check nextmag value
nextmag=`cat "${statedir}/nextmag"`
if [ $? -ne 0 -o "${nextmag}" == "" -o $nextmag -lt 1 -o $nextmag -gt 99 ]; then
echo "${statedir}/nextmag has invalid value"
return 1
fi
# Create 'loaded' files for each virtual drive that hold the slot
# number currently loaded in that 'drive'
i=0
while [ $i -le $maxdrive ]; do
if [ ! -f "${statedir}/loaded${i}" ]; then
echo "0" 2>/dev/null >"${statedir}/loaded${i}"
if [ $? -ne 0 ]; then
echo "Could not create ${statedir}/loaded${i}"
exit 1
fi
chmod 660 "${statedir}/loaded${i}"
if [ $? -ne 0 ]; then
echo "Could not chmod ${statedir}/loaded${i}"
exit 1
fi
fi
i=`expr ${i} + 1`
done
}


#
# Initialize magazine if not already initialized
#
function init_magazine() {
debug "Initializing magazine"
# Get max magazine index that has been used
nextmag=`cat "${statedir}/nextmag"`
if [ $? -ne 0 -o "${nextmag}" == "" ]; then
echo "Failed to read ${statedir}/nextmag"
exit 1
fi
# Check magazine for existing index
if [ -f "${mountpoint}/index" ]; then
# retrieve existing magazine index
mi=`cat "${mountpoint}/index"`
if [ $? -ne 0 ]; then
echo "Failed to read ${mountpoint}/index"
exit 1
fi
# must be 1-99
if [ $mi -lt 1 -o $mi -gt 99 ]; then
echo "Magazine has invalid index ${mi}"
exit 1
fi
else
# new magazine, so assign it the next avail index
mi=`expr ${nextmag} + 1`
if [ $mi -lt 0 -o $mi -gt 99 ]; then
echo "Max magazines exceeded"
exit 1
fi
echo $mi 2>/dev/null >"${mountpoint}/index"
if [ $? -ne 0 ]; then
echo "Failed to write ${mountpoint}/index"
exit 1
fi
fi
# make sure max index used is up to date
if [ $mi -gt $nextmag ]; then
echo $mi 2>/dev/null >"${statedir}/nextmag"
if [ $? -ne 0 ]; then
echo "Failed to update ${statedir}/nextmag"
exit 1
fi
fi
# make magazine index 2 digits
magindex=`eval mklen ${mi} 2`
# setup slot files (ie. virtual tapes)
i=1
while [ $i -le $magslots ]; do
s=`eval mklen ${i} 3`
f="${mountpoint}/m${magindex}s${s}"
if [ ! -f "${f}" ]; then
touch "${f}" 2>/dev/null >/dev/null
if [ $? -ne 0 ]; then
echo "Failed to create ${f}"
exit 1
fi
fi
i=`expr ${i} + 1`
done
return 0
}


#
# check parameter count on commandline
#
function check_parm_count() {
pCount=$1
pCountNeed=$2
if test $pCount -lt $pCountNeed; then
echo "usage: vchanger ctl-device command [slot archive-device drive-index]"
echo " Insufficient number of arguments arguments given."
if test $pCount -lt 2; then
echo " Mimimum usage is first two arguments ..."
else
echo " Command expected $pCountNeed arguments"
fi
exit 1
fi
}


# Setup arguments
ctl=$1
cmd="$2"
slot=$3
device=$4
drive=$5

# Setup default config values
magslots=10
maxdrive=0
statedir="/var/bacula/vchanger"
mountpoint=

# Pull in conf file
if [ -f $ctl ]; then
. $ctl
else
echo "Config file ${ctl} not found"
exit 1
fi

# check for required config values
if [ "${mountpoint}" == "" ]; then
echo "Required variable 'mountpoint' not defined in ${ctl}"
exit 1
fi
if [ "${magslots}" == "" -o $magslots -lt 1 -o $magslots -gt 999 ]; then
echo "Ivalid value for 'magslots' in ${ctl}"
exit 1
fi
if [ "${maxdrive}" == "" -o $maxdrive -lt 0 -o $maxdrive -ge $magslots ]; then
echo "Invalid value for 'maxdrive' in ${ctl}"
exit 1
fi
if [ "${statedir}" == "" ]; then
echo "Invalid value for 'statedir' in ${ctl}"
exit 1
fi

# Initialize state directory for this autochanger
init_statedir

# Check for special cases where only 2 arguments are needed,
# all others are a minimum of 5
#
case $2 in
list)
check_parm_count $# 2
;;
slots)
check_parm_count $# 2
;;
*)
check_parm_count $# 5
if [ $drive -gt $maxdrive ]; then
echo "Drive ($drive) out of range (0-${maxdrive})"
exit 1
fi
if [ $slot -gt $magslots ]; then
echo "Slot ($slot) out of range (1-$magslots)"
exit 1
fi
;;
esac

debug "Parms: $ctl $cmd $slot $device $drive"

case $cmd in
unload)
debug "Doing vchanger -f $ctl unload $slot $device $drive"
ld=`cat "${statedir}/loaded${drive}"`
if [ $? -ne 0 ]; then
echo "Failed to read ${statedir}/loaded${drive}"
exit 1
fi
if [ $slot -eq $ld ]; then
echo "0" >"${statedir}/loaded${drive}"
if [ $? -ne 0 ]; then
echo "Failed to write ${statedir}/loaded${drive}"
exit 1
fi
unlink "${device}" 2>/dev/null >/dev/null
exit 0
fi
if [ $ld -eq 0 ]; then
echo "Drive ${drive} Is Empty"
else
echo "Storage Element ${slot} is Already Full"
fi
exit 1
;;

load)
debug "Doing vchanger $ctl load $slot $device $drive"
ld=`cat "${statedir}/loaded${drive}"`
if [ $? -ne 0 ]; then
echo "Failed to read ${statedir}/loaded${drive}"
exit 1
fi
if [ $ld -eq 0 ]; then
unlink "${device}" 2>/dev/null >/dev/null
# make sure slot is not loaded in another drive
i=0
while [ $i -le $maxdrive ]; do
if [ $i -ne $drive ]; then
ldi=`cat "${statedir}/loaded${i}"`
if [ $ldi -eq $slot ]; then
echo "Storage Element ${slot} Empty (loaded in drive ${i})"
exit 1
fi
fi
i=`expr ${i} + 1`
done
init_magazine
if [ $? -ne 0 ]; then
echo "Magazine Not Loaded"
exit 1
fi
s=`eval mklen ${slot} 3`
ln -s "${mountpoint}/m${magindex}s${s}" "${device}"
echo $slot >"${statedir}/loaded${drive}"
exit 0
else
echo "Drive ${drive} Full (Storage element ${ld} loaded)"
exit 1
fi
;;

list)
debug "Doing vchanger -f $ctl -- to list volumes"
init_magazine
if [ $? -ne 0 ]; then
echo "Magazine Not Loaded"
exit 1
fi
i=1
while [ $i -le $magslots ]; do
s=`eval mklen ${i} 3`
echo "${i}:m${magindex}s${s}"
i=`expr ${i} + 1`
done
exit 0
;;

loaded)
debug "Doing vchanger -f $ctl $drive -- to find what is loaded"
cat "${statedir}/loaded${drive}"
exit 0
;;

slots)
debug "Doing vchanger -f $ctl -- to get count of slots"
echo $magslots
exit 0
;;
esac
# eof


Here is the config of the 1st changer(vchanger1.conf):

# /etc/bacula/vchanger1.conf
baculasd="backup-sd"
baculasd_user=bacula
bconsole=/usr/bin/bconsole

magslots=1
maxdrive=0

mountpoint=/mnt/vchanger1/magazine
statedir=/var/bacula/vchanger1
# eof

P.S.: remember to take care of file permissions, since Bacula needs to read them from its user; so I suggest to chown the USB disks and vchanger folder with user 'bacula' and group 'tape' (or whatever you use to run the bacula-sd daemon ). For example, considering the folder used in the config above, you could run:
chown bacula:tape /mnt/vchanger1/magazine/ -R
and
chown bacula:tape /var/bacula/vchanger1/ -R

Update:
You can download script and configuration files using the following links:
vchanger
vchanger1.conf

Monday, August 4, 2008

Have you read the API docs? Really? Seriously?

This morning one of the JavaScript procedures that had run every night for a few months, clamorously failed.
Why? It worked like a charm until today!
Someone changed something? None.
Has data changed in someway? Only the month, august instead of july.
It took a long time before I was able to discover the mystery: I had used a JS API, a simple parseInt() function, without reading docs carefully: that leaded me to a bad bug.
If you use the javascript parseInt embedded function, you've to know that it takes two parameters: the first is the string to parse, the second is the radix. OK, if you reason like me you may think: if I don't provide the radix, which is an optional argument, the function implicitly convert using base 10, right? Then you make some try, maybe you will use a JS command line in order to try something like this:

parseInt("1")
1
parseInt("5")
5
parseInt("12")
12

Ok, at this point you may think that you've understood how to use it. After that you can make some tests, some debugging, as I do. Then your procedure could work well for months, as mine did.
Until now.
What have I discovered, after dozens of minutes of debugging? After identifying that the problems was in the parseInt function I've found that parseInt("08") returns 0, while parseInt("07") returns 7.
Without anymore thinking I googled the magic keywords and readed this FAQ's entry:

Why parseInt(08) & parseInt(09) is showing the value 0 ?

Arg! This article says that parseInt assumes the base from the content of the string. If the string contains a leading '0' character, then parseInt identify the value as octal and do the relative conversion! That means that, in octal, converting '07' returns 7, but converting '08' or '09' doesn't mean anything in octal, so the function returns 0.
Without considerations of what the Javascript's architects had smoked when they had designed the APIs, I've learned another big point: read the docs carefully, always! Even if it seems that the things works, take time to study carefully if you want to write solid software.
OK Javascript guys, I've learned the lesson! Now you can return to write less cryptic functions!

Thursday, July 31, 2008

Mail server... hacked!

Monday morning my colleagues have found the mail server broken. When I've arrived at the office they've had already restored the situation by restarting the server from the console. Something's broken the sockets of the networking of the OS. Strange... very strange for a Linux kernel.
I've started my inspections reading the logs, obviously. Reading and reading, getting back line after line, I've found the time when the sockets got broken: I was horrified reading that someone, at the same time, two minutes before, logged in via ssh with the zimbra user (a program's user, that normally don't have any password, in order to disable remote ssh logins ).
Deep inspection of access logs reported that someone has used this user to login remotely, for a month or two. That was very bad. The IP used for the login changed every time, but the class was the same: all the IP's was pulled from the pool of the Romtelecom company, a rumanian company.
That was a very stupid security flaw: someone of us putted a too simple password for the zimbra user during a test and after the tests he forgotten to remove it.
The result was that a simple scanner was able to discover the too short password.
The next question was: what the hell does the hacker with my mail server?
I've found the answer in the /var/tmp directory: the only one opened for writing to all users. Our bad guy used this folder as a place to store, download and compile various scanner/knocking softwares, that was done in order to use our email server as a base for scanning very large set of ip's classes.
Our bad guy doesn't have any other interest, so, in order to hide his presence, he hadn't touched anything else: in this way he could perform his tasks for days, weeks... until he made the bad step that broken the sockets.
He was ingenuous too, and he doesn't removed the history, he never cleared anything, so now I can reconstruct everything: what was installed, what was his activities, what sites he used to download software: everything. I don't have interests to continue my inspections, but If I would... especially helped with a claim presented to the italian's police... I could easily follow him, and maybe one or two of the groups behind that.
I will report some of the software he downloaded, useful if someone want to study a little:

http://l3iliboi.hub.io/nonmin.tar.gz
http://mafiot.clan.io/pass.txt
http://personales.ya.com/q1w2/nonmin.tgz
http://personales.ya.com/q1w2/nonmin.tgz
http://mafiot.clan.io/webmin.txt
http://www.transfer.ro/storage/staykuS-f953a.tar
http://www.transfer.ro/storage/VIP-c2a6a.tgz
http://nasa-undernet.ucoz.org/screen.tgz
http://Linux-Help.clan.su/download/2008.TGZ
http://w.wtf.la/xpl/26roott
http://www.transfer.ro/storage/FidoScan-3fcb0.tgz

If you want to know something more, such as user and passwords lists, classes of scanned ip,
or if you want the full programs, in order to study them: feel free to ask me.
Oh, I've also wrote to the romanian telecommunication company. Here's their reply:

Thanks for Contacting Romtelecom's Abuse Team . Our customer will be notified .

Marius Nastase
AbuseTeam
abuse@romtelecom.ro


Very pleased to see all this interest to stop eastern's cracking groups.