Section 5.3. Dissection and Discussion


5.3. Dissection and Discussion

The implementation of Samba BDCs necessitates the installation and configuration of LDAP. For this site, you use OpenLDAP, the open source software LDAP server platform. Commercial LDAP servers in current use with Samba-3 include:

  • Novell eDirectory[1] is being successfully used by some sites. Information on how to use eDirectory can be obtained from the Samba mailing lists or from Novell.

    [1] <http://www.novell.com/products/edirectory/>

  • IBM Tivoli Directory Server[2] can be used to provide the Samba LDAP backend. Example schema files are provided in the Samba source code tarball under the directory ~samba/example/LDAP.

    [2] <http://www-306.ibm.com/software/tivoli/products/directory-server/>

  • Sun ONE Identity Server product suite[3] provides an LDAP server that can be used for Samba. Example schema files are provided in the Samba source code tarball under the directory ~samba/example/LDAP.

    [3] <http://www.sun.com/software/software/products/identity_srvr/home_identity.xml>

A word of caution is fully in order. OpenLDAP is purely an LDAP server, and unlike commercial offerings, it requires that you manually edit the server configuration files and manually initialize the LDAP directory database. OpenLDAP itself has only command-line tools to help you to get OpenLDAP and Samba-3 running as required, albeit with some learning curve challenges.

For most sites, the deployment of Microsoft Active Directory from the shrink-wrapped installation is quite adequate. If you are migrating from Microsoft Active Directory, be warned that OpenLDAP does not include GUI-based directory management tools. Even a simple task such as adding users to the OpenLDAP database requires an understanding of what you are doing, why you are doing it, and the tools that you must use.

When installed and configured, an OpenLDAP Identity Management backend for Samba functions well. High availability operation may be obtained through directory replication/synchronization and master/slave server configurations. OpenLDAP is a mature platform to host the organizational directory infrastructure that can include all UNIX accounts, directories for electronic mail, and much more. The price paid through learning how to design an LDAP directory schema in implementation and configuration of management tools is well rewarded by performance and flexibility and the freedom to manage directory contents with greater ability to back up, restore, and modify the directory than is generally possible with Microsoft Active Directory.

A comparison of OpenLDAP with Microsoft Active Directory does not do justice to either. OpenLDAP is an LDAP directory tool-set. Microsoft Active Directory Server is an implementation of an LDAP server that is largely preconfigured for a specific task orientation. It comes with a set of administrative tools that is entirely customized for the purpose of running MS Windows applications that include file and print services, Microsoft Exchange server, Microsoft SQL server, and more. The complexity of OpenLDAP is highly valued by the UNIX administrator who wants to build a custom directory solution. Microsoft provides an application called MS ADAM[4] that provides more generic LDAP services, yet it does not have the vanilla-like services of OpenLDAP.

[4] <http://www.microsoft.com/windowsserver2003/adam/default.mspx>

You may wish to consider outsourcing the development of your OpenLDAP directory to an expert, particularly if you find the challenge of learning about LDAP directories, schemas, configuration, and management tools and the creation of shell and Perl scripts a bit challenging. OpenLDAP can be easily customized, though it includes many ready-to-use schemas. Samba-3 provides an OpenLDAP schema file that is required for use as a passdb backend.

For those who are willing to brave the process of installing and configuring LDAP and Samba-3 interoperability, there are a few nice Web-based tools that may help you to manage your users and groups more effectively. The Web-based tools you might like to consider include the LDAP Account Manager[5] (LAM) and the Webmin-based Webmin[6] Idealx CGI tools[7].

[5] <http://lam.sourceforge.net/>

[6] <http://www.webmin.com>

[7] <http://webmin.idealx.org/index.en.html>

Some additional LDAP tools should be mentioned. Every so often a Samba user reports using one of these, so it may be useful to them: GQ[8], a GTK-based LDAP browser; LDAP Browser/Editor[9] ; JXplorer[10] (by Computer Associates); and phpLDAPadmin[11].

[8] <http://biot.com/gq>

[9] <http://www.iit.edu/~gawojar/ldap/>

[10] <http://www.jxplorer.org/>

[11] <http://phpldapadmin.sourceforge.net/>

Note

