Linux

Subversion, webdav, LDAP and folder restrictions

If you need to configure a svn server on Linux with LDAP authentication, webdav and insert specific directory restrictions you can follow these instructions.

  • One: you need to install subversion and apache in your Linux server (I will omit this part).
  • Two: you need to configure webdav to access svn over http and configure LDAP access.

    Make sure to have the following apache modules installed and configured:

    LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
    LoadModule dav_module modules/mod_dav.so
    LoadModule dav_svn_module modules/mod_dav_svn.so
    LoadModule authz_svn_module modules/mod_authz_svn.so
    LoadModule authn_alias_module modules/mod_authn_alias.so
    

    Assumptions:

    • I am usual to configure subversion in /srv/svn folder.
    • The users allowed to access the SVN have to belong to the LDAP group CN=SVN-AUTHORIZATION,OU=Groups GSO,DC=test,DC=example,DC=com

    Edit the /etc/apache2/mods-enabled/dav_svn.conf (this is valid for Ubuntu. Maybe in other distros this file is placed somewhere) and make sure to have the following information:

    <Location /svn/>
      # Enable svn over webdav
      DAV svn
      # Set parent path for multiple repositories
      SVNParentPath /srv/svn/
      # Set authentication type
      AuthType Basic
      # Set authentication name
      AuthName "FLR Subversion Repository"
      # Set authorization (permissions) file
      AuthzSVNAccessFile /etc/apache2/dav_svn.authz
      # Allow to list the parent path
      SVNListParentPath On
      # Use LDAP for authentication
      AuthBasicProvider ldap
      # LDAP server is authoritative (so is the final step for autentication)
      AuthzLDAPAuthoritative On
      # LDAP bind user
      AuthLDAPBindDN "CN=svnbind,OU=Users OS,DC=test,DC=example,DC=com"
      # LDAP bind password
      AuthLDAPBindPassword mypassword
      # LDAP URL
      AuthLDAPUrl "ldap://ldap_ip_address:389/DC=test,DC=example,DC=com?sAMAccountName?sub?(&(&(objectClass=user)(objectCategory=person))(memberof=CN=SVN-AUTHORIZATION,OU=Groups GSO,DC=test,DC=example,DC=com))"
    
      # A valid user is required
      Require valid-user
    </Location>
  • Three Create the permission file /etc/apache2/dav_svn.authz
    It will have the following content based on your needings:

    [groups]
    admin = matteo
    group1 = user1, user2, user3
    group2 = user2
    group3 = user4
    
    ###################################
    [/]
    * = r
    @admin = rw
    ###################################
    [repository1:/]
    * = rw
    ###################################
    [repository2:/]
    * =
    @admin = rw
    @group1 = rw
    ###################################
    [repository3:/]
    * =
    @admin = rw
    @group2 = rw
    @group1 = r
    ###################################
    [repository4:/]
    * = r
    @admin = rw
    [repository4:/trunk/sources]
    * = r
    @admin = rw
    @group3 = rw
    ###################################

    Now restart apache with /etc/init.d/apache2 restart

  • Four: create repositories.

    As root issue the following commands:

    cd /srv/svn
    svnadmin create repository1
    chown www-data.www-data -R repository1
    svnadmin create repository2
    chown www-data.www-data -R repository2
    svnadmin create repository3
    chown www-data.www-data -R repository3
    svnadmin create repository4
    chown www-data.www-data -R repository4

    You are now ready to use your new subversion repository with LDAP account, webdav access and custom user/group directory restrictions.

PHP Fatal error with PhpMyAdmin and APC

If you are running PhpMyAdmin and APC can happens that you get some errors like these:
PHP Fatal error: Call to undefined function PMA_log_user() in /usr/share/webapps/phpMyAdmin/libraries/common.inc.php on line 914
PHP Fatal error: Call to undefined function PMA_select_language() in /usr/share/webapps/phpMyAdmin/libraries/auth/cookie.auth.lib.php on line 220
PHP Fatal error: Call to undefined function pma_generate_common_url() in /usr/share/webapps/phpMyAdmin/libraries/header_meta_style.inc.php on line 48
PHP Fatal error: Call to undefined function PMA_DBI_connect() in /srv/http/librolandia.it/test/phpmyadmin/libraries/common.inc.php on line 916
PHP Fatal error: Class 'PMA_Error_Handler' not found in /path/to/phpMyAdmin/libraries/common.inc.php on line 58
PHP Fatal error: Call to undefined function PMA_getenv() in /path/to/phpMyAdmin/libraries/common.inc.php on line 143

If this is the case, you need to make some little changes to disable apc in your phpmyadmin virtual host:

Alias /phpmyadmin "/usr/share/webapps/phpMyAdmin"
<Directory "/usr/share/webapps/phpMyAdmin">
      AllowOverride All
      Options FollowSymlinks
      Order allow,deny
      Allow from all
      php_admin_value open_basedir "/srv/:/tmp/:/usr/share/webapps/:/etc/webapps:/usr/share/pear/"
      php_admin_value apc.enabled 0
</Directory>

And also modify your apc filter in php.ini under the APC section:

[APC]
apc.filter="-/usr/share/webapps/phpMyAdmin/.*"

Now restart Apache and you phpMyAdmin should work regularly.

Extract u-boot multi-file image in Python

This simple piece of code shows how to extract/decompress a u-boot multi-file image created with mkimage using Python. The image format is pretty simple:

64 bytes of image header.
4 bytes for the size of first image.
4 bytes for the size of second image.
...
4 bytes of zeros for termination.
image1.
image2.
...

You need to remember also that each image is padded to 4 bytes.

#!/usr/bin/env python

import sys

def toNumber(buf):
        """ Convert string in number """
        size = 0
        for b in buf: size=size*256+ord(b)
        return size

if len(sys.argv)!=2:
        sys.stdout.write('Usage %s uboot_image_file\n' % sys.argv[0])
        sys.exit(1)

f = open(sys.argv[1],'rb')
f.seek(64) # skip image header

parts = []

# get file size of all images
while True:
        buf = f.read(4)
        size = toNumber(buf)
        if size==0: break
        parts.append(size)

i = 0
for size in parts:
        pattern = 'file%d.img' % i
        p = open(pattern, 'wb')
        buf = f.read(size)
        p.write(buf)
        p.close()
        if size%4 != 0:
                # 4 byte padding
                f.read(4-(size%4))
        i+=1

f.close()

PySide Signals and Slots with QThread example

In these days I started studying PySide. After some days spent in reading lot of stuff, I thought that a real example could be useful for who intends to start learning PySide as well. In this example I can show you how you can implement a custom signal (MySignal) together with the usage of threads with QThread.

The following code creates a window with two button: the first starts and stop a thread (MyThread) that runs a batch that prints a point in the stdout every seconds continuously. The second button lets you only start another thread (MyLongThread) that prints an asterisk in the stdout every second for 10 seconds.

This example uses the api version 2 (introduced with PyQt 4.5) to connect signals to slots.

#!/usr/bin/env python2

import sys, time
from PySide.QtGui import *
from PySide.QtCore import *

class MySignal(QObject):
	sig = Signal(str)

class MyLongThread(QThread):
	def __init__(self, parent = None):
		QThread.__init__(self, parent)
		self.exiting = False
		self.signal = MySignal()

	def run(self):
		end = time.time()+10
		while self.exiting==False:
			sys.stdout.write('*')
			sys.stdout.flush()
			time.sleep(1)
			now = time.time()
			if now>=end:
				self.exiting=True
		self.signal.sig.emit('OK')

class MyThread(QThread):
	def __init__(self, parent = None):
		QThread.__init__(self, parent)
		self.exiting = False

	def run(self):
		while self.exiting==False:
			sys.stdout.write('.')
			sys.stdout.flush()
			time.sleep(1)