The following prescriptive guidance is not an LDAP tutorial. The LDAP implementation expressly uses minimal security controls. No form of secure LDAP communications is attempted. The LDAP configuration information provided is considered to consist of the barest essentials only. You are strongly encouraged to learn more about LDAP before attempting to deploy it in a business-critical environment.



Information to help you get started with OpenLDAP is available from the OpenLDAP web site[12]. Many people have found the book LDAP System Administration,[13] by Jerry Carter quite useful.

[12] <http://www.openldap.org/pub/>

[13] <http://www.oreilly.com/catalog/ldapsa/index.html>

Mary's problems are due to two factors. First, the absence of a domain controller on the local network is the main cause of the errors that result in blue screen crashes. Second, Mary has a large profile that must be loaded over the WAN connection. The addition of BDCs on each network segment significantly improves overall network performance for most users, but it is not enough. You must gain control over user desktops, and this must be done in a way that wins their support and does not cause further loss of staff morale. The following procedures solve this problem.

There is also an opportunity to implement smart printing features. You add this to the Samba configuration so that future printer changes can be managed without need to change desktop configurations.

You add the ability to automatically download new printer drivers, even if they are not installed in the default desktop profile. Only one example of printing configuration is given. It is assumed that you can extrapolate the principles and use them to install all printers that may be needed.

5.3.1. Technical Issues

The solution provided is a minimal approach to getting OpenLDAP running as an identity management directory server for UNIX system accounts as well as for Samba. From the OpenLDAP perspective, UNIX system accounts are stored POSIX schema extensions.

Samba provides its own schema to permit storage of account attributes Samba needs. Samba-3 can use the LDAP backend to store:

  • Windows Networking User Accounts

  • Windows NT Group Accounts

  • Mapping Information between UNIX Groups and Windows NT Groups

  • ID Mappings for SIDs to UIDs (also for foreign Domain SIDs)

The use of LDAP with Samba-3 makes it necessary to store UNIX accounts as well as Windows Networking accounts in the LDAP backend. This implies the need to use the PADL LDAP tools[14]. The resolution of the UNIX group name to its GID must be enabled from either the /etc/group or from the LDAP backend. This requires the use of the PADL nss_ldap tool-set that integrates with the NSS. The same requirements exist for resolution of the UNIX username to the UID. The relationships are demonstrated in Figure 5.1.

[14] <http://www.padl.com/Contents/OpenSourceSoftware.html>

Figure 5.1. The Interaction of LDAP, UNIX Posix Accounts and Samba Accounts


You configure OpenLDAP so that it is operational. Before deploying the OpenLDAP, you really ought to learn how to configure secure communications over LDAP so that site security is not at risk. This is not covered in the following guidance.

When OpenLDAP has been made operative, you configure the PDC called MASSIVE. You initialize the Samba secrets.tdb file. Then you create the LDAP Interchange Format (LDIF) file from which the LDAP database can be initialized. You need to decide how best to create user and group accounts. A few hints are, of course, provided. You can also find on the enclosed CD-ROM, in the Chap06 directory, a few tools that help to manage user and group configuration.

In order to effect folder redirection and to add robustness to the implementation, create a network default profile. All network users workstations are configured to use the new profile. Roaming profiles will automatically be deleted from the workstation when the user logs off.

The profile is configured so that users cannot change the appearance of their desktop. This is known as a mandatory profile. You make certain that users are able to use their computers efficiently.

A network logon script is used to deliver flexible but consistent network drive connections.

5.3.1.1 Addition of Machines to the Domain

Samba versions prior to 3.0.11 necessitated the use of a domain administrator account that maps to the UNIX UID=0. The UNIX operating system permits only the root user to add user and group accounts. Samba 3.0.11 introduced a new facility known as Privileges, which provides five new privileges that can be assigned to users and/or groups; see Table 5.1.

Table 5.1. Current Privilege Capabilities

Privilege

Description

SeMachineAccountPrivilege

Add machines to domain

SePrintOperatorPrivilege

Manage printers

SeAddUsersPrivilege

Add users and groups to the domain

SeRemoteShutdownPrivilege

Force shutdown from a remote system

SeDiskOperatorPrivilege

Manage disk share


In this network example use is made of one of the supported privileges purely to demonstrate how any user can now be given the ability to add machines to the domain using a normal user account that has been given the appropriate privileges.

5.3.1.2 Roaming Profile Background

As XP roaming profiles grow, so does the amount of time it takes to log in and out.

An XP roaming profile consists of the HKEY_CURRENT_USER hive file NTUSER.DAT and a number of folders (My Documents, Application Data, Desktop, Start Menu, Templates, Net-Hood, Favorites, and so on). When a user logs onto the network with the default configuration of MS Windows NT/200x/XPP, all this data is copied to the local machine under the C:\Documents and Settings\%USERNAME% directory. While the user is logged in, any changes made to any of these folders or to the HKEY_CURRENT_USER branch of the registry are made to the local copy of the profile. At logout the profile data is copied back to the server. This behavior can be changed through appropriate registry changes and/or through changes to the default user profile. In the latter case, it updates the registry with the values that are set in the profile NTUSER.DAT file.

The first challenge is to reduce the amount of data that must be transferred to and from the profile server as roaming profiles are processed. This includes removing all the shortcuts in the Recent directory, making sure the cache used by the Web browser is not being dumped into the Application Data folder, removing the Java plug-ins cache (the .jpi_cache directory in the profile), as well as training the user to not place large files on the desktop and to use his or her mapped home directory instead of the My Documents folder for saving documents.

Using a folder other than My Documents is a nuisance for some users, since many applications use it by default.

The secret to rapid loading of roaming profiles is to prevent unnecessary data from being copied back and forth, without losing any functionality. This is not difficult; it can be done by making changes to the Local Group Policy on each client as well as changing some paths in each user's NTUSER.DAT hive.

Every user profile has its own NTUSER.DAT file. This means you need to edit every user's profile, unless a better method can be followed. Fortunately, with the right preparations, this is not difficult. It is possible to remove the NTUSER.DAT file from each user's profile. Then just create a Network Default Profile. Of course, it is necessary to copy all files from redirected folders to the network share to which they are redirected.

5.3.1.3 The Local Group Policy

Without an Active Directory PDC, you cannot take full advantage of Group Policy Objects. However, you can still make changes to the Local Group Policy by using the Group Policy editor (gpedit.msc).

The Exclude directories in roaming profile settings can be found under User Configuration Simply add the folders you do not wish to be copied back and forth to this semicolon-separated list. Note that this change must be made on all clients that are using roaming profiles.

5.3.1.4 Profile Changes

There are two changes that should be done to each user's profile. Move each of the directories that you have excluded from being copied back and forth out of the usual profile path. Modify each user's NTUSER.DAT file to point to the new paths that are shared over the network instead of to the default path (C:\Documents and Settings\%USERNAME%).

The above modifies existing user profiles. So that newly created profiles have these settings, you need to modify the NTUSER.DAT in the C:\Documents and Settings\Default User folder on each client machine, changing the same registry keys. You could do this by copying NTUSER.DAT to a Linux box and using regedt32. The basic method is described under Section 5.7.1.

5.3.1.5 Using a Network Default User Profile

If you are using Samba as your PDC, you should create a file share called NETLOGON and within that create a directory called Default User, which is a copy of the desired default user configuration (including a copy of NTUSER.DAT). If this share exists and the Default User folder exists, the first login from a new account pulls its configuration from it. See also the Real Men Don't Click[15] Web site.

[15] <http://isg.ee.ethz.ch/tools/realmen/det/skel.en.html>

5.3.1.6 Installation of Printer Driver Auto-Download

The subject of printing is quite topical. Printing problems run second place to name resolution issues today. So far in this book, you have experienced only what is generally known as "dumb" printing. Dumb printing is the arrangement by which all drivers are manually installed on each client and the printing subsystems perform no filtering or intelligent processing. Dumb printing is easily understood. It usually works without many problems, but it has its limitations also. Dumb printing is better known as Raw-Print-Through printing.

Samba permits the configuration of smart printing using the Microsoft Windows point-and-click (also called drag-and-drop) printing. What this provides is essentially the ability to print to any printer. If the local client does not yet have a driver installed, the driver is automatically downloaded from the Samba server and installed on the client. Drag-and-drop printing is neat; it means the user never needs to fuss with driver installation, and that is a Good Thing,TM isn't it?

There is a further layer of print job processing that is known as intelligent printing that automatically senses the file format of data submitted for printing and then invokes a suitable print filter to convert the incoming data stream into a format suited to the printer to which the job is dispatched.