class MainWindow(QMainWindow):
	def __init__(self, parent=None):
		QMainWindow.__init__(self,parent)
		self.centralwidget = QWidget(self)
		self.batchbutton = QPushButton('Start batch',self)
		self.longbutton = QPushButton('Start long (10 seconds) operation',self)
		self.label1 = QLabel('Continuos batch')
		self.label2 = QLabel('Long batch')
		self.vbox = QVBoxLayout()
		self.vbox.addWidget(self.batchbutton)
		self.vbox.addWidget(self.longbutton)
		self.vbox.addWidget(self.label1)
		self.vbox.addWidget(self.label2)
		self.setCentralWidget(self.centralwidget)
		self.centralwidget.setLayout(self.vbox)
		self.thread = MyThread()
		self.longthread = MyLongThread()
		self.batchbutton.clicked.connect(self.handletoggle)
		self.longbutton.clicked.connect(self.longoperation)
		self.thread.started.connect(self.started)
		self.thread.finished.connect(self.finished)
		self.thread.terminated.connect(self.terminated)
		self.longthread.signal.sig.connect(self.longoperationcomplete)

	def started(self):
		self.label1.setText('Continuous batch started')

	def finished(self):
		self.label1.setText('Continuous batch stopped')

	def terminated(self):
		self.label1.setText('Continuous batch terminated')

	def handletoggle(self):
		if self.thread.isRunning():
			self.thread.exiting=True
			self.batchbutton.setEnabled(False)
			while self.thread.isRunning():
				time.sleep(0.01)
				continue
			self.batchbutton.setText('Start batch')
			self.batchbutton.setEnabled(True)
		else:
			self.thread.exiting=False
			self.thread.start()
			self.batchbutton.setEnabled(False)
			while not self.thread.isRunning():
				time.sleep(0.01)
				continue
			self.batchbutton.setText('Stop batch')
			self.batchbutton.setEnabled(True)

	def longoperation(self):
		if not self.longthread.isRunning():
			self.longthread.exiting=False
			self.longthread.start()
			self.label2.setText('Long operation started')
			self.longbutton.setEnabled(False)

	def longoperationcomplete(self,data):
		self.label2.setText('Long operation completed with: '+data)
		self.longbutton.setEnabled(True)

if __name__=='__main__':
	app = QApplication(sys.argv)
	window = MainWindow()
	window.show()
	sys.exit(app.exec_())

For more information you can look at:
QThread documentation: http://doc.qt.nokia.com/latest/qthread.html
PySide signals and slots: http://developer.qt.nokia.com/wiki/Signals_and_Slots_in_PySide
PyQt api 2 on PySide: http://www.pyside.org/docs/pseps/psep-0101.html

Export MySQL table in CSV format using PHP

The following PHP code is intendend to be used to export a MySQL table in CSV format in order to be used with MS Excel.

$link = mysql_connect($mysql_host,$mysql_user,$mysql_pass) or die('Could not connect: '.mysql_error());
mysql_select_db($mysql_db,$link) or die('Could not select database: '.$mysql_db);

$query = "SELECT * FROM $tablename ORDER BY id";
$result = mysql_query($query) or die("Error executing query: ".mysql_error());
$row = mysql_fetch_assoc($result);
$line = "";
$comma = "";
foreach($row as $name => $value)
{
	$line .= $comma . '"' . str_replace('"', '""', $name) . '"';
	$comma = ";";
}
$line .= "\n";
$out = $line;

mysql_data_seek($result, 0);

while($row = mysql_fetch_assoc($result))
{
	$line = "";
	$comma = "";
	foreach($row as $value)
	{
		$line .= $comma . '"' . str_replace('"', '""', $value) . '"';
		$comma = ";";
	}
	$line .= "\n";
	$out.=$line;
}
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=listino.csv");
echo $out;
exit;

How to execute commands with specific user privilege in C and Python under Linux

If you have root access but you need to run some applications/scripts with some other user credentials you can do it with

su - username -c "command to execute"

But if you need to do it within a C/C++ program you need to follow this procedure:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