The CUPS printing subsystem is capable of intelligent printing. It has the capacity to detect the data format and apply a print filter. This means that it is feasible to install on all Windows clients a single printer driver for use with all printers that are routed through CUPS. The most sensible driver to use is one for a PostScript printer. Fortunately, Easy Software Products[16], the authors of CUPS, have released a PostScript printing driver for Windows. It can be installed into the Samba printing backend so that it automatically downloads to the client when needed.

[16] <http://www.easysw.com>

This means that so long as there is a CUPS driver for the printer, all printing from Windows software can use PostScript, no matter what the actual printer language for the physical device is. It also means that the administrator can swap out a printer with a totally different type of device without ever needing to change a client workstation driver.

This book is about Samba-3, so you can confine the printing style to just the smart style of installation. Those interested in further information regarding intelligent printing should review documentation on the Easy Software Products Web site.

5.3.1.7 Avoiding Failures: Solving Problems Before They Happen

It has often been said that there are three types of people in the world: those who have sharp minds and those who forget things. Please do not ask what the third group is like! Well, it seems that many of us have company in the second group. There must be a good explanation why so many network administrators fail to solve apparently simple problems efficiently and effectively.

Here are some diagnostic guidelines that can be referred to when things go wrong:

Preliminary Advice: Dangers Can Be Avoided The best advice regarding how to mend a broken leg is "Never break a leg!"

Newcomers to Samba and LDAP seem to struggle a great deal at first. If you want advice regarding the best way to remedy LDAP and Samba problems: "Avoid them like the plague!"

If you are now asking yourself how problems can be avoided, the best advice is to start out your learning experience with a known-good configuration. After you have seen a fully working solution, a good way to learn is to make slow and progressive changes that cause things to break, then observe carefully how and why things ceased to work.

The examples in this chapter (also in the book as a whole) are known to work. That means that they could serve as the kick-off point for your journey through fields of knowledge. Use this resource carefully; we hope it serves you well.

Warning

Do not be lulled into thinking that you can easily adopt the examples in this book and adapt them without first working through the examples provided. A little thing overlooked can cause untold pain and may permanently tarnish your experience.



The Name Service Caching Daemon The name service caching daemon (nscd) is a primary cause of difficulties with name resolution, particularly where winbind is used. Winbind does its own caching, thus nscd causes double caching which can lead to peculiar problems during debugging. As a rule, it is a good idea to turn off the name service caching daemon.

Operation of the name service caching daemon is controlled by the /etc/nscd.conf file. Typical contents of this file are as follows:

# /etc/nscd.conf # An example Name Service Cache config file.   This file is needed by nscd. # Legal entries are: #       logfile                 <file> #       debug-level             <level> #       threads                 <threads to use> #       server-user             <user to run server as instead of root> #               server-user is ignored if nscd is started with -S parameters #       stat-user               <user who is allowed to request statistics> #       reload-count            unlimited|<number> # #       enable-cache            <service> <yes|no> #       positive-time-to-live   <service> <time in seconds> #       negative-time-to-live   <service> <time in seconds> #       suggested-size          <service> <prime number> #       check-files             <service> <yes|no> #       persistent              <service> <yes|no> #       shared                  <service> <yes|no> # Currently supported cache names (services): passwd, group, hosts #       logfile                 /var/log/nscd.log #       threads                 6 #       server-user             nobody #       stat-user               somebody         debug-level             0 #       reload-count            5         enable-cache            passwd          yes         positive-time-to-live   passwd          600         negative-time-to-live   passwd          20         suggested-size          passwd          211         check-files             passwd          yes         persistent              passwd          yes         shared                  passwd          yes         enable-cache            group           yes         positive-time-to-live   group           3600         negative-time-to-live   group           60         suggested-size          group           211         check-files             group           yes         persistent              group           yes         shared                  group           yes # !!!!!WARNING!!!!! Host cache is insecure!!! The mechanism in nscd to # cache hosts will cause your local system to not be able to trust # forward/reverse lookup checks. DO NOT USE THIS if your system relies on # this sort of security mechanism. Use a caching DNS server instead.         enable-cache            hosts           no         positive-time-to-live   hosts           3600         negative-time-to-live   hosts           20         suggested-size          hosts           211         check-files             hosts           yes         persistent              hosts           yes         shared                  hosts           yes 

It is feasible to comment out the passwd and group entries so they will not be cached. Alternatively, it is often simpler to just disable the nscd service by executing (on Novell SUSE Linux):

root#  chkconfig nscd off root#  rcnscd off 

Debugging LDAP In the example /etc/openldap/slapd.conf control file (see Example 5.4.1) there is an entry for loglevel 256. To enable logging via the syslog infrastructure, it is necessary to uncomment this parameter and restart slapd.

LDAP log information can be directed into a file that is separate from the normal system log files by changing the /etc/syslog.conf file so it has the following contents:

# Some foreign boot scripts require local7 # local0,local1.*                 -/var/log/localmessages local2,local3.*                 -/var/log/localmessages local5.*                        -/var/log/localmessages local6,local7.*                 -/var/log/localmessages local4.*                        -/var/log/ldaplogs 

In this case, all LDAP-related logs will be directed to the file /var/log/ldaplogs. This makes it easy to track LDAP errors. The snippet provides a simple example of usage that can be modified to suit local site needs. The configuration used later in this chapter reflects such customization with the intent that LDAP log files will be stored at a location that meets local site needs and wishes more fully.

Debugging NSS_LDAP The basic mechanism for diagnosing problems with the nss_ldap utility involves adding to the /etc/ldap.conf file the following parameters:

debug 256 logdir /data/logs 

Create the log directory as follows:

root# mkdir /data/logs 

The diagnostic process should follow these steps:

NSS_LDAP DIAGNOSTIC STEPS

1.

Verify the nss_base_passwd, nss_base_shadow, nss_base_group entries in the /etc/ldap.conf file and compare them closely with the directory tree location that was chosen when the directory was first created. One way this can be done is by executing:

root#  slapcat | grep Group | grep dn dn: ou=Groups,dc=abmas,dc=biz dn: cn=Domain Admins,ou=Groups,dc=abmas,dc=biz dn: cn=Domain Users,ou=Groups,dc=abmas,dc=biz dn: cn=Domain Guests,ou=Groups,dc=abmas,dc=biz dn: cn=Domain Computers,ou=Groups,dc=abmas,dc=biz dn: cn=Administrators,ou=Groups,dc=abmas,dc=biz dn: cn=Print Operators,ou=Groups,dc=abmas,dc=biz dn: cn=Backup Operators,ou=Groups,dc=abmas,dc=biz dn: cn=Replicators,ou=Groups,dc=abmas,dc=biz 

The first line is the DIT entry point for the container for POSIX groups. The correct entry for the /etc/ldap.conf for the nss_base_group parameter therefore is the distinguished name (dn) as applied here:

nss_base_group ou=Groups,dc=abmas,dc=biz?one 

The same process may be followed to determine the appropriate dn for user accounts. If the container for computer accounts is not the same as that for users (see the smb.conf file entry for ldap machine suffix), it may be necessary to set the following DIT dn in the /etc/ldap.conf file:

nss_base_passwd dc=abmas,dc=biz?sub 

This instructs LDAP to search for machine as well as user entries from the top of the DIT down. This is inefficient, but at least should work. Note: It is possible to specify multiple nss_base_passwd entries in the /etc/ldap.conf file; they will be evaluated sequentially. Let us consider an example of use where the following DIT has been implemented:

  • User accounts are stored under the DIT: ou=Users, dc=abmas, dc=biz

  • User login accounts are under the DIT: ou=People, ou-Users, dc=abmas, dc=biz

  • Computer accounts are under the DIT: ou=Computers, ou=Users, dc=abmas, dc=biz

The appropriate multiple entry for the nss_base_passwd directive in the /etc/ldap. conf file may be:

nss_base_passwd ou=People,ou=Users,dc=abmas,dc=org?one nss_base_passwd ou=Computers,ou=Users,dc=abmas,dc=org?one 

2.

Perform lookups such as:

root#  getent passwd 

Each such lookup will create an entry in the /data/log directory for each such process executed. The contents of each file created in this directory may provide a hint as to the cause of the a problem that is under investigation.

3.

For additional diagnostic information, check the contents of the /var/log/messages to see what error messages are being generated as a result of the LDAP lookups. Here is an example of a successful lookup:

slapd[12164]: conn=0 fd=10 ACCEPT from IP=127.0.0.1:33539 (IP=0.0.0.0:389) slapd[12164]: conn=0 op=0 BIND dn="" method=128 slapd[12164]: conn=0 op=0 RESULT tag=97 err=0 text= slapd[12164]: conn=0 op=1 SRCH base="" scope=0 deref=0 filter="(objectClass=*)" slapd[12164]: conn=0 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text= slapd[12164]: conn=0 op=2 UNBIND slapd[12164]: conn=0 fd=10 closed slapd[12164]: conn=1 fd=10 ACCEPT from IP=127.0.0.1:33540 (IP=0.0.0.0:389) slapd[12164]: conn=1 op=0 BIND dn="cn=Manager,dc=abmas,dc=biz" method=128 slapd[12164]: conn=1 op=0 BIND dn="cn=Manager,dc=abmas,dc=biz" mech=SIMPLE ssf=0 slapd[12164]: conn=1 op=0 RESULT tag=97 err=0 text= slapd[12164]: conn=1 op=1 SRCH base="ou=People,dc=abmas,dc=biz" scope=1 deref=0 filter="(objectClass=posixAccount)" slapd[12164]: conn=1 op=1 SRCH attr=uid userPassword uidNumber gidNumber cn homeDirectory loginShell gecos description objectClass slapd[12164]: conn=1 op=1 SEARCH RESULT tag=101 err=0 nentries=2 text= slapd[12164]: conn=1 fd=10 closed 

4.

Check that the bindpw entry in the /etc/ldap.conf or in the /etc/ldap.secrets file is correct, as specified in the /etc/openldap/slapd.conf file.

Debugging Samba The following parameters in the smb.conf file can be useful in tracking down Samba-related problems:

[global]    ...    log level = 5    log file = /var/log/samba/%m.log    max log size = 0    ... 

This will result in the creation of a separate log file for every client from which connections are made. The log file will be quite verbose and will grow continually. Do not forget to change these lines to the following when debugging has been completed:

[global]    ...    log level = 1    log file = /var/log/samba/%m.log    max log size = 50    ... 

The log file can be analyzed by executing:

root#  cd /var/log/samba root#  grep -v "^\[200" machine_name.log 

Search for hints of what may have failed by looking for the words fail and error.

Debugging on the Windows Client MS Windows 2000 Professional and Windows XP Professional clients can be configured to create a netlogon.log file that can be very helpful in diagnosing network logon problems. Search the Microsoft knowledge base for detailed instructions. The techniques vary a little with each version of MS Windows.

5.3.2. Political Issues

MS Windows network users are generally very sensitive to limits that may be imposed when confronted with locked-down workstation configurations. The challenge you face must be promoted as a choice between reliable, fast network operation and a constant flux of problems that result in user irritation.

5.3.3. Installation Checklist

You are starting a complex project. Even though you went through the installation of a complex network in Chapter 4, "The 500-User Office", this network is a bigger challenge because of the large number of complex applications that must be configured before the first few steps can be validated. Take stock of what you are about to undertake, prepare yourself, and frequently review the steps ahead while making at least a mental note of what has already been completed. The following task list may help you to keep track of the task items that are covered:

  • Samba-3 PDC Server Configuration

    1.

    DHCP and DNS servers

    2.

    OpenLDAP server

    3.

    PAM and NSS client tools

    4.

    Samba-3 PDC

    5.

    Idealx smbldap scripts

    6.

    LDAP initialization

    7.

    Create user and group accounts

    8.

    Printers

    9.

    Share point directory roots

    10.

    Profile directories

    11.

    Logon scripts

    12.

    Configuration of user rights and privileges

  • Samba-3 BDC Server Configuration

    1.

    DHCP and DNS servers

    2.

    PAM and NSS client tools

    3.

    Printers

    4.

    Share point directory roots

    5.

    Profiles directories

  • Windows XP Client Configuration

    1.

    Default profile folder redirection

    2.

    MS Outlook PST file relocation

    3.

    Delete roaming profile on logout

    4.

    Upload printer drivers to Samba servers

    5.

    Install software

    6.

    Creation of roll-out images



    Samba-3 by Example. Practical Exercises to Successful Deployment
    Samba-3 by Example: Practical Exercises to Successful Deployment (2nd Edition)
    ISBN: 013188221X
    EAN: 2147483647
    Year: 2005
    Pages: 142

    Similar book on Amazon

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net