int main(int argc, char* argv[])
{
        if(argc != 3)
        {
                printf("Usage: %s [USERNAME] [COMMAND]\n",argv[0]);
                return 1;
        }
        char *env[16];
        char envc[16][64];
        struct passwd *pw = getpwnam(argv[1]);
        if(pw==NULL)
        {
                printf("User %s does not exists!\n",argv[1]);
                return 1;
        }

        sprintf(env[0]=envc[0],"TERM=xterm");
        sprintf(env[1]=envc[1],"USER=%s",pw->pw_name);
        sprintf(env[2]=envc[2],"HOME=%s",pw->pw_dir);
        sprintf(env[3]=envc[3],"SHELL=%s",pw->pw_shell);
        sprintf(env[4]=envc[4],"LOGNAME=%s",pw->pw_name);
        sprintf(env[5]=envc[5],"PATH=/usr/bin:/bin:/opt/bin");
        env[6]=0;

        initgroups(argv[1],pw->pw_gid);
        setgid(pw->pw_gid);
        setuid(pw->pw_uid);
        execve(argv[2],NULL,env);

        return 0;
}

This is how to compile and execute the above code:

[root@barracuda ~]# gcc mysu.c -o mysu
[root@barracuda ~]# id
uid=0(root) gid=0(root) gruppi=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),19(log)
[root@barracuda ~]# ./mysu matteo /bin/bash
[matteo@barracuda /root]$ id
uid=1000(matteo) gid=100(users) groups=100(users),3(sys),10(wheel),14(uucp),91(video),92(audio),93(optical),95(storage),96(scanner),97(camera),98(power),108(vboxusers)

The same result could be obtained also in Python with a very little effort:

#!/usr/bin/env python

import sys,pwd,os

pw = pwd.getpwnam(sys.argv[1])
os.initgroups(sys.argv[1],pw.pw_gid)
env={"TERM":"xterm","USER":pw.pw_name,"HOME":pw.pw_dir,"SHELL":pw.pw_shell,"LOGNAME":pw.pw_name,"PATH":"/usr/bin:/bin:/opt/bin"};
os.setgid(pw.pw_gid);
os.setuid(pw.pw_uid);
os.execve(sys.argv[2],[],env);

Shrink Ext4 partition on VMware player

Few days ago I spent some times to try to shrink my Ubuntu 11.04 appliance with root partition formatted with EXT4 filesystem.

The main problem is that the current VMware tools (8.4.6, build-385536) does not support the ext4 shrink. If you run sudo vmware-toolbox, your root partition is formatted in ext4 and you try to execute the shrink an error message like the following could appear.

Anyway there is a trick to streamline the final vmdk size.
Run this command within a shell into the guest system:

sudo dd if=/dev/zero of=/zero.raw bs=20480
rm -f /zero.raw

Then, shutdown the virtual image and download the vdiskmanager tool from VMware website.
Now run the vmware-vdiskmanager with the -k parameter:

vmware-diskmanager -k /path/to/image.vmdk

This operation will take a while, but at the end you will get a considerable smaller vmdk image file.

How to implement MAC_X919 algorithm in Python

Today with my friend Nicola, we were looking in internet for the implementation of the X9.19 algorithm in python. Unfortunately we didn’t find it anywhere so we made it ourself:

#!/usr/bin/env python2

import Crypto.Cipher.DES as des
import Crypto.Cipher.DES3 as des3

def mac_x919(key,data):
  while len(data) % 8 != 0:
    data += '\x00'
  des_key1 = des.new(key[0:8],des.MODE_CBC)
  des_key2 = des.new(key[0:8],des.MODE_ECB)
  buf = des_key1.encrypt(data)
  buf = buf[len(buf)-8:]
  buf = des_key2.decrypt(buf)
  des3_key = des3.new(key,des3.MODE_ECB)
  buf = des3_key.encrypt(buf)
  return buf[0:4]

mac = mac_x919('0123456789abcdef','test data to be encrypted')

How to clone MySQL database schema in PHP

For my client I needed to create a PHP script that can export a full MySQL database schema in another database. This script also need to keep and set constraints.

You only need to configure $DB_SRC_* and $DB_DST_* variables to fit your environment.

Here below you can find the code I created for this purpose:

<?php

/********************* START CONFIGURATION *********************/
$DB_SRC_HOST='localhost';
$DB_SRC_USER='root';
$DB_SRC_PASS='password';
$DB_SRC_NAME='database1';

$DB_DST_HOST='localhost';
$DB_DST_USER='root';
$DB_DST__PASS='password';
$DB_DST_NAME='database2';

/*********************** GRAB OLD SCHEMA ***********************/
$db1 = mysql_connect($DB_SRC_HOST,$DB_SRC_USER,$DB_SRC_PASS) or die(mysql_error());
mysql_select_db($DB_SRC_NAME, $db1) or die(mysql_error());

$result = mysql_query("SHOW TABLES;",$db1) or die(mysql_error());
$buf="set foreign_key_checks = 0;\n";
$constraints='';
while($row = mysql_fetch_array($result))
{
        $result2 = mysql_query("SHOW CREATE TABLE ".$row[0].";",$db1) or die(mysql_error());
        $res = mysql_fetch_array($result2);
        if(preg_match("/[ ]*CONSTRAINT[ ]+.*\n/",$res[1],$matches))
        {
                $res[1] = preg_replace("/,\n[ ]*CONSTRAINT[ ]+.*\n/","\n",$res[1]);
                $constraints.="ALTER TABLE ".$row[0]." ADD ".trim($matches[0]).";\n";
        }
        $buf.=$res[1].";\n";
}
$buf.=$constraints;
$buf.="set foreign_key_checks = 1";

/**************** CREATE NEW DB WITH OLD SCHEMA ****************/
$db2 = mysql_connect($DB_DST_HOST,$DB_DST_USER,$DB_DST_PASS) or die(mysql_error());
$sql = 'CREATE DATABASE '.$DB_DST_NAME;
if(!mysql_query($sql, $db2)) die(mysql_error());
mysql_select_db($DB_DST_NAME, $db2) or die(mysql_error());
$queries = explode(';',$buf);
foreach($queries as $query)
        if(!mysql_query($query, $db2)) die(mysql_error());
?>

Virtual users on vsftpd

I’m usually to configure vsftp on web servers to allow FTP access based on domains. Few days ago my client asked me to create multiple FTP users for a single domain every one with a different root folder into that domain.

This is my usual configuration of my /etc/vsftpd.conf

listen=YES
anonymous_enable=NO
local_enable=YES
virtual_use_local_privs=YES
write_enable=YES
connect_from_port_20=YES
xferlog_enable=YES
pam_service_name=vsftpd
guest_enable=YES
guest_username=www-data
user_sub_token=$USER
local_root=/var/www/$USER
chroot_local_user=YES
hide_ids=YES
force_dot_files=YES
ftpd_banner=Welcome to my private FTP service.
local_umask=022

and this is my /etc/pam.d/vsftpd

auth required pam_pwdfile.so pwdfile /etc/ftpd.passwd
account required pam_permit.so

The first time I’ve created the file /etc/ftpd.passwd in this way:

htpasswd -c -d -b /etc/ftpd.passwd domain1.com <password>

For the future users simply avoid the ‘-c’ parameter:

htpasswd -d -b /etc/ftpd.passwd domain2.com <password>

With this simple configuration all users have these credentials:

  • host: domain1.com
  • username: domain1.com
  • password: password
  • port: 21
  • Root folder: /var/www/domain1/

Now the point is: how can we create multiple users for a single domain each one with a different root folder?
The answer is pretty simple, follow me!

Create the folder /var/www/users and add the following line at the end of /etc/vsftpd.conf

user_config_dir=/var/www/users

Into the folder /var/www/users create a file for each virtual user (for example the user user1.domain1.com) containing a line the root directory for that user:

echo "local_root=/var/www/domain1.com/pub/user1" > /var/www/users/user1.domain1.com

Now add the new user/password in /etc/ftpd.passwd as usual:

htpasswd -d -b /etc/ftpd.passwd user1.domain1.com <password>

Restart vsftpd server and test your new configuration!