diff options
author | cvs2svn Import User <samba-bugs@samba.org> | 2001-10-10 17:04:24 +0000 |
---|---|---|
committer | cvs2svn Import User <samba-bugs@samba.org> | 2001-10-10 17:04:24 +0000 |
commit | 8d4870bccd026d059c160de3642bb427338256ec (patch) | |
tree | 0e96f4ea88ad27caef33451c52f7d1c0c43b9f6a | |
parent | 155c3073b178a9bcfee942807c4a98f8b1a1bfde (diff) | |
parent | 96c9df577bcffeec1b7d516a5431e54e679bd6b4 (diff) | |
download | samba-8d4870bccd026d059c160de3642bb427338256ec.tar.gz |
This commit was manufactured by cvs2svn to create branch
'SAMBA_2_2_RELEASE'.
-rw-r--r-- | docs/textdocs/Samba-OpenSSL.txt | 405 | ||||
-rw-r--r-- | source/include/doserr.h | 173 | ||||
-rw-r--r-- | source/nsswitch/pam_winbind.h | 81 | ||||
-rw-r--r-- | source/nsswitch/winbindd_glue.c | 442 | ||||
-rw-r--r-- | source/nsswitch/winbindd_misc.c | 174 | ||||
-rw-r--r-- | source/passdb/machine_sid.c | 255 | ||||
-rw-r--r-- | source/passdb/pdb_ldap.c | 1033 | ||||
-rw-r--r-- | source/passdb/pdb_nisplus.c | 1404 | ||||
-rw-r--r-- | source/passdb/pdb_smbpasswd.c | 1520 | ||||
-rw-r--r-- | source/passdb/pdb_tdb.c | 830 | ||||
-rw-r--r-- | source/rpc_client/cli_trust.c | 247 | ||||
-rw-r--r-- | source/utils/pdbedit.c | 709 |
12 files changed, 7273 insertions, 0 deletions
diff --git a/docs/textdocs/Samba-OpenSSL.txt b/docs/textdocs/Samba-OpenSSL.txt new file mode 100644 index 00000000000..e1b54b1a032 --- /dev/null +++ b/docs/textdocs/Samba-OpenSSL.txt @@ -0,0 +1,405 @@ +Contributor: Christian Starkjohann <cs@obdev.at> +Date: May 29, 1998 +Status: + +Comment: Updated by Lutz Jaenicke <Lutz.Jaenicke@aet.TU-Cottbus.DE> +Date: July 16, 2001 + +Subject: Compiling and using samba with SSL support +============================================================================ + +What is SSL and SSLeay/OpenSSL? +=============================== +SSL (Secure Socket Layer) is a protocol for encrypted and authenticated data +transport. It is used by secure web servers for shopping malls, telebanking +and things like that. + +SSLeay is a free implementation of the SSL protocol. The successor of it is +OpenSSL, available from + + http://www.openssl.org/ + +The current version while these lines are written is 0.9.6b. In some countries +encryption is plagued by legal problems, even though things have relaxed a +lot in the last years. + +To compile samba with SSL support, you must first compile and install OpenSSL. +At least version 0.9.5 of OpenSSL is required. Version 0.9.6b is the latest +version and is strongly recommended. +OpenSSL consists of a library (which can be linked to other applications like +samba) and several utility programs needed for key generation, certification +etc. OpenSSL installs to /usr/local/ssl/ by default. + + +Compiling samba with OpenSSL +============================ +1. Get and install OpenSSL. The rest of this documentation assumes that you + have installed it at the default location, which is /usr/local/ssl/. +2. Call "configure" with the "--with-ssl" flag. If OpenSSL is not installed in + the default directory, you can use the "--with-sslinc" and "--with-ssllib" + flags to specify the location. +3. Compile and install as usual. + + +Configuring SSL in samba +======================== +Before you configure SSL, you should know the basics of cryptography and how +SSL relates to all of this. A basic introduction can be found further down in +this document. The following variables in the "[global]" section of the +configuration file are used to configure SSL: + +ssl = yes + This variable enables or disables the entire SSL mode. If it is set to + "no", the SSL enabled samba behaves exactly like the non-SSL samba. If set + to "yes", it depends on the variables "ssl hosts" and "ssl hosts resign" + whether an SSL connection will be required. +ssl hosts = +ssl hosts resign = 192.168. + These two variables define whether samba will go into SSL mode or not. If + none of them is defined, samba will allow only SSL connections. If the + "ssl hosts" variable lists hosts (by IP-address, IP-address range, net + group or name), only these hosts will be forced into SSL mode. If the + "ssl hosts resign" variable lists hosts, only these hosts will NOT be + forced into SSL mode. The syntax for these two variables is the same as + for the "hosts allow" and "hosts deny" pair of variables, only that the + subject of the decision is different: It's not the access right but + whether SSL is used or not. See the man page of smb.conf (section about + "allow hosts") for details. The above example requires SSL connections + from all hosts outside the local net (which is 192.168.*.*). +ssl CA certDir = /usr/local/ssl/certs + This variable defines where to look up the Certification Autorities. The + given directory should contain one file for each CA that samba will trust. + The file name must be the hash value over the "Distinguished Name" of the + CA. How this directory is set up is explained later in this document. All + files within the directory that don't fit into this naming scheme are + ignored. You don't need this variable if you don't verify client + certificates. +ssl CA certFile = /usr/local/ssl/certs/trustedCAs.pem + This variable is a second way to define the trusted CAs. The certificates + of the trusted CAs are collected in one big file and this variable points + to the file. You will probably only use one of the two ways to define your + CAs. The first choice is preferable if you have many CAs or want to be + flexible, the second is perferable if you only have one CA and want to + keep things simple (you won't need to create the hashed file names). You + don't need this variable if you don't verify client certificates. +ssl server cert = /usr/local/ssl/certs/samba.pem + This is the file containing the server's certificate. The server _must_ + have a certificate. The file may also contain the server's private key. + See later for how certificates and private keys are created. +ssl server key = /usr/local/ssl/private/samba.pem + This file contains the private key of the server. If this variable is not + defined, the key is looked up in the certificate file (it may be appended + to the certificate). The server _must_ have a private key and the + certificate _must_ match this private key. +ssl client cert = /usr/local/ssl/certs/smbclient.pem + The certificate in this file is used by smbclient if it exists. It's needed + if the server requires a client certificate. +ssl client key = /usr/local/ssl/private/smbclient.pem + This is the private key for smbclient. It's only needed if the client + should have a certificate. +ssl require clientcert = yes + If this variable is set to "yes", the server will not tolerate connections + from clients that don't have a valid certificate. The directory/file + given in "ssl CA certDir" and "ssl CA certFile" will be used to look up + the CAs that issued the client's certificate. If the certificate can't be + verified positively, the connection will be terminated. + If this variable is set to "no", clients don't need certificates. Contrary + to web applications you really _should_ require client certificates. In + the web environment the client's data is sensitive (credit card numbers) + and the server must prove to be trustworthy. In a file server environment + the server's data will be sensitive and the clients must prove to be + trustworthy. +ssl require servercert = yes + If this variable is set to "yes", the smbclient will request a certificate + from the server. Same as "ssl require clientcert" for the server. +ssl ciphers = ??? + This variable defines the ciphers that should be offered during SSL + negotiation. You should not set this variable unless you know what you do. +ssl version = ssl2or3 + This enumeration variable defines the versions of the SSL protocol that + will be used. "ssl2or3" allows dynamic negotiation of SSL v2 or v3, "ssl2" + results SSL v2, "ssl3" results in SSL v3 and "tls1" results in TLS v1. TLS + (Transport Layer Security) is the (proposed?) new standard for SSL. The + default value is "ssl2or3". +ssl compatibility = no + This variable defines whether SSLeay should be configured for bug + compatibility with other SSL implementations. This is probably not + desirable because currently no clients with SSL implementations other than + SSLeay exist. +ssl entropy file = + Specifies a file from which processes will read "random bytes" on startup. + In order to seed the internal pseudo random number generator, entropy + must be provided. On system with a /dev/urandom device file, the processes + will retrieve its entropy from the kernel. On systems without kernel + entropy support, a file can be supplied that will be read on startup + and that will be used to seed the PRNG. +ssl entropy bytes = 256 + Number of bytes that will be read from entropy file. If -1 is given, the + complete file will be read. +ssl egd socket = + Location of the communiation socket of an EGD or PRNGD daemon, from which + entropy can be retrieved. This option can be used instead of or together + with the "ssl entropy file" directive. 255bytes of entropy will be + retrieved from the daemon. + + +Running samba with OpenSSL +========================== +Samba is started as usual. The daemon will ask for the private key's pass +phrase before it goes to background if the private key has been encrypted. +If you start smbd from inetd, this won't work. Therefore you must not encrypt +your private key if you run smbd from inetd. + +Windows clients will try to connect to the SSL enabled samba daemon and they +will fail. This can fill your log with failed SSL negotiation messages. To +avoid this, you can either not run nmbd (if all clients use DNS to look up +the server), which will leave the Windows machine unaware of the server, or +list all (local) Windows machines in the "ssl hosts resign" variable. + + +About certificates +================== +Secure samba servers will not be set up for public use as it is the case with +secure web servers. Most installations will probably use it for distributed +offices that use parts of the internet for their intranet, for access to a +web server that's physically hosted by the provider or simply for teleworking. +All these applications work with a known group of users that can easily agree +on a certification authority. The CA can be operated by the company and the +policy for issuing certificates can be determined by the company. If samba is +configured to verify client certificates, it (currently) only verifies +whether a valid certificate exists. It does not verify any of the data within +the certificate (although it prints some of the data to the log file). + + +Which clients are available that support SSL? +============================================= +Currently there are only smbclient which is part of the samba package and +Sharity. Shariy versions newer than 0.14 in the beta branch and 1.01 in the +main branch can be compiled with SSLeay. Sharity is a CIFS/SMB client +implementation for Unix. It is a commercial product, but it is available in +source code and the demo-mode allows access to the first three layers of the +mounted directory hierarchy. Licenses for universities and students are free. +Sharity is available at + + http://www.obdev.at/Products/Sharity.html + + + +########################################################################### +Basics about Cryptography and SSL(eay) +########################################################################### + +There are many good introductions to cryptography. I assume that the reader +is familiar with the words "encryption", "digital signature" and RSA. If you +don't know these terms, please read the cryptography FAQ part 6 and 7, which +is posted to the usenet newsgroup sci.crypt. It is also available from + + ftp://rtfm.mit.edu/pub/usenet/news.answers/cryptography-faq +and + http://www.cis.ohio-state.edu/hypertext/faq/usenet/cryptography-faq + +I'll concentrate on the questions specific to SSL and samba here. + + +What is a certificate? +====================== +A certificate is issued by an issuer, usually a "Certification Authority" +(CA), who confirms something by issuing the certificate. The subject of this +confirmation depends on the CA's policy. CAs for secure web servers (used for +shopping malls etc.) usually only attest that the given public key belongs the +the given domain name. Company-wide CAs might attest that you are an employee +of the company, that you have permissions to use a server or whatever. + + +What is an X.509 certificate technically? +========================================= +Technically, the certificate is a block of data signed by the certificate +issuer (the CA). The relevant fields are: + - unique identifier (name) of the certificate issuer + - time range during that the certificate is valid + - unique identifier (name) of the certified subject + - public key of the certified subject + - the issuer's signature over all of the above +If this certificate should be verified, the verifier must have a table of the +names and public keys of trusted CAs. For simplicity, these tables are lists +of certificates issued by the respective CAs for themselves (self-signed +certificates). + + +What are the implications of this certificate structure? +======================================================== + - Because the certificate contains the subject's public key, the + certificate and the private key together are all that's needed to encrypt + and decrypt. + - To verify certificates, you need the certificates of all CAs you trust. + - The simplest form of a dummy-certificate is one that's signed by the + subject itself. + - A CA is needed. The client can't simply issue local certificates for + servers it trusts because the server determines which certificate it + presents. + + + +########################################################################### +Setting up files and directories for OpenSSL +########################################################################### + +The first thing you should do is to change your PATH environment variable to +include the bin directory of OpenSSL. E.g.: + + PATH=$PATH:/usr/local/ssl/bin + +If your system's kernel supports a /dev/urandom device, all OpenSSL operations +will automatically retrieve its entropy from it. If your system does not +support /dev/urandom, you may install an EGD/PRNGD daemon for entropy +supply or can generate seed from reading files (that should contain information +unpredictable/unknown to attackers). Use the "-rand" option to the openssl +commands to specify the entropy source (if /dev/urandom is not available). + +OpenSSL additionally keeps random seed in the $HOME/.rnd file. You can +initialize this file using: + + openssl rand -rand /tmp/rfile.txt > $HOME/.rnd + rm -f /tmp/rfile.txt # nobody must know!! + +or + + openssl rand -rand /path/to/egd-socket > $HOME/.rnd + +How to create a keypair +======================= +This is done with 'genrsa' for RSA keys and 'gendsa' for DSA keys. For an RSA +key with 1024 bits which is written to the file "key.pem" type: + + openssl genrsa -des3 -rand /path/to/source 1024 > key.pem + +You will be asked for a pass phrase to protect this key. If you don't want to +protect your private key with a pass phrase, just omit the parameter "-des3". +If you want a different key size, replace the parameter "1024". You really +should use a pass phrase. + +If you want to remove the pass phrase from a key use: + + openssl rsa -in key.pem -out newkey.pem + +And to add or change a pass phrase: + + openssl rsa -des3 -in key.pem -out newkey.pem + + +How to create a dummy certificate +================================= +If you still have your keypair in the file "key.pem", the command + + openssl req -new -x509 -key key.pem -out cert.pem + +will write a self-signed dummy certificate to the file "cert.pem". This can +be used for testing or if only encryption and no certification is needed. +Please bear in mind that encryption without authentication (certification) +can never be secure. It's open to (at least) "man-in-the-middle" attacks. + + +How to create a certificate signing request +=========================================== +You must not simply send your keypair to the CA for signing because it +contains the private key which _must_ be kept secret. A signing request +consists of your public key and some additional information you want to have +bound to that key by the certificate. If you operate a secure web server, +this additional information will (among other things) contain the URL of +your server in the field "Common Name". The certificate signing request is +created from the keypair with the following command (assuming that the key +pair is still in "key.pem"): + + openssl req -new -key key.pem -out csr.pem + +This command will ask you for the information which must be included in the +certificate and will write the signing request to the file "csr.pem". This +signing request is all the CA needs for signing, at least technically. Most +CAs will demand bureaucratic material and money, too. + + +How to set up a Certification Authority (CA) +============================================ +Being a certification authority requires a database that holds the CA's +keypair, the CA's certificate, a list of all signed certificates and other +information. This database is kept in a directory hierarchy below a +configurable starting point. The starting point must be configured in the +ssleay.conf file. This file is at /usr/local/ssl/lib/ssleay.conf if you have +not changed the default installation path. + +The first thing you should do is to edit this file according to your needs. +Let's assume that you want to hold the CA's database at the directory +"/usr/local/ssl/CA". Change the variable "dir" in section "CA_default" to +this path. You may also want to edit the default settings for some variables, +but the values given should be OK. This path is also contained in the shell +script CA.sh, which should be at "/usr/local/ssl/bin/CA.sh". Change the path +in the shell script: + + CATOP=/usr/local/ssl/CA + CAKEY=./cakey.pem # relative to $CATOP/ + CACERT=./cacert.pem # relative to $CATOP/private/ + +Then create the directory "/usr/local/ssl/CA" and make it writable for the +user that operates the CA. You should also initialize SSLeay as CA user (set +up the random number generator). Now you should call the shell script CA.sh +to set up the initial database: + + CA.sh -newca + +This command will ask you whether you want to use an existing certificate or +create one. Just press enter to create a new key pair and certificate. You +will be asked the usual questions for certificates: the country, state, city, +"Common Name", etc. Enter the appropriate values for the CA. When CA.sh +finishes, it has set up a bunch of directories and files. A CA must publish +it's certificate, which is in the file "/usr/local/ssl/CA/cacert.pem". + + +How to sign a certificate request +================================= +After setting up the CA stuff, you can start signing certificate requests. +Make sure that the SSLeay utilities know where the configuration file is. +The default is compiled in, if you don't use the default location, add the +parameter "-config <cfg-file>". Make also sure that the configuration file +contains the correct path to the CA database. If all this is set up properly, +you can sign the request in the file "csr.pem" with the command: + + openssl ca -policy policy_anything -days 365 -infiles csr.pem >cert.pem + +The resulting certificate (and additional information) will be in "cert.pem". +If you want the certificate to be valid for a period different from 365 days, +simply change the "-days" parameter. + + +How to install a new CA certificate +=================================== +Whereever a certificate must be checked, the CA's certificate must be +available. Let's take the common case where the client verifies the server's +certificate. The case where the server verfies the client's certificate works +the same way. The client receives the server's certificate, which contains +the "Distinguished Name" of the CA. To verify whether the signature in this +certificate is OK, it must look up the public key of that CA. Therefore each +client must hold a database of CAs, indexed by CA name. This database is best +kept in a directory where each file contains the certificate of one CA and is +named after the hashvalue (checksum) of the CA's name. This section describes +how such a database is managed technically. Whether or not to install (and +thereby trust) a CA is a totally different matter. + +The client must know the directory of the CA database. This can be configured. +There may also be a configuration option to set up a CA database file which +contains all CA certs in one file. Let's assume that the CA database is kept +in the directory "/usr/local/ssl/certs". The following example assumes that +the CA's certificate is in the file "cacert.pem" and the CA is known as +"myCA". To install the certificate, do the following: + + cp cacert.pem /usr/local/ssl/cers/myCA.pem + cd /usr/local/ssl/certs + ln -s myCA.pem `openssl x509 -noout -hash < myCA.pem`.0 + +The last command creates a link from the hashed name to the real file. + +From now on all certificates signed by the myCA authority will be accepted by +clients that use the directory "/usr/local/ssl/certs/" as their CA certificate +database. + + + diff --git a/source/include/doserr.h b/source/include/doserr.h new file mode 100644 index 00000000000..dc5bd0414a3 --- /dev/null +++ b/source/include/doserr.h @@ -0,0 +1,173 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + DOS error code constants + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) John H Terpstra 1996-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Paul Ashton 1998-2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _DOSERR_H +#define _DOSERR_H + +/* Error classes */ + +#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ +#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* SMB X/Open error codes for the ERRDOS error class */ +#define ERRsuccess 0 /* No error */ +#define ERRbadfunc 1 /* Invalid function (or system call) */ +#define ERRbadfile 2 /* File not found (pathname error) */ +#define ERRbadpath 3 /* Directory not found */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid fid */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* Out of memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ +#define ERRres 14 /* reserved */ +#define ERRbaddrive 15 /* Invalid drive */ +#define ERRremcd 16 /* Attempt to delete current directory */ +#define ERRdiffdevice 17 /* rename/move across different filesystems */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode on file conflict with open mode */ +#define ERRlock 33 /* Lock request conflicts with existing lock */ +#define ERRunsup 50 /* Request unsupported, returned by Win 95, RJS 20Jun98 */ +#define ERRnosuchshare 67 /* You specified an invalid share name */ +#define ERRfilexists 80 /* File in operation already exists */ +#define ERRinvalidparam 87 +#define ERRcannotopen 110 /* Cannot open the file specified */ +#define ERRinsufficientbuffer 122 +#define ERRinvalidname 123 /* Invalid name */ +#define ERRunknownlevel 124 +#define ERRnotlocked 158 /* This region is not locked by this locking context. */ +#define ERRrename 183 +#define ERRbadpipe 230 /* Named pipe invalid */ +#define ERRpipebusy 231 /* All instances of pipe are busy */ +#define ERRpipeclosing 232 /* named pipe close in progress */ +#define ERRnotconnected 233 /* No process on other end of named pipe */ +#define ERRmoredata 234 /* More data to be returned */ +#define ERRnomoreitems 259 +#define ERRbaddirectory 267 /* Invalid directory name in a path. */ +#define ERReasnotsupported 282 /* Extended attributes */ +#define ERRbuftoosmall 2123 +#define ERRunknownipc 2142 +#define ERRnosuchprintjob 2151 + +/* here's a special one from observing NT */ +#define ERRnoipc 66 /* don't support ipc */ + +/* Error codes for the ERRSRV class */ + +#define ERRerror 1 /* Non specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* No permissions to do the requested operation */ +#define ERRinvnid 5 /* tid invalid */ +#define ERRinvnetname 6 /* Invalid servername */ +#define ERRinvdevice 7 /* Invalid device */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Queued item too big */ +#define ERRinvpfid 52 /* Invalid print file in smb_fid */ +#define ERRsmbcmd 64 /* Unrecognised command */ +#define ERRsrverror 65 /* smb server internal error */ +#define ERRfilespecs 67 /* fid and pathname invalid combination */ +#define ERRbadlink 68 /* reserved */ +#define ERRbadpermits 69 /* Access specified for a file is not valid */ +#define ERRbadpid 70 /* reserved */ +#define ERRsetattrmode 71 /* attribute mode invalid */ +#define ERRpaused 81 /* Message server paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room for message */ +#define ERRrmuns 87 /* too many remote usernames */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* too many userids */ +#define ERRbaduid 91 /* bad userid */ +#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ +#define ERRuseSTD 251 /* temporarily unable to use raw mode, use standard mode */ +#define ERRcontMPX 252 /* resume MPX mode */ +#define ERRbadPW /* reserved */ +#define ERRnosupport 0xFFFF +#define ERRunknownsmb 22 /* from NT 3.5 response */ + +/* Error codes for the ERRHRD class */ + +#define ERRnowrite 19 /* read only media */ +#define ERRbadunit 20 /* Unknown device */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data (CRC) error */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 +#define ERRbadmedia 26 +#define ERRbadsector 27 +#define ERRnopaper 28 +#define ERRwrite 29 /* write fault */ +#define ERRread 30 /* read fault */ +#define ERRgeneral 31 /* General hardware failure */ +#define ERRwrongdisk 34 +#define ERRFCBunavail 35 +#define ERRsharebufexc 36 /* share buffer exceeded */ +#define ERRdiskfull 39 + + +/* these are win32 error codes. There are only a few places where + these matter for Samba, primarily in the NT printing code */ +#define WERR_OK W_ERROR(0) +#define WERR_ACCESS_DENIED W_ERROR(5) +#define WERR_BADFID W_ERROR(6) +#define WERR_BADFUNC W_ERROR(1) +#define WERR_INSUFFICIENT_BUFFER W_ERROR(122) +#define WERR_INVALID_PARAM W_ERROR(87) +#define WERR_NOT_SUPPORTED W_ERROR(50) +#define WERR_BAD_PASSWORD W_ERROR(86) +#define WERR_NOMEM W_ERROR(8) +#define WERR_INVALID_NAME W_ERROR(123) +#define WERR_UNKNOWN_LEVEL W_ERROR(124) +#define WERR_NO_MORE_ITEMS W_ERROR(259) +#define WERR_MORE_DATA W_ERROR(234) +#define WERR_UNKNOWN_PRINTER_DRIVER W_ERROR(1797) +#define WERR_INVALID_PRINTER_NAME W_ERROR(1801) +#define WERR_INVALID_DATATYPE W_ERROR(1804) +#define WERR_INVALID_ENVIRONMENT W_ERROR(1805) +#define WERR_BUF_TOO_SMALL W_ERROR(2123) +#define WERR_JOB_NOT_FOUND W_ERROR(2151) +#define WERR_DEST_NOT_FOUND W_ERROR(2152) +#define WERR_PRINTER_DRIVER_IN_USE W_ERROR(3001) +#define WERR_STATUS_MORE_ENTRIES W_ERROR(0x0105) + +/* DFS errors */ + +#ifndef NERR_BASE +#define NERR_BASE (2100) +#endif + +#define WERR_DFS_NO_SUCH_VOL W_ERROR(NERR_BASE+562) +#define WERR_DFS_NO_SUCH_SHARE W_ERROR(NERR_BASE+565) +#define WERR_DFS_NO_SUCH_SERVER W_ERROR(NERR_BASE+573) +#define WERR_DFS_INTERNAL_ERROR W_ERROR(NERR_BASE+590) +#define WERR_DFS_CANT_CREATE_JUNCT W_ERROR(NERR_BASE+569) + +#endif /* _DOSERR_H */ diff --git a/source/nsswitch/pam_winbind.h b/source/nsswitch/pam_winbind.h new file mode 100644 index 00000000000..991c117656f --- /dev/null +++ b/source/nsswitch/pam_winbind.h @@ -0,0 +1,81 @@ +/* pam_winbind header file + (Solaris needs some macros from Linux for common PAM code) + + Shirish Kalele 2000 +*/ + +#ifdef HAVE_FEATURES_H +#include <features.h> +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <config.h> + +#define MODULE_NAME "pam_winbind" +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_PASSWORD + +#if defined(SUNOS5) || defined(SUNOS4) + +/* Solaris always uses dynamic pam modules */ +#define PAM_EXTERN extern +#include <security/pam_appl.h> + +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR +#endif + +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include <security/pam_modules.h> +#endif + +#ifdef HAVE_SECURITY__PAM_MACROS_H +#include <security/_pam_macros.h> +#else +/* Define required macros from (Linux PAM 0.68) security/_pam_macros.h */ +#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \ +do { \ + int reply_i; \ + \ + for (reply_i=0; reply_i<replies; ++reply_i) { \ + if (reply[reply_i].resp) { \ + _pam_overwrite(reply[reply_i].resp); \ + free(reply[reply_i].resp); \ + } \ + } \ + if (reply) \ + free(reply); \ +} while (0) + +#define _pam_overwrite(x) \ +do { \ + register char *__xx__; \ + if ((__xx__=(x))) \ + while (*__xx__) \ + *__xx__++ = '\0'; \ +} while (0) + +/* + * Don't just free it, forget it too. + */ + +#define _pam_drop(X) SAFE_FREE(X) + +#define x_strdup(s) ( (s) ? strdup(s):NULL ) +#endif + +#define PAM_DEBUG_ARG (1<<0) +#define PAM_USE_AUTHTOK_ARG (1<<1) +#define PAM_UNKNOWN_OK_ARG (1<<2) + +#include "winbind_nss_config.h" +#include "winbindd_nss.h" diff --git a/source/nsswitch/winbindd_glue.c b/source/nsswitch/winbindd_glue.c new file mode 100644 index 00000000000..0c508ccf4b2 --- /dev/null +++ b/source/nsswitch/winbindd_glue.c @@ -0,0 +1,442 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon glue functions to connect new cli interface + to older style lsa_ and samr_ functions + + Copyright (C) tridge@samba.org 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +/**************************************************************************** +do a LSA Open Policy +****************************************************************************/ +BOOL wb_lsa_open_policy(char *server, BOOL sec_qos, uint32 des_access, + CLI_POLICY_HND *pol) +{ + struct nmb_name calling, called; + struct ntuser_creds creds; + struct in_addr dest_ip; + fstring dest_host; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + extern pstring global_myname; + + ZERO_STRUCTP(pol); + + pol->cli = (struct cli_state *)malloc(sizeof(struct cli_state)); + pol->mem_ctx = talloc_init(); + + ZERO_STRUCTP(pol->cli); + + if (!pol->cli || !pol->mem_ctx) + return False; + + /* Initialise RPC connection */ + + if (!cli_initialise(pol->cli)) + goto done; + + ZERO_STRUCT(creds); + creds.pwd.null_pwd = 1; + + cli_init_creds(pol->cli, &creds); + + /* Establish a SMB connection */ + + if (!resolve_srv_name(server, dest_host, &dest_ip)) { + goto done; + } + + make_nmb_name(&called, dns_to_netbios_name(dest_host), 0x20); + make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0); + + if (!cli_establish_connection(pol->cli, dest_host, &dest_ip, &calling, + &called, "IPC$", "IPC", False, True)) { + goto done; + } + + if (!cli_nt_session_open (pol->cli, PIPE_LSARPC)) { + goto done; + } + + result = cli_lsa_open_policy(pol->cli, pol->mem_ctx, sec_qos, + des_access, &pol->handle); + + done: + if (!NT_STATUS_IS_OK(result) && pol->cli) { + if (pol->cli->initialised) + cli_shutdown(pol->cli); + SAFE_FREE(pol->cli); + } + + return NT_STATUS_IS_OK(result); +} + +/**************************************************************************** +do a LSA Enumerate Trusted Domain +****************************************************************************/ +BOOL wb_lsa_enum_trust_dom(CLI_POLICY_HND *hnd, uint32 *enum_ctx, + uint32 * num_doms, char ***names, DOM_SID **sids) +{ + NTSTATUS ret; + + ret = cli_lsa_enum_trust_dom(hnd->cli, hnd->mem_ctx, &hnd->handle, + enum_ctx, num_doms, names, sids); + + return NT_STATUS_IS_OK(ret); +} + +/**************************************************************************** +do a LSA Query Info Policy +****************************************************************************/ +BOOL wb_lsa_query_info_pol(CLI_POLICY_HND *hnd, uint16 info_class, + fstring domain_name, DOM_SID *domain_sid) +{ + NTSTATUS ret; + + ret = cli_lsa_query_info_policy(hnd->cli, hnd->mem_ctx, &hnd->handle, + info_class, domain_name, domain_sid); + + return NT_STATUS_IS_OK(ret); +} + +/**************************************************************************** +do a LSA Lookup Names +****************************************************************************/ +BOOL wb_lsa_lookup_names(CLI_POLICY_HND *hnd, int num_names, char **names, + DOM_SID **sids, uint32 **types, int *num_sids) +{ + NTSTATUS ret; + + ret = cli_lsa_lookup_names(hnd->cli, hnd->mem_ctx, &hnd->handle, + num_names, names, sids, types, num_sids); + + return NT_STATUS_IS_OK(ret); +} + +/**************************************************************************** +do a LSA Lookup SIDS +****************************************************************************/ +BOOL wb_lsa_lookup_sids(CLI_POLICY_HND *hnd, int num_sids, DOM_SID *sids, + char ***names, uint32 **types, int *num_names) +{ + NTSTATUS ret; + + ret = cli_lsa_lookup_sids(hnd->cli, hnd->mem_ctx, &hnd->handle, + num_sids, sids, names, types, num_names); + + return NT_STATUS_IS_OK(ret); +} + +/**************************************************************************** +lsa_close glue +****************************************************************************/ +BOOL wb_lsa_close(CLI_POLICY_HND *hnd) +{ + NTSTATUS ret; + + ret = cli_lsa_close(hnd->cli, hnd->mem_ctx, &hnd->handle); + + return NT_STATUS_IS_OK(ret); +} + + +/**************************************************************************** +samr_close glue +****************************************************************************/ +BOOL wb_samr_close(CLI_POLICY_HND *hnd) +{ + NTSTATUS ret; + + ret = cli_samr_close(hnd->cli, hnd->mem_ctx, &hnd->handle); + + return NT_STATUS_IS_OK(ret); +} + + +/**************************************************************************** +samr_connect glue +****************************************************************************/ +BOOL wb_samr_connect(char *server, uint32 access_mask, CLI_POLICY_HND *pol) +{ + struct nmb_name calling, called; + struct ntuser_creds creds; + struct in_addr dest_ip; + fstring dest_host; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + extern pstring global_myname; + + ZERO_STRUCTP(pol); + + pol->cli = (struct cli_state *)malloc(sizeof(struct cli_state)); + + ZERO_STRUCTP(pol->cli); + + pol->mem_ctx = talloc_init(); + + if (!pol->cli || !pol->mem_ctx) + return False; + + /* Initialise RPC connection */ + + if (!cli_initialise(pol->cli)) + goto done; + + ZERO_STRUCT(creds); + creds.pwd.null_pwd = 1; + + cli_init_creds(pol->cli, &creds); + + /* Establish a SMB connection */ + + if (!resolve_srv_name(server, dest_host, &dest_ip)) { + goto done; + } + + make_nmb_name(&called, dns_to_netbios_name(dest_host), 0x20); + make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0); + + if (!cli_establish_connection(pol->cli, dest_host, &dest_ip, &calling, + &called, "IPC$", "IPC", False, True)) { + goto done; + } + + if (!cli_nt_session_open (pol->cli, PIPE_SAMR)) { + goto done; + } + + result = cli_samr_connect(pol->cli, pol->mem_ctx, + access_mask, &pol->handle); + + done: + if (!NT_STATUS_IS_OK(result) && pol->cli) { + if (pol->cli->initialised) + cli_shutdown(pol->cli); + SAFE_FREE(pol->cli); + } + + return NT_STATUS_IS_OK(result); +} + + +/**************************************************************************** +samr_open_domain glue +****************************************************************************/ +BOOL wb_samr_open_domain(CLI_POLICY_HND *connect_pol, uint32 ace_perms, + DOM_SID *sid, CLI_POLICY_HND *domain_pol) +{ + NTSTATUS ret; + + ret = cli_samr_open_domain(connect_pol->cli, + connect_pol->mem_ctx, + &connect_pol->handle, + ace_perms, + sid, + &domain_pol->handle); + + if NT_STATUS_IS_OK(ret) { + domain_pol->cli = connect_pol->cli; + domain_pol->mem_ctx = connect_pol->mem_ctx; + return True; + } + + return False; +} + +/**************************************************************************** +do a SAMR enumerate groups +****************************************************************************/ +NTSTATUS wb_samr_enum_dom_groups(CLI_POLICY_HND *pol, uint32 *start_idx, + uint32 size, struct acct_info **sam, + uint32 *num_sam_groups) +{ + return cli_samr_enum_dom_groups(pol->cli, pol->mem_ctx, &pol->handle, + start_idx, size, sam, num_sam_groups); +} + +/**************************************************************************** +do a SAMR query userinfo +****************************************************************************/ +BOOL wb_get_samr_query_userinfo(CLI_POLICY_HND *pol, uint32 info_level, + uint32 user_rid, SAM_USERINFO_CTR **ctr) +{ + POLICY_HND user_pol; + BOOL got_user_pol = False; + NTSTATUS result; + + result = cli_samr_open_user(pol->cli, pol->mem_ctx, + &pol->handle, MAXIMUM_ALLOWED_ACCESS, + user_rid, &user_pol); + if (!NT_STATUS_IS_OK(result)) + goto done; + + got_user_pol = True; + + result = cli_samr_query_userinfo(pol->cli, pol->mem_ctx, + &user_pol, info_level, ctr); + if (!NT_STATUS_IS_OK(result)) + goto done; + + done: + if (got_user_pol) cli_samr_close(pol->cli, pol->mem_ctx, &user_pol); + + return NT_STATUS_IS_OK(result); +} + +/**************************************************************************** +do a SAMR enumerate groups +****************************************************************************/ +BOOL wb_samr_open_user(CLI_POLICY_HND *pol, uint32 access_mask, uint32 rid, + POLICY_HND *user_pol) +{ + NTSTATUS ret; + + ret = cli_samr_open_user(pol->cli, pol->mem_ctx, &pol->handle, + access_mask, rid, user_pol); + + return NT_STATUS_IS_OK(ret); +} + +BOOL wb_samr_query_usergroups(CLI_POLICY_HND *pol, uint32 *num_groups, + DOM_GID **gid) +{ + NTSTATUS ret; + + ret = cli_samr_query_usergroups(pol->cli, pol->mem_ctx, &pol->handle, + num_groups, gid); + + return NT_STATUS_IS_OK(ret); +} + +BOOL wb_get_samr_query_groupinfo(CLI_POLICY_HND *pol, uint32 info_level, + uint32 group_rid, GROUP_INFO_CTR *ctr) +{ + POLICY_HND group_pol; + BOOL got_group_pol = False; + NTSTATUS result; + + result = cli_samr_open_group(pol->cli, pol->mem_ctx, + &pol->handle, MAXIMUM_ALLOWED_ACCESS, + group_rid, &group_pol); + if (!NT_STATUS_IS_OK(result)) + goto done; + + got_group_pol = True; + + result = cli_samr_query_groupinfo(pol->cli, pol->mem_ctx, + &group_pol, info_level, + ctr); + done: + if (got_group_pol) cli_samr_close(pol->cli, pol->mem_ctx, &group_pol); + + return NT_STATUS_IS_OK(result); +} + +BOOL wb_sam_query_groupmem(CLI_POLICY_HND *pol, uint32 group_rid, + uint32 *num_names, uint32 **rid_mem, + char ***names, uint32 **name_types) +{ + BOOL got_group_pol = False; + POLICY_HND group_pol; + NTSTATUS result; + uint32 i, total_names = 0; + + result = cli_samr_open_group(pol->cli, pol->mem_ctx, + &pol->handle, MAXIMUM_ALLOWED_ACCESS, + group_rid, &group_pol); + if (!NT_STATUS_IS_OK(result)) + goto done; + + got_group_pol = True; + + result = cli_samr_query_groupmem(pol->cli, pol->mem_ctx, + &group_pol, num_names, rid_mem, + name_types); + if (!NT_STATUS_IS_OK(result)) + goto done; + + /* Call cli_samr_lookup_rids() in bunches of ~1000 rids to avoid + crashing NT4. */ + +#define MAX_LOOKUP_RIDS 900 + + *names = talloc(pol->mem_ctx, *num_names * sizeof(char *)); + *name_types = talloc(pol->mem_ctx, *num_names * sizeof(uint32)); + + for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) { + int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS); + uint32 tmp_num_names = 0; + char **tmp_names = NULL; + uint32 *tmp_types = NULL; + + /* Lookup a chunk of rids */ + + result = cli_samr_lookup_rids(pol->cli, pol->mem_ctx, + &pol->handle, 1000, /* flags */ + num_lookup_rids, + &(*rid_mem)[i], + &tmp_num_names, + &tmp_names, &tmp_types); + if (!NT_STATUS_IS_OK(result)) + goto done; + + /* Copy result into array. The talloc system will take + care of freeing the temporary arrays later on. */ + + memcpy(&(*names)[i], tmp_names, sizeof(char *) * + tmp_num_names); + + memcpy(&(*name_types)[i], tmp_types, sizeof(uint32) * + tmp_num_names); + + total_names += tmp_num_names; + } + + *num_names = total_names; + + done: + if (got_group_pol) + cli_samr_close(pol->cli, pol->mem_ctx, &group_pol); + + return NT_STATUS_IS_OK(result); +} + +BOOL wb_samr_query_dom_info(CLI_POLICY_HND *pol, uint16 switch_value, + SAM_UNK_CTR *ctr) +{ + NTSTATUS ret; + + ret = cli_samr_query_dom_info(pol->cli, pol->mem_ctx, + &pol->handle, switch_value, ctr); + + return NT_STATUS_IS_OK(ret); +} + +/* Unlike all the others, the status code of this function is actually used + by winbindd. */ + +NTSTATUS wb_samr_query_dispinfo(CLI_POLICY_HND *pol, uint32 *start_ndx, + uint16 info_level, uint32 *num_entries, + SAM_DISPINFO_CTR *ctr) +{ + return cli_samr_query_dispinfo(pol->cli, pol->mem_ctx, + &pol->handle, start_ndx, + info_level, num_entries, + 0xffff, ctr); +} diff --git a/source/nsswitch/winbindd_misc.c b/source/nsswitch/winbindd_misc.c new file mode 100644 index 00000000000..620dc9e7d9b --- /dev/null +++ b/source/nsswitch/winbindd_misc.c @@ -0,0 +1,174 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon - miscellaneous other functions + + Copyright (C) Tim Potter 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +extern pstring global_myname; + +/* Some routines to fetch the trust account password from a HEAD + version of Samba. Yuck. )-: */ + +/************************************************************************ +form a key for fetching a domain trust password from +************************************************************************/ +static char *trust_keystr(char *domain) +{ + static fstring keystr; + + snprintf(keystr,sizeof(keystr),"%s/%s", SECRETS_MACHINE_ACCT_PASS, + domain); + + return keystr; +} + +/************************************************************************ + Routine to get the trust account password for a domain +************************************************************************/ +static BOOL _get_trust_account_password(char *domain, unsigned char *ret_pwd, + time_t *pass_last_set_time) +{ + struct machine_acct_pass *pass; + size_t size; + + if (!(pass = secrets_fetch(trust_keystr(domain), &size)) || + size != sizeof(*pass)) return False; + + if (pass_last_set_time) *pass_last_set_time = pass->mod_time; + memcpy(ret_pwd, pass->hash, 16); + SAFE_FREE(pass); + return True; +} + +/* Check the machine account password is valid */ + +enum winbindd_result winbindd_check_machine_acct(struct winbindd_cli_state *state) +{ + NTSTATUS status; + uchar trust_passwd[16]; + struct in_addr *ip_list = NULL; + int count; + uint16 validation_level; + fstring controller, trust_account; + int num_retries = 0; + + DEBUG(3, ("[%5d]: check machine account\n", state->pid)); + + /* Get trust account password */ + + again: + if (!_get_trust_account_password(lp_workgroup(), trust_passwd, + NULL)) { + status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + /* Get domain controller */ + + if (!get_dc_list(True, lp_workgroup(), &ip_list, &count) || + !lookup_pdc_name(global_myname, lp_workgroup(), &ip_list[0], + controller)) { + DEBUG(0, ("could not find domain controller for " + "domain %s\n", lp_workgroup())); + status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + goto done; + } + + DEBUG(3, ("contacting controller %s to check secret\n", controller)); + + /* Contact domain controller to check secret */ + + slprintf(trust_account, sizeof(trust_account) - 1, "%s$", + global_myname); + +#if 0 /* XXX */ + status = cli_nt_setup_creds(controller, lp_workgroup(), global_myname, + trust_account, trust_passwd, + SEC_CHAN_WKSTA, &validation_level); +#endif + + /* There is a race condition between fetching the trust account + password and joining the domain so it's possible that the trust + account password has been changed on us. We are returned + NT_STATUS_ACCESS_DENIED if this happens. */ + +#define MAX_RETRIES 8 + + if ((num_retries < MAX_RETRIES) && + NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) { + num_retries++; + goto again; + } + + /* Pass back result code - zero for success, other values for + specific failures. */ + + DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(status) ? "good" : "bad")); + + done: + state->response.data.num_entries = NT_STATUS_V(status); + return WINBINDD_OK; +} + +enum winbindd_result winbindd_list_trusted_domains(struct winbindd_cli_state + *state) +{ + struct winbindd_domain *domain; + int total_entries = 0, extra_data_len = 0; + char *ted, *extra_data = NULL; + + DEBUG(3, ("[%5d]: list trusted domains\n", state->pid)); + + for(domain = domain_list; domain; domain = domain->next) { + + /* Skip own domain */ + + if (strequal(domain->name, lp_workgroup())) continue; + + /* Add domain to list */ + + total_entries++; + ted = Realloc(extra_data, sizeof(fstring) * + total_entries); + + if (!ted) { + DEBUG(0,("winbindd_list_trusted_domains: failed to enlarge buffer!\n")); + SAFE_FREE(extra_data); + return WINBINDD_ERROR; + } + else extra_data = ted; + + memcpy(&extra_data[extra_data_len], domain->name, + strlen(domain->name)); + + extra_data_len += strlen(domain->name); + extra_data[extra_data_len++] = ','; + } + + if (extra_data) { + if (extra_data_len > 1) extra_data[extra_data_len - 1] = '\0'; + state->response.extra_data = extra_data; + state->response.length += extra_data_len; + } + + return WINBINDD_OK; +} diff --git a/source/passdb/machine_sid.c b/source/passdb/machine_sid.c new file mode 100644 index 00000000000..859f00b4c6e --- /dev/null +++ b/source/passdb/machine_sid.c @@ -0,0 +1,255 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password and authentication handling + Copyright (C) Jeremy Allison 1996-1998 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + Copyright (C) Gerald (Jerry) Carter 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** + Read the machine SID from a file. +****************************************************************************/ + +static BOOL read_sid_from_file(int fd, char *sid_file) +{ + fstring fline; + + memset(fline, '\0', sizeof(fline)); + + if(read(fd, fline, sizeof(fline) -1 ) < 0) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Convert to the machine SID. + */ + + fline[sizeof(fline)-1] = '\0'; + if(!string_to_sid( &global_sam_sid, fline)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + return True; +} + +/**************************************************************************** + Generate the global machine sid. Look for the MACHINE.SID file first, if + not found then look in smb.conf and use it to create the MACHINE.SID file. + Note this function will be replaced soon. JRA. +****************************************************************************/ + +BOOL pdb_generate_sam_sid(void) +{ + int fd; + pstring sid_file; + fstring sid_string; + SMB_STRUCT_STAT st; + BOOL overwrite_bad_sid = False; + + generate_wellknown_sids(); + + pstrcpy(sid_file, lp_private_dir()); + + if (!directory_exist(sid_file, NULL)) { + if (mkdir(sid_file, 0700) != 0) { + DEBUG(0,("can't create private directory %s : %s\n", + sid_file, strerror(errno))); + return False; + } + } + + pstrcat(sid_file, "/MACHINE.SID"); + + if((fd = sys_open(sid_file, O_RDWR | O_CREAT, 0644)) == -1) { + DEBUG(0,("unable to open or create file %s. Error was %s\n", + sid_file, strerror(errno) )); + return False; + } + + /* + * Check if the file contains data. + */ + + if(sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(st.st_size > 0) { + /* + * We have a valid SID - read it. + */ + if(!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + /* + * JRA. Reversed the sense of this test now that I have + * actually done this test *personally*. One more reason + * to never trust third party information you have not + * independently verified.... sigh. JRA. + */ + + if(global_sam_sid.num_auths > 0 && global_sam_sid.sub_auths[0] == 0x21) { + /* + * Fix and re-write... + */ + overwrite_bad_sid = True; + global_sam_sid.sub_auths[0] = 21; + DEBUG(5,("pdb_generate_sam_sid: Old (incorrect) sid id_auth of hex 21 \ +detected - re-writing to be decimal 21 instead.\n" )); + sid_to_string(sid_string, &global_sam_sid); + if(sys_lseek(fd, (SMB_OFF_T)0, SEEK_SET) != 0) { + DEBUG(0,("unable to seek file file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + } else { + close(fd); + return True; + } + } else { + /* + * The file contains no data - we need to generate our + * own sid. + * Generate the new sid data & turn it into a string. + */ + int i; + uchar raw_sid_data[12]; + DOM_SID mysid; + + memset((char *)&mysid, '\0', sizeof(DOM_SID)); + mysid.sid_rev_num = 1; + mysid.id_auth[5] = 5; + mysid.num_auths = 0; + mysid.sub_auths[mysid.num_auths++] = 21; + + generate_random_buffer( raw_sid_data, 12, True); + for( i = 0; i < 3; i++) + mysid.sub_auths[mysid.num_auths++] = IVAL(raw_sid_data, i*4); + + sid_to_string(sid_string, &mysid); + } + + fstrcat(sid_string, "\n"); + + /* + * Ensure our new SID is valid. + */ + + if(!string_to_sid( &global_sam_sid, sid_string)) { + DEBUG(0,("unable to generate machine SID.\n")); + return False; + } + + /* + * Do an exclusive blocking lock on the file. + */ + + if(!do_file_lock( fd, 60, F_WRLCK)) { + DEBUG(0,("unable to lock file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(!overwrite_bad_sid) { + /* + * At this point we have a blocking lock on the SID + * file - check if in the meantime someone else wrote + * SID data into the file. If so - they were here first, + * use their data. + */ + + if(sys_fstat( fd, &st) < 0) { + DEBUG(0,("unable to stat file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + + if(st.st_size > 0) { + /* + * Unlock as soon as possible to reduce + * contention on the exclusive lock. + */ + do_file_lock( fd, 60, F_UNLCK); + + /* + * We have a valid SID - read it. + */ + + if(!read_sid_from_file( fd, sid_file)) { + DEBUG(0,("unable to read file %s. Error was %s\n", + sid_file, strerror(errno) )); + close(fd); + return False; + } + close(fd); + return True; + } + } + + /* + * The file is still empty and we have an exlusive lock on it, + * or we're fixing an earlier mistake. + * Write out out SID data into the file. + */ + + /* + * Use chmod here as some (strange) UNIX's don't + * have fchmod. JRA. + */ + + if(chmod(sid_file, 0644) < 0) { + DEBUG(0,("unable to set correct permissions on file %s. \ +Error was %s\n", sid_file, strerror(errno) )); + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return False; + } + + if(write( fd, sid_string, strlen(sid_string)) != strlen(sid_string)) { + DEBUG(0,("unable to write file %s. Error was %s\n", + sid_file, strerror(errno) )); + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return False; + } + + /* + * Unlock & exit. + */ + + do_file_lock( fd, 60, F_UNLCK); + close(fd); + return True; +} + + diff --git a/source/passdb/pdb_ldap.c b/source/passdb/pdb_ldap.c new file mode 100644 index 00000000000..d44a6133f37 --- /dev/null +++ b/source/passdb/pdb_ldap.c @@ -0,0 +1,1033 @@ +/* + Unix SMB/Netbios implementation. + Version 2.9. + LDAP protocol helper functions for SAMBA + Copyright (C) Shahms King 2001 + Copyright (C) Jean François Micouleau 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +#ifdef WITH_LDAP_SAM +/* TODO: +* persistent connections: if using NSS LDAP, many connections are made +* however, using only one within Samba would be nice +* +* Clean up SSL stuff, compile on OpenLDAP 1.x, 2.x, and Netscape SDK +* +* Other LDAP based login attributes: accountExpires, etc. +* (should be the domain of Samba proper, but the sam_password/SAM_ACCOUNT +* structures don't have fields for some of these attributes) +* +* SSL is done, but can't get the certificate based authentication to work +* against on my test platform (Linux 2.4, OpenLDAP 2.x) +*/ + +/* NOTE: this will NOT work against an Active Directory server +* due to the fact that the two password fields cannot be retrieved +* from a server; recommend using security = domain in this situation +* and/or winbind +*/ + +#include <lber.h> +#include <ldap.h> + +#ifndef SAM_ACCOUNT +#define SAM_ACCOUNT struct sam_passwd +#endif + +struct ldap_enum_info +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; +}; + +static struct ldap_enum_info global_ldap_ent; + + +/******************************************************************* + open a connection to the ldap server. +******************************************************************/ +static BOOL +ldap_open_connection (LDAP ** ldap_struct) +{ + int port; + int version, rc; + int tls = LDAP_OPT_X_TLS_HARD; + + if (lp_ldap_ssl() == LDAP_SSL_ON && lp_ldap_port() == 389) { + port = 636; + } + else { + port = lp_ldap_port(); + } + + if ((*ldap_struct = ldap_init(lp_ldap_server(), port)) == NULL) { + DEBUG(0, ("The LDAP server is not responding !\n")); + return (False); + } + + /* Connect to older servers using SSL and V2 rather than Start TLS */ + if (ldap_get_option(*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) + { + if (version != LDAP_VERSION2) + { + version = LDAP_VERSION2; + ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); + } + } + + switch (lp_ldap_ssl()) + { + case LDAP_SSL_START_TLS: + if (ldap_get_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, + &version) == LDAP_OPT_SUCCESS) + { + if (version < LDAP_VERSION3) + { + version = LDAP_VERSION3; + ldap_set_option (*ldap_struct, LDAP_OPT_PROTOCOL_VERSION, + &version); + } + } + if ((rc = ldap_start_tls_s (*ldap_struct, NULL, NULL)) != LDAP_SUCCESS) + { + DEBUG(0, + ("Failed to issue the StartTLS instruction: %s\n", + ldap_err2string(rc))); + return False; + } + DEBUG (2, ("StartTLS issued: using a TLS connection\n")); + break; + case LDAP_SSL_ON: + if (ldap_set_option (*ldap_struct, LDAP_OPT_X_TLS, &tls) != LDAP_SUCCESS) + { + DEBUG(0, ("Failed to setup a TLS session\n")); + } + break; + case LDAP_SSL_OFF: + default: + } + + DEBUG(2, ("ldap_open_connection: connection opened\n")); + return (True); +} + +/******************************************************************* + connect to the ldap server under system privilege. +******************************************************************/ +static BOOL ldap_connect_system(LDAP * ldap_struct) +{ + int rc; + static BOOL got_pw = False; + static pstring ldap_secret; + + /* get the password if we don't have it already */ + if (!got_pw && !(got_pw=fetch_ldap_pw(lp_ldap_admin_dn(), ldap_secret, sizeof(pstring)))) + { + DEBUG(0, ("ldap_connect_system: Failed to retrieve password for %s from secrets.tdb\n", + lp_ldap_admin_dn())); + return False; + } + + /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite + (OpenLDAP) doesnt' seem to support it */ + if ((rc = ldap_simple_bind_s(ldap_struct, lp_ldap_admin_dn(), + ldap_secret)) != LDAP_SUCCESS) + { + DEBUG(0, ("Bind failed: %s\n", ldap_err2string(rc))); + return (False); + } + + DEBUG(2, ("ldap_connect_system: succesful connection to the LDAP server\n")); + return (True); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldap_search_one_user (LDAP * ldap_struct, const char *filter, LDAPMessage ** result) +{ + int scope = LDAP_SCOPE_SUBTREE; + int rc; + + DEBUG(2, ("ldap_search_one_user: searching for:[%s]\n", filter)); + + rc = ldap_search_s (ldap_struct, lp_ldap_suffix (), scope, + filter, NULL, 0, result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("ldap_search_one_user: Problem during the LDAP search: %s\n", + ldap_err2string (rc))); + DEBUG(3,("ldap_search_one_user: Query was: %s, %s\n", lp_ldap_suffix(), + filter)); + } + return (rc); +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldap_search_one_user_by_name (LDAP * ldap_struct, const char *user, + LDAPMessage ** result) +{ + pstring filter; + + /* + in the filter expression, replace %u with the real name + so in ldap filter, %u MUST exist :-) + */ + pstrcpy(filter, lp_ldap_filter()); + + /* have to use this here because $ is filtered out + * in pstring_sub + */ + all_string_sub(filter, "%u", user, sizeof(pstring)); + + return ldap_search_one_user(ldap_struct, filter, result); +} + +/******************************************************************* + run the search by uid. +******************************************************************/ +static int ldap_search_one_user_by_uid(LDAP * ldap_struct, int uid, + LDAPMessage ** result) +{ + struct passwd *user; + pstring filter; + + /* Get the username from the system and look that up in the LDAP */ + user = sys_getpwuid(uid); + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", user->pw_name, sizeof(pstring)); + + return ldap_search_one_user(ldap_struct, filter, result); +} + +/******************************************************************* + run the search by rid. +******************************************************************/ +static int ldap_search_one_user_by_rid (LDAP * ldap_struct, uint32 rid, + LDAPMessage ** result) +{ + pstring filter; + int rc; + + /* check if the user rid exsists, if not, try searching on the uid */ + snprintf(filter, sizeof(filter) - 1, "rid=%i", rid); + rc = ldap_search_one_user(ldap_struct, filter, result); + + if (rc != LDAP_SUCCESS) + rc = ldap_search_one_user_by_uid(ldap_struct, + pdb_user_rid_to_uid(rid), result); + + return rc; +} + +/******************************************************************* +search an attribute and return the first value found. +******************************************************************/ +static void get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry, + char *attribute, char *value) +{ + char **valeurs; + + if ((valeurs = ldap_get_values (ldap_struct, entry, attribute)) != NULL) { + pstrcpy(value, valeurs[0]); + ldap_value_free(valeurs); + DEBUG (2, ("get_single_attribute: [%s] = [%s]\n", attribute, value)); + } + else { + value = NULL; + DEBUG (2, ("get_single_attribute: [%s] = [NULL]\n", attribute)); + } +} + +/************************************************************************ +Routine to manage the LDAPMod structure array +manage memory used by the array, by each struct, and values + +************************************************************************/ +static void make_a_mod (LDAPMod *** modlist, int modop, char *attribute, char *value) +{ + LDAPMod **mods; + int i; + int j; + + mods = *modlist; + + if (attribute == NULL || *attribute == '\0') + return; + + if (value == NULL || *value == '\0') + return; + + if (mods == NULL) + { + mods = (LDAPMod **) malloc(sizeof(LDAPMod *)); + if (mods == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[0] = NULL; + } + + for (i = 0; mods[i] != NULL; ++i) { + if (mods[i]->mod_op == modop && !strcasecmp(mods[i]->mod_type, attribute)) + break; + } + + if (mods[i] == NULL) + { + mods = (LDAPMod **) realloc (mods, (i + 2) * sizeof (LDAPMod *)); + if (mods == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod)); + if (mods[i] == NULL) + { + DEBUG(0, ("make_a_mod: out of memory!\n")); + return; + } + mods[i]->mod_op = modop; + mods[i]->mod_values = NULL; + mods[i]->mod_type = strdup(attribute); + mods[i + 1] = NULL; + } + + if (value != NULL) + { + j = 0; + if (mods[i]->mod_values != NULL) { + for (; mods[i]->mod_values[j] != NULL; j++); + } + mods[i]->mod_values = (char **)realloc(mods[i]->mod_values, + (j + 2) * sizeof (char *)); + + if (mods[i]->mod_values == NULL) { + DEBUG (0, ("make_a_mod: Memory allocation failure!\n")); + return; + } + mods[i]->mod_values[j] = strdup(value); + mods[i]->mod_values[j + 1] = NULL; + } + *modlist = mods; +} + +/* New Interface is being implemented here */ + +/********************************************************************** +Initialize SAM_ACCOUNT from an LDAP query +(Based on init_sam_from_buffer in pdb_tdb.c) +*********************************************************************/ +static BOOL init_sam_from_ldap (SAM_ACCOUNT * sampass, + LDAP * ldap_struct, LDAPMessage * entry) +{ + time_t logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + static pstring username; + static pstring domain; + static pstring nt_username; + static pstring fullname; + static pstring homedir; + static pstring dir_drive; + static pstring logon_script; + static pstring profile_path; + static pstring acct_desc; + static pstring munged_dial; + static pstring workstations; + struct passwd *sys_user; + uint32 user_rid, group_rid; + static uint8 smblmpwd[16]; + static uint8 smbntpwd[16]; + uint16 acct_ctrl, logon_divs; + uint32 hours_len; + uint8 *hours; + pstring temp; + + get_single_attribute(ldap_struct, entry, "uid", username); + DEBUG(2, ("Entry found for user: %s\n", username)); + + pstrcpy(nt_username, username); + + get_single_attribute(ldap_struct, entry, "sambaDomain", domain); + if (!domain) + pstrcpy(domain, lp_workgroup()); + + get_single_attribute(ldap_struct, entry, "pwdLastSet", temp); + pass_last_set_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "logonTime", temp); + logon_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "logoffTime", temp); + logoff_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "kickoffTime", temp); + kickoff_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdCanChange", temp); + pass_can_change_time = (time_t) strtol(temp, NULL, 16); + + get_single_attribute(ldap_struct, entry, "pwdMustChange", temp); + pass_must_change_time = (time_t) strtol(temp, NULL, 16); + + /* recommend that 'gecos' and 'displayName' should refer to the same + * attribute OID. userFullName depreciated, only used by Samba + * primary rules of LDAP: don't make a new attribute when one is already defined + * that fits your needs; using gecos then displayName then cn rather than 'userFullName' + */ + + get_single_attribute(ldap_struct, entry, "gecos", fullname); + + if (!fullname) { + get_single_attribute(ldap_struct, entry, "displayName", fullname); + get_single_attribute(ldap_struct, entry, "cn", fullname); + } + + get_single_attribute(ldap_struct, entry, "homeDrive", dir_drive); + DEBUG(5,("homeDrive is set to %s\n",dir_drive)); + if (!*dir_drive) { + pstrcpy(dir_drive, lp_logon_drive()); + DEBUG(5,("homeDrive fell back to %s\n",dir_drive)); + } + + get_single_attribute(ldap_struct, entry, "smbHome", homedir); + DEBUG(5,("smbHome is set to %s\n",homedir)); + if (!*homedir) { + pstrcpy(homedir, lp_logon_home()); + DEBUG(5,("smbHome fell back to %s\n",homedir)); + } + + get_single_attribute(ldap_struct, entry, "scriptPath", logon_script); + DEBUG(5,("scriptPath is set to %s\n",logon_script)); + if (!*logon_script) { + pstrcpy(logon_script, lp_logon_script()); + DEBUG(5,("scriptPath fell back to %s\n",logon_script)); + } + + get_single_attribute(ldap_struct, entry, "profilePath", profile_path); + DEBUG(5,("profilePath is set to %s\n",profile_path)); + if (!*profile_path) { + pstrcpy(profile_path, lp_logon_path()); + DEBUG(5,("profilePath fell back to %s\n",profile_path)); + } + + get_single_attribute(ldap_struct, entry, "description", acct_desc); + get_single_attribute(ldap_struct, entry, "userWorkstations", workstations); + get_single_attribute(ldap_struct, entry, "rid", temp); + user_rid = (uint32)strtol(temp, NULL, 16); + get_single_attribute(ldap_struct, entry, "primaryGroupID", temp); + group_rid = (uint32)strtol(temp, NULL, 16); + + + /* These values MAY be in LDAP, but they can also be retrieved through + * sys_getpw*() which is how we're doing it (if you use nss_ldap, then + * these values will be stored in LDAP as well, but if not, we want the + * local values to override the LDAP for this anyway + * homeDirectory attribute + */ + sys_user = sys_getpwnam(username); + if (sys_user == NULL) + return False; + + + /* FIXME: hours stuff should be cleaner */ + logon_divs = 168; + hours_len = 21; + hours = malloc(sizeof(hours) * hours_len); + memset(hours, 0xff, hours_len); + + get_single_attribute (ldap_struct, entry, "lmPassword", temp); + pdb_gethexpwd(temp, smblmpwd); + memset((char *)temp, '\0', sizeof(temp)); + get_single_attribute (ldap_struct, entry, "ntPassword", temp); + pdb_gethexpwd(temp, smbntpwd); + memset((char *)temp, '\0', sizeof(temp)); + get_single_attribute (ldap_struct, entry, "acctFlags", temp); + acct_ctrl = pdb_decode_acct_ctrl(temp); + + if (acct_ctrl == 0) + acct_ctrl |= ACB_NORMAL; + + + pdb_set_acct_ctrl(sampass, acct_ctrl); + pdb_set_logon_time(sampass, logon_time); + pdb_set_logoff_time(sampass, logoff_time); + pdb_set_kickoff_time(sampass, kickoff_time); + pdb_set_pass_can_change_time(sampass, pass_can_change_time); + pdb_set_pass_must_change_time(sampass, pass_must_change_time); + pdb_set_pass_last_set_time(sampass, pass_last_set_time); + + pdb_set_hours_len(sampass, hours_len); + pdb_set_logons_divs(sampass, logon_divs); + + pdb_set_uid(sampass, sys_user->pw_uid); + pdb_set_gid(sampass, sys_user->pw_gid); + pdb_set_user_rid(sampass, user_rid); + pdb_set_group_rid(sampass, group_rid); + + pdb_set_username(sampass, username); + + pdb_set_domain(sampass, domain); + pdb_set_nt_username(sampass, nt_username); + + pdb_set_fullname(sampass, fullname); + + pdb_set_logon_script(sampass, logon_script); + pdb_set_profile_path(sampass, profile_path); + pdb_set_dir_drive(sampass, dir_drive); + pdb_set_homedir(sampass, homedir); + pdb_set_acct_desc(sampass, acct_desc); + pdb_set_workstations(sampass, workstations); + pdb_set_munged_dial(sampass, munged_dial); + if (!pdb_set_nt_passwd(sampass, smbntpwd)) + return False; + if (!pdb_set_lanman_passwd(sampass, smblmpwd)) + return False; + + /* pdb_set_unknown_3(sampass, unknown3); */ + /* pdb_set_unknown_5(sampass, unknown5); */ + /* pdb_set_unknown_6(sampass, unknown6); */ + + pdb_set_hours(sampass, hours); + + return True; +} + +/********************************************************************** +Initialize SAM_ACCOUNT from an LDAP query +(Based on init_buffer_from_sam in pdb_tdb.c) +*********************************************************************/ +static BOOL init_ldap_from_sam (LDAPMod *** mods, int ldap_state, SAM_ACCOUNT * sampass) +{ + pstring temp; + + *mods = NULL; + + /* + * took out adding "objectclass: sambaAccount" + * do this on a per-mod basis + */ + + + make_a_mod(mods, ldap_state, "uid", pdb_get_username(sampass)); + DEBUG(2, ("Setting entry for user: %s\n", pdb_get_username(sampass))); + + /* not sure about using this for the nt_username */ + make_a_mod(mods, ldap_state, "sambaDomain", pdb_get_domain(sampass)); + + slprintf(temp, sizeof(temp) - 1, "%i", pdb_get_uid(sampass)); + make_a_mod(mods, ldap_state, "uidNumber", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass)); + make_a_mod(mods, ldap_state, "pwdLastSet", temp); + + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass)); + make_a_mod(mods, ldap_state, "logonTime", temp); + + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass)); + make_a_mod(mods, ldap_state, "logoffTime", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass)); + make_a_mod(mods, ldap_state, "kickoffTime", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass)); + make_a_mod(mods, ldap_state, "pwdCanChange", temp); + + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass)); + make_a_mod(mods, ldap_state, "pwdMustChange", temp); + + /* displayName, cn, and gecos should all be the same + * most easily accomplished by giving them the same OID + * gecos isn't set here b/c it should be handled by the + * add-user script + */ + + make_a_mod(mods, ldap_state, "displayName", pdb_get_fullname(sampass)); + make_a_mod(mods, ldap_state, "cn", pdb_get_fullname(sampass)); + + make_a_mod(mods, ldap_state, "smbHome", pdb_get_homedir(sampass)); + make_a_mod(mods, ldap_state, "homeDrive", pdb_get_dirdrive(sampass)); + make_a_mod(mods, ldap_state, "scriptPath", pdb_get_logon_script(sampass)); + make_a_mod(mods, ldap_state, "profilePath", pdb_get_profile_path(sampass)); + make_a_mod(mods, ldap_state, "description", pdb_get_acct_desc(sampass)); + make_a_mod(mods, ldap_state, "userWorkstations", pdb_get_workstations(sampass)); + + slprintf(temp, sizeof(temp) - 1, "%i", sampass->user_rid); + make_a_mod(mods, ldap_state, "rid", temp); + + slprintf(temp, sizeof(temp) - 1, "%i", sampass->group_rid); + make_a_mod(mods, ldap_state, "primaryGroupID", temp); + + /* FIXME: Hours stuff goes in LDAP */ + pdb_sethexpwd (temp, pdb_get_lanman_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_state, "lmPassword", temp); + pdb_sethexpwd (temp, pdb_get_nt_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_state, "ntPassword", temp); + make_a_mod (mods, ldap_state, "acctFlags", pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN)); + + return True; +} + +/********************************************************************** +Connect to LDAP server for password enumeration +*********************************************************************/ +BOOL pdb_setsampwent(BOOL update) +{ + int rc; + pstring filter; + + if (!ldap_open_connection(&global_ldap_ent.ldap_struct)) + { + return False; + } + if (!ldap_connect_system(global_ldap_ent.ldap_struct)) + { + ldap_unbind(global_ldap_ent.ldap_struct); + return False; + } + + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", "*", sizeof(pstring)); + + rc = ldap_search_s(global_ldap_ent.ldap_struct, lp_ldap_suffix(), + LDAP_SCOPE_SUBTREE, filter, NULL, 0, + &global_ldap_ent.result); + + if (rc != LDAP_SUCCESS) + { + DEBUG(0, ("LDAP search failed: %s\n", ldap_err2string(rc))); + DEBUG(3, ("Query was: %s, %s\n", lp_ldap_suffix(), filter)); + ldap_msgfree(global_ldap_ent.result); + ldap_unbind(global_ldap_ent.ldap_struct); + global_ldap_ent.ldap_struct = NULL; + global_ldap_ent.result = NULL; + return False; + } + + DEBUG(2, ("pdb_setsampwent: %d entries in the base!\n", + ldap_count_entries(global_ldap_ent.ldap_struct, + global_ldap_ent.result))); + + global_ldap_ent.entry = ldap_first_entry(global_ldap_ent.ldap_struct, + global_ldap_ent.result); + + return True; +} + +/********************************************************************** +End enumeration of the LDAP password list +*********************************************************************/ +void pdb_endsampwent(void) +{ + if (global_ldap_ent.ldap_struct && global_ldap_ent.result) + { + ldap_msgfree(global_ldap_ent.result); + ldap_unbind(global_ldap_ent.ldap_struct); + global_ldap_ent.ldap_struct = NULL; + global_ldap_ent.result = NULL; + } +} + +/********************************************************************** +Get the next entry in the LDAP password database +*********************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT * user) +{ + if (!global_ldap_ent.entry) + return False; + + global_ldap_ent.entry = ldap_next_entry(global_ldap_ent.ldap_struct, + global_ldap_ent.entry); + + if (global_ldap_ent.entry != NULL) + { + return init_sam_from_ldap(user, global_ldap_ent.ldap_struct, + global_ldap_ent.entry); + } + return False; +} + +/********************************************************************** +Get SAM_ACCOUNT entry from LDAP by username +*********************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT * user, char *sname) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_name(ldap_struct, sname, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this user [%s] count=%d\n", sname, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + +/********************************************************************** +Get SAM_ACCOUNT entry from LDAP by rid +*********************************************************************/ +BOOL pdb_getsampwrid(SAM_ACCOUNT * user, uint32 rid) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_rid(ldap_struct, rid, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this rid [%i] count=%d\n", rid, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + +/********************************************************************** + Get SAM_ACCOUNT entry from LDAP by uid +*********************************************************************/ +BOOL pdb_getsampwuid(SAM_ACCOUNT * user, uid_t uid) +{ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + + if (!ldap_open_connection(&ldap_struct)) + return False; + + if (!ldap_connect_system(ldap_struct)) + { + ldap_unbind(ldap_struct); + return False; + } + if (ldap_search_one_user_by_uid(ldap_struct, uid, &result) != + LDAP_SUCCESS) + { + ldap_unbind(ldap_struct); + return False; + } + + if (ldap_count_entries(ldap_struct, result) < 1) + { + DEBUG(0, + ("We don't find this uid [%i] count=%d\n", uid, + ldap_count_entries(ldap_struct, result))); + ldap_unbind(ldap_struct); + return False; + } + entry = ldap_first_entry(ldap_struct, result); + if (entry) + { + init_sam_from_ldap(user, ldap_struct, entry); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return True; + } + else + { + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } +} + + +/********************************************************************** +Delete entry from LDAP for username +*********************************************************************/ +BOOL pdb_delete_sam_account(char *sname) +{ + int rc; + char *dn; + LDAP *ldap_struct; + LDAPMessage *entry; + LDAPMessage *result; + + if (!ldap_open_connection (&ldap_struct)) + return False; + + DEBUG (3, ("Deleting user %s from LDAP.\n", sname)); + + if (!ldap_connect_system (ldap_struct)) { + ldap_unbind (ldap_struct); + DEBUG(0, ("Failed to delete user %s from LDAP.\n", sname)); + return False; + } + + rc = ldap_search_one_user_by_name (ldap_struct, sname, &result); + if (ldap_count_entries (ldap_struct, result) == 0) { + DEBUG (0, ("User doesn't exit!\n")); + ldap_msgfree (result); + ldap_unbind (ldap_struct); + return False; + } + + entry = ldap_first_entry (ldap_struct, result); + dn = ldap_get_dn (ldap_struct, entry); + + rc = ldap_delete_s (ldap_struct, dn); + + ldap_memfree (dn); + if (rc != LDAP_SUCCESS) { + char *ld_error; + ldap_get_option (ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG (0,("failed to delete user with uid = %s with: %s\n\t%s\n", + sname, ldap_err2string (rc), ld_error)); + free (ld_error); + ldap_unbind (ldap_struct); + return False; + } + + DEBUG (2,("successfully deleted uid = %s from the LDAP database\n", sname)); + ldap_unbind (ldap_struct); + return True; +} + +/********************************************************************** +Update SAM_ACCOUNT +*********************************************************************/ +BOOL pdb_update_sam_account(SAM_ACCOUNT * newpwd, BOOL override) +{ + int rc; + char *dn; + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + LDAPMod **mods; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + return False; + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + rc = ldap_search_one_user_by_name(ldap_struct, + pdb_get_username(newpwd), &result); + + if (ldap_count_entries(ldap_struct, result) == 0) + { + DEBUG(0, ("No user to modify!\n")); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } + + init_ldap_from_sam(&mods, LDAP_MOD_REPLACE, newpwd); + + entry = ldap_first_entry(ldap_struct, result); + dn = ldap_get_dn(ldap_struct, entry); + + rc = ldap_modify_s(ldap_struct, dn, mods); + + if (rc != LDAP_SUCCESS) + { + char *ld_error; + ldap_get_option(ldap_struct, LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(0, + ("failed to modify user with uid = %s with: %s\n\t%s\n", + pdb_get_username(newpwd), ldap_err2string(rc), + ld_error)); + free(ld_error); + ldap_unbind(ldap_struct); + return False; + } + + DEBUG(2, + ("successfully modified uid = %s in the LDAP database\n", + pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return True; +} + +/********************************************************************** +Add SAM_ACCOUNT to LDAP +*********************************************************************/ +BOOL pdb_add_sam_account(SAM_ACCOUNT * newpwd) +{ + int rc; + pstring filter; + LDAP *ldap_struct; + LDAPMessage *result; + pstring dn; + LDAPMod **mods; + int ldap_op = LDAP_MOD_ADD; + + if (!ldap_open_connection(&ldap_struct)) /* open a connection to the server */ + { + return False; + } + + if (!ldap_connect_system(ldap_struct)) /* connect as system account */ + { + ldap_unbind(ldap_struct); + return False; + } + + if (pdb_get_username(newpwd) != NULL) { + slprintf (dn, sizeof (dn) - 1, "uid=%s,%s", + pdb_get_username(newpwd), lp_ldap_suffix ()); + } + else + { + return False; + } + + + rc = ldap_search_one_user_by_name (ldap_struct, pdb_get_username(newpwd), &result); + + if (ldap_count_entries(ldap_struct, result) != 0) + { + DEBUG(0,("User already in the base, with samba properties\n")); + ldap_msgfree(result); + ldap_unbind(ldap_struct); + return False; + } + ldap_msgfree(result); + + slprintf (filter, sizeof (filter) - 1, "uid=%s", pdb_get_username(newpwd)); + rc = ldap_search_one_user(ldap_struct, filter, &result); + if (ldap_count_entries(ldap_struct, result) == 1) + { + char *tmp; + LDAPMessage *entry; + DEBUG(3,("User exists without samba properties: adding them\n")); + ldap_op = LDAP_MOD_REPLACE; + entry = ldap_first_entry (ldap_struct, result); + tmp = ldap_get_dn (ldap_struct, entry); + slprintf (dn, sizeof (dn) - 1, "%s", tmp); + ldap_memfree (tmp); + } + else + { + DEBUG (3, ("More than one user with that uid exists: bailing out!\n")); + return False; + } + + ldap_msgfree(result); + + init_ldap_from_sam(&mods, ldap_op, newpwd); + make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "sambaAccount"); + + if (ldap_op == LDAP_MOD_REPLACE) { + rc = ldap_modify_s(ldap_struct, dn, mods); + } + else { + rc = ldap_add_s(ldap_struct, dn, mods); + } + + if (rc != LDAP_SUCCESS) + { + char *ld_error; + + ldap_get_option (ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG(0,("failed to modify user with uid = %s with: %s\n\t%s\n", + pdb_get_username(newpwd), ldap_err2string (rc), ld_error)); + free(ld_error); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return False; + } + + DEBUG(2,("added: uid = %s in the LDAP database\n", pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + ldap_unbind(ldap_struct); + return True; +} + +#else +void dummy_function(void); +void +dummy_function (void) +{ +} /* stop some compilers complaining */ +#endif diff --git a/source/passdb/pdb_nisplus.c b/source/passdb/pdb_nisplus.c new file mode 100644 index 00000000000..a86d5ecb680 --- /dev/null +++ b/source/passdb/pdb_nisplus.c @@ -0,0 +1,1404 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * Copyright (C) Benny Holmgren 1998 <bigfoot@astrakan.hgs.se> + * Copyright (C) Luke Kenneth Casson Leighton 1996-1998. + * Copyright (C) Toomas Soome <tsoome@ut.ee> 2001 + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program 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. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 675 + * Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +#ifdef WITH_NISPLUS_SAM + +#ifdef BROKEN_NISPLUS_INCLUDE_FILES + +/* + * The following lines are needed due to buggy include files + * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and + * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA. + * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as + * an enum in /usr/include/rpcsvc/nis.h. + */ + +#if defined(GROUP) +#undef GROUP +#endif + +#if defined(GROUP_OBJ) +#undef GROUP_OBJ +#endif + +#endif + +#include <rpcsvc/nis.h> + +extern int DEBUGLEVEL; +extern pstring samlogon_user; +extern BOOL sam_logon_in_ssb; + +struct nisp_enum_info +{ + nis_result *result; + int enum_entry; +}; + +static struct nisp_enum_info global_nisp_ent; +static VOLATILE sig_atomic_t gotalarm; + +/*************************************************************** + + the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are: + + name=S,nogw=r + uid=S,nogw=r + user_rid=S,nogw=r + smb_grpid=,nw+r + group_rid=,nw+r + acb=,nw+r + + lmpwd=C,nw=,g=r,o=rm + ntpwd=C,nw=,g=r,o=rm + + logon_t=,nw+r + logoff_t=,nw+r + kick_t=,nw+r + pwdlset_t=,nw+r + pwdlchg_t=,nw+r + pwdmchg_t=,nw+r + + full_name=,nw+r + home_dir=,nw+r + dir_drive=,nw+r + logon_script=,nw+r + profile_path=,nw+r + acct_desc=,nw+r + workstations=,nw+r + + hours=,nw+r + +****************************************************************/ + +#define NPF_NAME 0 +#define NPF_UID 1 +#define NPF_USER_RID 2 +#define NPF_SMB_GRPID 3 +#define NPF_GROUP_RID 4 +#define NPF_ACB 5 +#define NPF_LMPWD 6 +#define NPF_NTPWD 7 +#define NPF_LOGON_T 8 +#define NPF_LOGOFF_T 9 +#define NPF_KICK_T 10 +#define NPF_PWDLSET_T 11 +#define NPF_PWDCCHG_T 12 +#define NPF_PWDMCHG_T 13 +#define NPF_FULL_NAME 14 +#define NPF_HOME_DIR 15 +#define NPF_DIR_DRIVE 16 +#define NPF_LOGON_SCRIPT 17 +#define NPF_PROFILE_PATH 18 +#define NPF_ACCT_DESC 19 +#define NPF_WORKSTATIONS 20 +#define NPF_HOURS 21 + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/*************************************************************** + make_nisname_from_user_rid + ****************************************************************/ +static char *make_nisname_from_user_rid(uint32 rid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[user_rid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, rid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_uid + ****************************************************************/ +static char *make_nisname_from_uid(int uid, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[uid=", sizeof(nisname)-1); + slprintf(nisname, sizeof(nisname)-1, "%s%d", nisname, uid); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_name + ****************************************************************/ +static char *make_nisname_from_name(char *user_name, char *pfile) +{ + static pstring nisname; + + safe_strcpy(nisname, "[name=", sizeof(nisname)-1); + safe_strcat(nisname, user_name, sizeof(nisname) - strlen(nisname) - 1); + safe_strcat(nisname, "],", sizeof(nisname)-strlen(nisname)-1); + safe_strcat(nisname, pfile, sizeof(nisname)-strlen(nisname)-1); + + return nisname; +} + +/************************************************************************* + gets a NIS+ attribute + *************************************************************************/ +static void get_single_attribute(nis_object *new_obj, int col, + char *val, int len) +{ + int entry_len; + + if (new_obj == NULL || val == NULL) return; + + entry_len = ENTRY_LEN(new_obj, col); + if (len > entry_len) + { + len = entry_len; + } + + safe_strcpy(val, ENTRY_VAL(new_obj, col), len-1); +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ object. + ************************************************************************/ +static BOOL make_sam_from_nisp_object(SAM_ACCOUNT *pw_buf, nis_object *obj) +{ + char *ptr; + pstring full_name; /* this must be translated to dos code page */ + pstring acct_desc; /* this must be translated to dos code page */ + pstring home_dir; /* set default value from smb.conf for user */ + pstring home_drive; /* set default value from smb.conf for user */ + pstring logon_script; /* set default value from smb.conf for user */ + pstring profile_path; /* set default value from smb.conf for user */ + pstring hours; + int hours_len; + unsigned char smbpwd[16]; + unsigned char smbntpwd[16]; + + + /* + * time values. note: this code assumes 32bit time_t! + */ + + /* Don't change these timestamp settings without a good reason. They are + important for NT member server compatibility. */ + + pdb_set_logon_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_LOGON_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LNT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_logon_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_logoff_time(pw_buf, get_time_t_max()); + ptr = (uchar *)ENTRY_VAL(obj, NPF_LOGOFF_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LOT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_logoff_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_kickoff_time(pw_buf, get_time_t_max()); + ptr = (uchar *)ENTRY_VAL(obj, NPF_KICK_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "KOT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_kickoff_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_last_set_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDLSET_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "LCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_last_set_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_can_change_time(pw_buf, (time_t)0); + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDCCHG_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "CCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_can_change_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + pdb_set_pass_must_change_time(pw_buf, get_time_t_max()); /* Password never expires. */ + ptr = (uchar *)ENTRY_VAL(obj, NPF_PWDMCHG_T); + if(ptr && *ptr && (StrnCaseCmp(ptr, "MCT-", 4)==0)) { + int i; + ptr += 4; + for(i = 0; i < 8; i++) { + if(ptr[i] == '\0' || !isxdigit(ptr[i])) + break; + } + if(i == 8) { + pdb_set_pass_must_change_time(pw_buf, (time_t)strtol(ptr, NULL, 16)); + } + } + + /* string values */ + pdb_set_username(pw_buf, ENTRY_VAL(obj, NPF_NAME)); + pdb_set_domain(pw_buf, lp_workgroup()); + /* pdb_set_nt_username() -- cant set it here... */ + + get_single_attribute(obj, NPF_FULL_NAME, full_name, sizeof(pstring)); + unix_to_dos(full_name, True); + pdb_set_fullname(pw_buf, full_name); + + pdb_set_acct_ctrl(pw_buf, pdb_decode_acct_ctrl(ENTRY_VAL(obj, + NPF_ACB))); + + get_single_attribute(obj, NPF_ACCT_DESC, acct_desc, sizeof(pstring)); + unix_to_dos(acct_desc, True); + pdb_set_acct_desc(pw_buf, acct_desc); + + pdb_set_workstations(pw_buf, ENTRY_VAL(obj, NPF_WORKSTATIONS)); + pdb_set_munged_dial(pw_buf, NULL); + + pdb_set_uid(pw_buf, atoi(ENTRY_VAL(obj, NPF_UID))); + pdb_set_gid(pw_buf, atoi(ENTRY_VAL(obj, NPF_SMB_GRPID))); + pdb_set_user_rid(pw_buf, atoi(ENTRY_VAL(obj, NPF_USER_RID))); + pdb_set_group_rid(pw_buf, atoi(ENTRY_VAL(obj, NPF_GROUP_RID))); + + /* values, must exist for user */ + if( !(pdb_get_acct_ctrl(pw_buf) & ACB_WSTRUST) ) { + /* FIXME!! This doesn't belong here. + Should be set in net_sam_logon() + --jerry */ + pstrcpy(samlogon_user, pdb_get_username(pw_buf)); + + get_single_attribute(obj, NPF_HOME_DIR, home_dir, sizeof(pstring)); + if( !(home_dir && *home_dir) ) + pstrcpy(home_dir, lp_logon_home()); + pdb_set_homedir(pw_buf, home_dir); + + get_single_attribute(obj, NPF_DIR_DRIVE, home_drive, sizeof(pstring)); + if( !(home_drive && *home_drive) ) + pstrcpy(home_drive, lp_logon_drive()); + pdb_set_dir_drive(pw_buf, home_drive); + + get_single_attribute(obj, NPF_LOGON_SCRIPT, logon_script, + sizeof(pstring)); + if( !(logon_script && *logon_script) ) + pstrcpy(logon_script, lp_logon_script()); + pdb_set_logon_script(pw_buf, logon_script); + + get_single_attribute(obj, NPF_PROFILE_PATH, profile_path, + sizeof(pstring)); + if( !(profile_path && *profile_path) ) + pstrcpy(profile_path, lp_logon_path()); + pdb_set_profile_path(pw_buf, profile_path); + } else { + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + pdb_set_group_rid (pw_buf, DOMAIN_GROUP_RID_USERS); + } + + /* Check the lanman password column. */ + ptr = (char *)ENTRY_VAL(obj, NPF_LMPWD); + pdb_set_lanman_passwd(pw_buf, NULL); + + if (!strncasecmp(ptr, "NO PASSWORD", 11)) { + pdb_set_acct_ctrl(pw_buf, pdb_get_acct_ctrl(pw_buf) | ACB_PWNOTREQ); + } else { + if (strlen(ptr) != 32 || !pdb_gethexpwd(ptr, smbpwd)) { + DEBUG(0, ("malformed LM pwd entry: %s.\n", + pdb_get_username(pw_buf))); + return False; + } + pdb_set_lanman_passwd(pw_buf, smbpwd); + } + + /* Check the NT password column. */ + ptr = ENTRY_VAL(obj, NPF_NTPWD); + pdb_set_nt_passwd(pw_buf, NULL); + + if (!(pdb_get_acct_ctrl(pw_buf) & ACB_PWNOTREQ) && + strncasecmp(ptr, "NO PASSWORD", 11)) { + if (strlen(ptr) != 32 || !pdb_gethexpwd(ptr, smbntpwd)) { + DEBUG(0, ("malformed NT pwd entry: + uid = %d.\n", + pdb_get_uid(pw_buf))); + return False; + } + pdb_set_nt_passwd(pw_buf, smbntpwd); + } + + pdb_set_unknown_3(pw_buf, 0xffffff); /* don't know */ + pdb_set_logons_divs(pw_buf, 168); /* hours per week */ + + if( (hours_len = ENTRY_LEN(obj, NPF_HOURS)) == 21 ) { + memcpy(hours, ENTRY_VAL(obj, NPF_HOURS), hours_len); + } else { + hours_len = 21; /* 21 times 8 bits = 168 */ + /* available at all hours */ + memset(hours, 0xff, hours_len); + } + pdb_set_hours_len(pw_buf, hours_len); + pdb_set_hours(pw_buf, hours); + + pdb_set_unknown_5(pw_buf, 0x00020000); /* don't know */ + pdb_set_unknown_6(pw_buf, 0x000004ec); /* don't know */ + + return True; +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ result. + ************************************************************************/ +static BOOL make_sam_from_nisresult(SAM_ACCOUNT *pw_buf, nis_result *result) +{ + if (pw_buf == NULL || result == NULL) return False; + + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) + { + DEBUG(0, ("NIS+ lookup failure: %s\n", + nis_sperrno(result->status))); + return False; + } + + /* User not found. */ + if (NIS_RES_NUMOBJ(result) <= 0) + { + DEBUG(10, ("user not found in NIS+\n")); + return False; + } + + if (NIS_RES_NUMOBJ(result) > 1) + { + DEBUG(10, ("WARNING: Multiple entries for user in NIS+ table!\n")); + } + + /* Grab the first hit. */ + return make_sam_from_nisp_object(pw_buf, &NIS_RES_OBJECT(result)[0]); +} + +/************************************************************************* + sets a NIS+ attribute + *************************************************************************/ +static void set_single_attribute(nis_object *new_obj, int col, + char *val, int len, int flags) +{ + if (new_obj == NULL) return; + + ENTRY_VAL(new_obj, col) = val; + ENTRY_LEN(new_obj, col) = len+1; + + if (flags != 0) + { + new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags; + } +} + +/*************************************************************** + copy or modify nis object. this object is used to add or update + nisplus table entry. + ****************************************************************/ +static BOOL init_nisp_from_sam(nis_object *obj, SAM_ACCOUNT *sampass, + nis_object *old) +{ + /* + * Fill nis_object for entry add or update. + * if we are updateing, we have to find out differences and set + * EN_MODIFIED flag. also set need_to_modify to trigger + * nis_modify_entry() call in pdb_update_sam_account(). + * + * TODO: + * get data from SAM + * if (modify) get data from nis_object, compare and store if + * different + set EN_MODIFIED and need_to_modify + * else + * store + */ + BOOL need_to_modify = False; + char *name; /* from SAM */ + /* these must be static or allocate and free entry columns! */ + static fstring uid; /* from SAM */ + static fstring user_rid; /* from SAM */ + static fstring gid; /* from SAM */ + static fstring group_rid; /* from SAM */ + char *acb; /* from SAM */ + static fstring smb_passwd; /* from SAM */ + static fstring smb_nt_passwd; /* from SAM */ + static fstring logon_t; /* from SAM */ + static fstring logoff_t; /* from SAM */ + static fstring kickoff_t; /* from SAM */ + static fstring pwdlset_t; /* from SAM */ + static fstring pwdlchg_t; /* from SAM */ + static fstring pwdmchg_t; /* from SAM */ + static fstring full_name; /* from SAM */ + static fstring acct_desc; /* from SAM */ + static char empty[1]; /* just an empty string */ + + + name = pdb_get_username(sampass); + slprintf(uid, sizeof(uid)-1, "%u", pdb_get_uid(sampass)); + slprintf(user_rid, sizeof(user_rid)-1, "%u", + pdb_get_user_rid(sampass)? pdb_get_user_rid(sampass): + pdb_uid_to_user_rid(pdb_get_uid(sampass))); + slprintf(gid, sizeof(gid)-1, "%u", pdb_get_gid(sampass)); + slprintf(group_rid, sizeof(group_rid)-1, "%u", + pdb_get_group_rid(sampass)? pdb_get_group_rid(sampass): + pdb_gid_to_group_rid(pdb_get_gid(sampass))); + acb = pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN); + pdb_sethexpwd (smb_passwd, pdb_get_lanman_passwd(sampass), + pdb_get_acct_ctrl(sampass)); + pdb_sethexpwd (smb_nt_passwd, pdb_get_nt_passwd(sampass), + pdb_get_acct_ctrl(sampass)); + slprintf(logon_t, 13, "LNT-%08X", + (uint32)pdb_get_logon_time(sampass)); + slprintf(logoff_t, 13, "LOT-%08X", + (uint32)pdb_get_logoff_time(sampass)); + slprintf(kickoff_t, 13, "KOT-%08X", + (uint32)pdb_get_kickoff_time(sampass)); + slprintf(pwdlset_t, 13, "LCT-%08X", + (uint32)pdb_get_pass_last_set_time(sampass)); + slprintf(pwdlchg_t, 13, "CCT-%08X", + (uint32)pdb_get_pass_can_change_time(sampass)); + slprintf(pwdmchg_t, 13, "MCT-%08X", + (uint32)pdb_get_pass_must_change_time(sampass)); + safe_strcpy(full_name, pdb_get_fullname(sampass), sizeof(full_name)-1); + dos_to_unix(full_name, True); + safe_strcpy(acct_desc, pdb_get_acct_desc(sampass), sizeof(acct_desc)-1); + dos_to_unix(acct_desc, True); + + if( old ) { + /* name */ + if(strcmp(ENTRY_VAL(old, NPF_NAME), name)) + { + need_to_modify = True; + set_single_attribute(obj, NPF_NAME, name, strlen(name), + EN_MODIFIED); + } + + + /* uid */ + if(pdb_get_uid(sampass) != -1) { + if(!ENTRY_VAL(old, NPF_UID) || strcmp(ENTRY_VAL(old, NPF_UID), uid)) + { + need_to_modify = True; + set_single_attribute(obj, NPF_UID, uid, + strlen(uid), EN_MODIFIED); + } + } + + /* user_rid */ + if (pdb_get_user_rid(sampass)) { + if(!ENTRY_VAL(old, NPF_USER_RID) || + strcmp(ENTRY_VAL(old, NPF_USER_RID), user_rid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_USER_RID, user_rid, + strlen(user_rid), EN_MODIFIED); + } + } + + /* smb_grpid */ + if (pdb_get_gid(sampass) != -1) { + if(!ENTRY_VAL(old, NPF_SMB_GRPID) || + strcmp(ENTRY_VAL(old, NPF_SMB_GRPID), gid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_SMB_GRPID, gid, + strlen(gid), EN_MODIFIED); + } + } + + /* group_rid */ + if (pdb_get_group_rid(sampass)) { + if(!ENTRY_VAL(old, NPF_GROUP_RID) || + strcmp(ENTRY_VAL(old, NPF_GROUP_RID), group_rid) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_GROUP_RID, group_rid, + strlen(group_rid), EN_MODIFIED); + } + } + + /* acb */ + if (!ENTRY_VAL(old, NPF_ACB) || + strcmp(ENTRY_VAL(old, NPF_ACB), acb)) { + need_to_modify = True; + set_single_attribute(obj, NPF_ACB, acb, strlen(acb), EN_MODIFIED); + } + + /* lmpwd */ + if(!ENTRY_VAL(old, NPF_LMPWD) || + strcmp(ENTRY_VAL(old, NPF_LMPWD), smb_passwd) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_LMPWD, smb_passwd, + strlen(smb_passwd), EN_CRYPT|EN_MODIFIED); + } + + /* ntpwd */ + if(!ENTRY_VAL(old, NPF_NTPWD) || + strcmp(ENTRY_VAL(old, NPF_NTPWD), smb_nt_passwd) ) { + need_to_modify = True; + set_single_attribute(obj, NPF_NTPWD, smb_nt_passwd, + strlen(smb_nt_passwd), EN_CRYPT|EN_MODIFIED); + } + + /* logon_t */ + if( pdb_get_logon_time(sampass) && + (!ENTRY_VAL(old, NPF_LOGON_T) || + strcmp(ENTRY_VAL(old, NPF_LOGON_T), logon_t ))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGON_T, logon_t, + strlen(logon_t), EN_MODIFIED); + } + + /* logoff_t */ + if( pdb_get_logoff_time(sampass) && + (!ENTRY_VAL(old, NPF_LOGOFF_T) || + strcmp(ENTRY_VAL(old, NPF_LOGOFF_T), logoff_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGOFF_T, logoff_t, + strlen(logoff_t), EN_MODIFIED); + } + + /* kick_t */ + if( pdb_get_kickoff_time(sampass) && + (!ENTRY_VAL(old, NPF_KICK_T) || + strcmp(ENTRY_VAL(old, NPF_KICK_T), kickoff_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_KICK_T, kickoff_t, + strlen(kickoff_t), EN_MODIFIED); + } + + /* pwdlset_t */ + if( pdb_get_pass_last_set_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDLSET_T) || + strcmp(ENTRY_VAL(old, NPF_PWDLSET_T), pwdlset_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDLSET_T, pwdlset_t, + strlen(pwdlset_t), EN_MODIFIED); + } + + /* pwdlchg_t */ + if( pdb_get_pass_can_change_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDCCHG_T) || + strcmp(ENTRY_VAL(old, NPF_PWDCCHG_T), pwdlchg_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen(pwdlchg_t), EN_MODIFIED); + } + + /* pwdmchg_t */ + if( pdb_get_pass_must_change_time(sampass) && + (!ENTRY_VAL(old, NPF_PWDMCHG_T) || + strcmp(ENTRY_VAL(old, NPF_PWDMCHG_T), pwdmchg_t))) { + need_to_modify = True; + set_single_attribute(obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen(pwdmchg_t), EN_MODIFIED); + } + + /* full_name */ + /* must support set, unset and change */ + if ( (pdb_get_fullname(sampass) && + !ENTRY_VAL(old, NPF_FULL_NAME)) || + (ENTRY_VAL(old, NPF_FULL_NAME) && + !pdb_get_fullname(sampass)) || + (ENTRY_VAL(old, NPF_FULL_NAME) && + pdb_get_fullname(sampass) && + strcmp( ENTRY_VAL(old, NPF_FULL_NAME), full_name ))) { + need_to_modify = True; + set_single_attribute(obj, NPF_FULL_NAME, full_name, + strlen(full_name), EN_MODIFIED); + } + + /* home_dir */ + /* must support set, unset and change */ + if( (pdb_get_homedir(sampass) && + !ENTRY_VAL(old, NPF_HOME_DIR)) || + (ENTRY_VAL(old, NPF_HOME_DIR) && + !pdb_get_homedir(sampass)) || + (ENTRY_VAL(old, NPF_HOME_DIR) && + pdb_get_homedir(sampass) && + strcmp( ENTRY_VAL(old, NPF_HOME_DIR), + pdb_get_homedir(sampass)))) { + need_to_modify = True; + set_single_attribute(obj, NPF_HOME_DIR, pdb_get_homedir(sampass), + strlen(pdb_get_homedir(sampass)), EN_MODIFIED); + } + + /* dir_drive */ + /* must support set, unset and change */ + if( (pdb_get_dirdrive(sampass) && + !ENTRY_VAL(old, NPF_DIR_DRIVE)) || + (ENTRY_VAL(old, NPF_DIR_DRIVE) && + !pdb_get_dirdrive(sampass)) || + (ENTRY_VAL(old, NPF_DIR_DRIVE) && + pdb_get_dirdrive(sampass) && + strcmp( ENTRY_VAL(old, NPF_DIR_DRIVE), + pdb_get_dirdrive(sampass)))) { + need_to_modify = True; + set_single_attribute(obj, NPF_DIR_DRIVE, pdb_get_dirdrive(sampass), + strlen(pdb_get_dirdrive(sampass)), EN_MODIFIED); + } + + /* logon_script */ + /* must support set, unset and change */ + if( (pdb_get_logon_script(sampass) && + !ENTRY_VAL(old, NPF_LOGON_SCRIPT) || + (ENTRY_VAL(old, NPF_LOGON_SCRIPT) && + !pdb_get_logon_script(sampass)) || + ( ENTRY_VAL(old, NPF_LOGON_SCRIPT) && + pdb_get_logon_script(sampass) && + strcmp( ENTRY_VAL(old, NPF_LOGON_SCRIPT), + pdb_get_logon_script(sampass))))) { + need_to_modify = True; + set_single_attribute(obj, NPF_LOGON_SCRIPT, + pdb_get_logon_script(sampass), + strlen(pdb_get_logon_script(sampass)), + EN_MODIFIED); + } + + /* profile_path */ + /* must support set, unset and change */ + if( (pdb_get_profile_path(sampass) && + !ENTRY_VAL(old, NPF_PROFILE_PATH)) || + (ENTRY_VAL(old, NPF_PROFILE_PATH) && + !pdb_get_profile_path(sampass)) || + (ENTRY_VAL(old, NPF_PROFILE_PATH) && + pdb_get_profile_path(sampass) && + strcmp( ENTRY_VAL(old, NPF_PROFILE_PATH), + pdb_get_profile_path(sampass) ) )) { + need_to_modify = True; + set_single_attribute(obj, NPF_PROFILE_PATH, + pdb_get_profile_path(sampass), + strlen(pdb_get_profile_path(sampass)), + EN_MODIFIED); + } + + /* acct_desc */ + /* must support set, unset and change */ + if( (pdb_get_acct_desc(sampass) && + !ENTRY_VAL(old, NPF_ACCT_DESC)) || + (ENTRY_VAL(old, NPF_ACCT_DESC) && + !pdb_get_acct_desc(sampass)) || + (ENTRY_VAL(old, NPF_ACCT_DESC) && + pdb_get_acct_desc(sampass) && + strcmp( ENTRY_VAL(old, NPF_ACCT_DESC), acct_desc ) )) { + need_to_modify = True; + set_single_attribute(obj, NPF_ACCT_DESC, acct_desc, + strlen(acct_desc), EN_MODIFIED); + } + + /* workstations */ + /* must support set, unset and change */ + if ( (pdb_get_workstations(sampass) && + !ENTRY_VAL(old, NPF_WORKSTATIONS) ) || + (ENTRY_VAL(old, NPF_WORKSTATIONS) && + !pdb_get_workstations(sampass)) || + (ENTRY_VAL(old, NPF_WORKSTATIONS) && + pdb_get_workstations(sampass)) && + strcmp( ENTRY_VAL(old, NPF_WORKSTATIONS), + pdb_get_workstations(sampass))) { + need_to_modify = True; + set_single_attribute(obj, NPF_WORKSTATIONS, + pdb_get_workstations(sampass), + strlen(pdb_get_workstations(sampass)), + EN_MODIFIED); + } + + /* hours */ + if ((pdb_get_hours_len(sampass) != ENTRY_LEN(old, NPF_HOURS)) || + memcmp(pdb_get_hours(sampass), ENTRY_VAL(old, NPF_HOURS), + ENTRY_LEN(old, NPF_HOURS))) { + need_to_modify = True; + /* set_single_attribute will add 1 for len ... */ + set_single_attribute(obj, NPF_HOURS, pdb_get_hours(sampass), + pdb_get_hours_len(sampass)-1, EN_MODIFIED); + } + } else { + char *homedir, *dirdrive, *logon_script, *profile_path, *workstations; + + *empty = '\0'; /* empty string */ + + set_single_attribute(obj, NPF_NAME, name, strlen(name), 0); + set_single_attribute(obj, NPF_UID, uid, strlen(uid), 0); + set_single_attribute(obj, NPF_USER_RID, user_rid, + strlen(user_rid), 0); + set_single_attribute(obj, NPF_SMB_GRPID, gid, strlen(gid), 0); + set_single_attribute(obj, NPF_GROUP_RID, group_rid, + strlen(group_rid), 0); + set_single_attribute(obj, NPF_ACB, acb, strlen(acb), 0); + set_single_attribute(obj, NPF_LMPWD, smb_passwd, + strlen(smb_passwd), EN_CRYPT); + set_single_attribute(obj, NPF_NTPWD, smb_nt_passwd, + strlen(smb_nt_passwd), EN_CRYPT); + set_single_attribute(obj, NPF_LOGON_T, logon_t, + strlen(logon_t), 0); + set_single_attribute(obj, NPF_LOGOFF_T, logoff_t, + strlen(logoff_t), 0); + set_single_attribute(obj, NPF_KICK_T, kickoff_t, + strlen(kickoff_t),0); + set_single_attribute(obj, NPF_PWDLSET_T, pwdlset_t, + strlen(pwdlset_t), 0); + set_single_attribute(obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen(pwdlchg_t), 0); + set_single_attribute(obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen(pwdmchg_t), 0); + set_single_attribute(obj, NPF_FULL_NAME , + full_name, strlen(full_name), 0); + + if(!(homedir = pdb_get_homedir(sampass))) + homedir = empty; + + set_single_attribute(obj, NPF_HOME_DIR, + homedir, strlen(homedir), 0); + + if(!(dirdrive = pdb_get_dirdrive(sampass))) + dirdrive = empty; + + set_single_attribute(obj, NPF_DIR_DRIVE, + dirdrive, strlen(dirdrive), 0); + + if(!(logon_script = pdb_get_logon_script(sampass))) + logon_script = empty; + + set_single_attribute(obj, NPF_LOGON_SCRIPT, + logon_script, strlen(logon_script), 0); + + if(!(profile_path = pdb_get_profile_path(sampass))) + profile_path = empty; + + set_single_attribute(obj, NPF_PROFILE_PATH, + profile_path, strlen(profile_path), 0); + + set_single_attribute(obj, NPF_ACCT_DESC, + acct_desc, strlen(acct_desc), 0); + + if(!(workstations = pdb_get_workstations(sampass))) + workstations = empty; + + set_single_attribute(obj, NPF_WORKSTATIONS, + workstations, strlen(workstations), 0); + + /* set_single_attribute will add 1 for len ... */ + set_single_attribute(obj, NPF_HOURS, + pdb_get_hours(sampass), + pdb_get_hours_len(sampass)-1, 0); + } + + return need_to_modify; +} + +/*************************************************************** + calls nis_list, returns results. + ****************************************************************/ +static nis_result *nisp_get_nis_list(char *nis_name, uint_t flags) +{ + nis_result *result; + int i; + + if( ! flags) + flags = FOLLOW_LINKS|FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP; + + for(i = 0; i<2;i++ ) { + alarm(60); /* hopefully ok for long searches */ + result = nis_list(nis_name, flags,NULL,NULL); + + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) + { + DEBUG(0,("NIS+ lookup time out\n")); + nis_freeresult(result); + return NULL; + } + if( !(flags & MASTER_ONLY) && NIS_RES_NUMOBJ(result) <= 0 ) { + /* nis replicas are not in sync perhaps? + * this can happen, if account was just added. + */ + DEBUG(10,("will try master only\n")); + nis_freeresult(result); + flags |= MASTER_ONLY; + } else + break; + } + return result; +} + +/*************************************************************** + Start to enumerate the nisplus passwd list. + ****************************************************************/ +BOOL pdb_setsampwent(BOOL update) +{ + char *sp, * p = lp_smb_passwd_file(); + pstring pfiletmp; + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + pdb_endsampwent(); /* just in case */ + global_nisp_ent.result = nisp_get_nis_list( pfiletmp, 0 ); + global_nisp_ent.enum_entry = 0; + return global_nisp_ent.result != NULL ? True : False; +} + +/*************************************************************** + End enumeration of the nisplus passwd list. +****************************************************************/ +void pdb_endsampwent(void) +{ + if( global_nisp_ent.result ) + nis_freeresult(global_nisp_ent.result); + global_nisp_ent.result = NULL; + global_nisp_ent.enum_entry = 0; +} + +/************************************************************************* + Routine to return the next entry in the nisplus passwd list. + *************************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + int enum_entry = (int)(global_nisp_ent.enum_entry); + nis_result *result = global_nisp_ent.result; + + if (user==NULL) { + DEBUG(0,("SAM_ACCOUNT is NULL.\n")); + return False; + } + + if (result == NULL || + enum_entry < 0 || enum_entry >= (NIS_RES_NUMOBJ(result) - 1)) + { + return False; + } + + if(!make_sam_from_nisp_object(user, &NIS_RES_OBJECT(result)[enum_entry]) ) + { + DEBUG(0,("Bad SAM_ACCOUNT entry returned from NIS+!\n")); + return False; + } + (int)(global_nisp_ent.enum_entry)++; + return True; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT * user, char *sname) +{ + /* Static buffers we will return. */ + nis_result *result = NULL; + pstring nisname; + BOOL ret; + char *pfile = lp_smb_passwd_file(); + int i; + + if (!*pfile) + { + DEBUG(0, ("No SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", sname, pfile); + DEBUG(10, ("search by nisname: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwrid(SAM_ACCOUNT * user, uint32 rid) +{ + nis_result *result; + char *nisname; + BOOL ret; + char *sp, *p = lp_smb_passwd_file(); + pstring pfiletmp; + + if (!*p) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + nisname = make_nisname_from_user_rid(rid, pfiletmp); + + DEBUG(10, ("search by rid: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to search the nisplus passwd file for an entry matching the username + *************************************************************************/ +BOOL pdb_getsampwuid(SAM_ACCOUNT * user, uid_t uid) +{ + nis_result *result; + char *nisname; + BOOL ret; + char *sp, *p = lp_smb_passwd_file(); + pstring pfiletmp; + + if (!*p) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + + if( (sp = strrchr( p, '/' )) ) + safe_strcpy(pfiletmp, sp+1, sizeof(pfiletmp)-1); + else + safe_strcpy(pfiletmp, p, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", sizeof(pfiletmp)-strlen(pfiletmp)-1); + + nisname = make_nisname_from_uid(uid, pfiletmp); + + DEBUG(10, ("search by uid: %s\n", nisname)); + + /* Search the table. */ + + if(!(result = nisp_get_nis_list(nisname, 0))) + { + return False; + } + + ret = make_sam_from_nisresult(user, result); + nis_freeresult(result); + + return ret; +} + +/************************************************************************* + Routine to remove entry from the nisplus smbpasswd table + *************************************************************************/ +BOOL pdb_delete_sam_account(char *sname) +{ + char *pfile = lp_smb_passwd_file(); + pstring nisname; + nis_result *result, *delresult; + nis_object *obj; + int i; + + if (!*pfile) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", sname, pfile); + + /* Search the table. */ + + if( !(result = nisp_get_nis_list(nisname, + MASTER_ONLY|FOLLOW_LINKS|FOLLOW_PATH|\ + EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + + if(result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) { + /* User not found. */ + DEBUG(0,("user not found in NIS+\n")); + nis_freeresult(result); + return False; + } + + obj = NIS_RES_OBJECT(result); + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.%s", sname, obj->zo_name, + obj->zo_domain); + + DEBUG(10, ("removing name: %s\n", nisname)); + delresult = nis_remove_entry(nisname, obj, + MASTER_ONLY|REM_MULTIPLE|ALL_RESULTS|FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + + nis_freeresult(result); + + if(delresult->status != NIS_SUCCESS) { + DEBUG(0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno(delresult->status))); + nis_freeresult(delresult); + return False; + } + nis_freeresult(delresult); + return True; +} + +/************************************************************************ + Routine to add an entry to the nisplus passwd file. +*************************************************************************/ +BOOL pdb_add_sam_account(SAM_ACCOUNT * newpwd) +{ + int local_user = 0; + char *pfile; + pstring pfiletmp; + char *nisname; + nis_result *result = NULL, + *tblresult = NULL; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + + /* + * 1. find user domain. + * a. try nis search in passwd.org_dir - if found use domain from result. + * b. try getpwnam. this may be needed if user is defined + * in /etc/passwd file (or elsewere) and not in passwd.org_dir. + * if found, use host default domain. + * c. exit with False - no such user. + * + * 2. add user + * a. find smbpasswd table + * search pfile in user domain if not found, try host default + * domain. + * b. smbpasswd domain is found, fill data and add entry. + * + * pfile should contain ONLY table name, org_dir will be concated. + * so, at first we will clear path prefix from pfile, and + * then we will use pfiletmp as playground to put together full + * nisname string. + * such approach will make it possible to specify samba private dir + * AND still use NIS+ table. as all domain related data is normally + * stored in org_dir.DOMAIN, this should be ok do do. + */ + + pfile = lp_smb_passwd_file(); + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + /* + * Check if user is already there. + */ + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", + sizeof(pfiletmp)-strlen(pfiletmp)-1); + + if(pdb_get_username(newpwd) != NULL) { + nisname = make_nisname_from_name(pdb_get_username(newpwd), + pfiletmp); + } else { + return False; + } + + if(!(result = nisp_get_nis_list(nisname, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + if (result->status != NIS_SUCCESS && + result->status != NIS_NOTFOUND) { + DEBUG(3, ( "nis_list failure: %s: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(result); + return False; + } + + if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ(result) > 0) + { + DEBUG(3, ("User already exists in NIS+ password db: %s\n", + pfile)); + nis_freeresult(result); + return False; + } + + nis_freeresult(result); /* no such user, free results */ + + /* + * check for user in unix password database. we need this to get + * domain, where smbpasswd entry should be stored. + */ + + nisname = make_nisname_from_name(pdb_get_username(newpwd), + "passwd.org_dir"); + + result = nisp_get_nis_list(nisname, + MASTER_ONLY|FOLLOW_LINKS|FOLLOW_PATH|\ + EXPAND_NAME|HARD_LOOKUP); + + if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) + { + DEBUG(3, ("nis_list failure: %s: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(result); + + if (!sys_getpwnam(pdb_get_username(newpwd))) { + /* no such user in system! */ + return False; + } + /* + * user is defined, but not in passwd.org_dir. + */ + local_user = 1; + } else { + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".", sizeof(pfiletmp)-strlen(pfiletmp)-1); + safe_strcat(pfiletmp, NIS_RES_OBJECT(result)->zo_domain, + sizeof(pfiletmp)-strlen(pfiletmp)-1); + nis_freeresult(result); /* not needed any more */ + + tblresult = nisp_get_nis_list(pfiletmp, + MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + } + + if (local_user || tblresult->status != NIS_SUCCESS) + { + /* + * no user domain or + * smbpasswd table not found in user domain, fallback to + * default domain. + */ + if (!local_user) /* free previous failed search result */ + nis_freeresult(tblresult); + + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".org_dir", + sizeof(pfiletmp)-strlen(pfiletmp)-1); + tblresult = nis_lookup(pfiletmp, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP); + if (tblresult->status != NIS_SUCCESS) + { + /* still nothing. bail out */ + nis_freeresult(tblresult); + DEBUG(3, ( "nis_lookup failure: %s\n", + nis_sperrno(tblresult->status))); + return False; + } + /* we need full name for nis_add_entry() */ + safe_strcpy(pfiletmp, pfile, sizeof(pfiletmp)-1); + safe_strcat(pfiletmp, ".", sizeof(pfiletmp)-strlen(pfiletmp)-1); + safe_strcat(pfiletmp, NIS_RES_OBJECT(tblresult)->zo_domain, + sizeof(pfiletmp)-strlen(pfiletmp)-1); + } + + memset((char *)&new_obj, 0, sizeof (new_obj)); + /* fill entry headers */ + /* we do not free these. */ + new_obj.zo_name = NIS_RES_OBJECT(tblresult)->zo_name; + new_obj.zo_owner = NIS_RES_OBJECT(tblresult)->zo_owner; + new_obj.zo_group = NIS_RES_OBJECT(tblresult)->zo_group; + new_obj.zo_domain = NIS_RES_OBJECT(tblresult)->zo_domain; + /* uints */ + new_obj.zo_access = NIS_RES_OBJECT(tblresult)->zo_access; + new_obj.zo_ttl = NIS_RES_OBJECT(tblresult)->zo_ttl; + + new_obj.zo_data.zo_type = ENTRY_OBJ; + new_obj.EN_data.en_type = + NIS_RES_OBJECT(tblresult)->TA_data.ta_type; + + ta_maxcol = NIS_RES_OBJECT(tblresult)->TA_data.ta_maxcol; + + if(!(ecol = (entry_col*)malloc(ta_maxcol*sizeof(entry_col)))) { + DEBUG(0, ("memory allocation failure\n")); + nis_freeresult(tblresult); + return False; + } + + memset((char *)ecol, 0, ta_maxcol*sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + init_nisp_from_sam(&new_obj, newpwd, NULL); + + DEBUG(10, ( "add NIS+ entry: %s\n", nisname)); + result = nis_add_entry(pfiletmp, &new_obj, 0); + + free(ecol); /* free allocated entry space */ + + if (result->status != NIS_SUCCESS) + { + DEBUG(3, ( "NIS+ table update failed: %s\n", + nisname, nis_sperrno(result->status))); + nis_freeresult(tblresult); + nis_freeresult(result); + return False; + } + + nis_freeresult(tblresult); + nis_freeresult(result); + + return True; +} + +/************************************************************************ + Routine to modify the nisplus passwd entry. +************************************************************************/ +BOOL pdb_update_sam_account(SAM_ACCOUNT * newpwd, BOOL override) +{ + nis_result *result, *addresult; + nis_object *obj; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + char *pfile = lp_smb_passwd_file(); + pstring nisname; + int i; + + if (!*pfile) + { + DEBUG(0, ("no SMB password file set\n")); + return False; + } + if( strrchr( pfile, '/') ) + pfile = strrchr( pfile, '/') + 1; + + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.org_dir", + pdb_get_username(newpwd), pfile); + + DEBUG(10, ("search by name: %s\n", nisname)); + + /* Search the table. */ + + if( !(result = nisp_get_nis_list(nisname, MASTER_ONLY|FOLLOW_LINKS|\ + FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP))) { + return False; + } + + if(result->status != NIS_SUCCESS || NIS_RES_NUMOBJ(result) <= 0) { + /* User not found. */ + DEBUG(0,("user not found in NIS+\n")); + nis_freeresult(result); + return False; + } + + obj = NIS_RES_OBJECT(result); + DEBUG(6,("entry found in %s\n", obj->zo_domain)); + + /* we must create new stub object with EN_MODIFIED flag. + this is because obj from result is going to be freed and + we do not want to break it or cause memory leaks or corruption. + */ + + memmove((char *)&new_obj, obj, sizeof (new_obj)); + ta_maxcol = obj->TA_data.ta_maxcol; + + if(!(ecol = (entry_col*)malloc(ta_maxcol*sizeof(entry_col)))) { + DEBUG(0, ("memory allocation failure\n")); + nis_freeresult(result); + return False; + } + + memmove((char *)ecol, obj->EN_data.en_cols.en_cols_val, + ta_maxcol*sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + if ( init_nisp_from_sam(&new_obj, newpwd, obj) == True ) { + slprintf(nisname, sizeof(nisname)-1, "[name=%s],%s.%s", + pdb_get_username(newpwd), pfile, obj->zo_domain); + + DEBUG(10, ("NIS+ table update: %s\n", nisname)); + addresult = + nis_modify_entry(nisname, &new_obj, + MOD_SAMEOBJ | FOLLOW_PATH | EXPAND_NAME | HARD_LOOKUP); + + if(addresult->status != NIS_SUCCESS) { + DEBUG(0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno(addresult->status))); + nis_freeresult(addresult); + nis_freeresult(result); + free(ecol); + return False; + } + + DEBUG(6,("password changed\n")); + nis_freeresult(addresult); + } else { + DEBUG(6,("nothing to change!\n")); + } + + free(ecol); + nis_freeresult(result); + + return True; +} + +#else + void nisplus_dummy_function(void); + void nisplus_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WITH_NISPLUSSAM */ + diff --git a/source/passdb/pdb_smbpasswd.c b/source/passdb/pdb_smbpasswd.c new file mode 100644 index 00000000000..45c983b1ca6 --- /dev/null +++ b/source/passdb/pdb_smbpasswd.c @@ -0,0 +1,1520 @@ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 + * Modified by Jeremy Allison 1995. + * Modified by Gerald (Jerry) Carter 2000-2001 + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program 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. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 675 + * Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +#ifdef WITH_SMBPASSWD_SAM + + +/* + smb_passwd is analogous to sam_passwd used everywhere + else. However, smb_passwd is limited to the information + stored by an smbpasswd entry + */ + +struct smb_passwd +{ + uid_t smb_userid; /* this is actually the unix uid_t */ + char *smb_name; /* username string */ + + unsigned char *smb_passwd; /* Null if no password */ + unsigned char *smb_nt_passwd; /* Null if no password */ + + uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ + time_t pass_last_set_time; /* password last set time */ +}; + + +extern pstring samlogon_user; +extern BOOL sam_logon_in_ssb; +extern struct passdb_ops pdb_ops; + +/* used for maintain locks on the smbpasswd file */ +static int pw_file_lock_depth; +static void *global_vp; + + +enum pwf_access_type { PWF_READ, PWF_UPDATE, PWF_CREATE }; + +/*************************************************************** + Lock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth) +{ + if (fd < 0) + return False; + + if(*plock_depth == 0) { + if (!do_file_lock(fd, secs, type)) { + DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n", + strerror(errno))); + return False; + } + } + + (*plock_depth)++; + + return True; +} + +/*************************************************************** + Unlock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +static BOOL pw_file_unlock(int fd, int *plock_depth) +{ + BOOL ret=True; + + if(*plock_depth == 1) + ret = do_file_lock(fd, 5, F_UNLCK); + + if (*plock_depth > 0) + (*plock_depth)--; + + if(!ret) + DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n", + strerror(errno))); + return ret; +} + + +/************************************************************** + Intialize a smb_passwd struct + *************************************************************/ + +static void pdb_init_smb(struct smb_passwd *user) +{ + if (user == NULL) + return; + ZERO_STRUCTP (user); + + user->pass_last_set_time = (time_t)0; +} + +/*************************************************************** + Internal fn to enumerate the smbpasswd list. Returns a void pointer + to ensure no modification outside this module. Checks for atomic + rename of smbpasswd file on update or create once the lock has + been granted to prevent race conditions. JRA. +****************************************************************/ + +static void *startsmbfilepwent(const char *pfile, enum pwf_access_type type, int *lock_depth) +{ + FILE *fp = NULL; + const char *open_mode = NULL; + int race_loop = 0; + int lock_type = F_RDLCK; + + if (!*pfile) { + DEBUG(0, ("startsmbfilepwent: No SMB password file set\n")); + return (NULL); + } + + switch(type) { + case PWF_READ: + open_mode = "rb"; + lock_type = F_RDLCK; + break; + case PWF_UPDATE: + open_mode = "r+b"; + lock_type = F_WRLCK; + break; + case PWF_CREATE: + /* + * Ensure atomic file creation. + */ + { + int i, fd = -1; + + for(i = 0; i < 5; i++) { + if((fd = sys_open(pfile, O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600))!=-1) + break; + sys_usleep(200); /* Spin, spin... */ + } + if(fd == -1) { + DEBUG(0,("startsmbfilepwent_internal: too many race conditions creating file %s\n", pfile)); + return NULL; + } + close(fd); + open_mode = "r+b"; + lock_type = F_WRLCK; + break; + } + } + + for(race_loop = 0; race_loop < 5; race_loop++) { + DEBUG(10, ("startsmbfilepwent_internal: opening file %s\n", pfile)); + + if((fp = sys_fopen(pfile, open_mode)) == NULL) { + DEBUG(0, ("startsmbfilepwent_internal: unable to open file %s. Error was %s\n", pfile, strerror(errno) )); + return NULL; + } + + if (!pw_file_lock(fileno(fp), lock_type, 5, lock_depth)) { + DEBUG(0, ("startsmbfilepwent_internal: unable to lock file %s. Error was %s\n", pfile, strerror(errno) )); + fclose(fp); + return NULL; + } + + /* + * Only check for replacement races on update or create. + * For read we don't mind if the data is one record out of date. + */ + + if(type == PWF_READ) { + break; + } else { + SMB_STRUCT_STAT sbuf1, sbuf2; + + /* + * Avoid the potential race condition between the open and the lock + * by doing a stat on the filename and an fstat on the fd. If the + * two inodes differ then someone did a rename between the open and + * the lock. Back off and try the open again. Only do this 5 times to + * prevent infinate loops. JRA. + */ + + if (sys_stat(pfile,&sbuf1) != 0) { + DEBUG(0, ("startsmbfilepwent_internal: unable to stat file %s. Error was %s\n", pfile, strerror(errno))); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + if (sys_fstat(fileno(fp),&sbuf2) != 0) { + DEBUG(0, ("startsmbfilepwent_internal: unable to fstat file %s. Error was %s\n", pfile, strerror(errno))); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + if( sbuf1.st_ino == sbuf2.st_ino) { + /* No race. */ + break; + } + + /* + * Race occurred - back off and try again... + */ + + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + } + } + + if(race_loop == 5) { + DEBUG(0, ("startsmbfilepwent_internal: too many race conditions opening file %s\n", pfile)); + return NULL; + } + + /* Set a buffer to do more efficient reads */ + setvbuf(fp, (char *)NULL, _IOFBF, 1024); + + /* Make sure it is only rw by the owner */ + if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) { + DEBUG(0, ("startsmbfilepwent_internal: failed to set 0600 permissions on password file %s. \ +Error was %s\n.", pfile, strerror(errno) )); + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + return NULL; + } + + /* We have a lock on the file. */ + return (void *)fp; +} + +/*************************************************************** + End enumeration of the smbpasswd list. +****************************************************************/ +static void endsmbfilepwent(void *vp, int *lock_depth) +{ + FILE *fp = (FILE *)vp; + + pw_file_unlock(fileno(fp), lock_depth); + fclose(fp); + DEBUG(7, ("endsmbfilepwent_internal: closed password file.\n")); +} + +/************************************************************************* + Routine to return the next entry in the smbpasswd list. + *************************************************************************/ + +static struct smb_passwd *getsmbfilepwent(void *vp) +{ + /* Static buffers we will return. */ + static struct smb_passwd pw_buf; + static pstring user_name; + static unsigned char smbpwd[16]; + static unsigned char smbntpwd[16]; + FILE *fp = (FILE *)vp; + char linebuf[256]; + unsigned char c; + unsigned char *p; + long uidval; + size_t linebuf_len; + + if(fp == NULL) { + DEBUG(0,("getsmbfilepwent: Bad password file pointer.\n")); + return NULL; + } + + pdb_init_smb(&pw_buf); + + pw_buf.acct_ctrl = ACB_NORMAL; + + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + linebuf[0] = '\0'; + + fgets(linebuf, 256, fp); + if (ferror(fp)) { + return NULL; + } + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + if ((linebuf_len = strlen(linebuf)) == 0) + continue; + + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') + break; + } + } else + linebuf[linebuf_len - 1] = '\0'; + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("getsmbfilepwent: got line |%s|\n", linebuf)); +#endif + if ((linebuf[0] == 0) && feof(fp)) { + DEBUG(4, ("getsmbfilepwent: end of file reached\n")); + break; + } + /* + * The line we have should be of the form :- + * + * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently + * ignored.... + * + * or, + * + * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored.... + * + * if Windows NT compatible passwords are also present. + * [Account type] is an ascii encoding of the type of account. + * LCT-(8 hex digits) is the time_t value of the last change time. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') { + DEBUG(6, ("getsmbfilepwent: skipping comment or blank line\n")); + continue; + } + p = (unsigned char *) strchr_m(linebuf, ':'); + if (p == NULL) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no :)\n")); + continue; + } + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + + /* Get smb uid. */ + + p++; /* Go past ':' */ + + if(*p == '-') { + DEBUG(0, ("getsmbfilepwent: uids in the smbpasswd file must not be negative.\n")); + continue; + } + + if (!isdigit(*p)) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (uid not number)\n")); + continue; + } + + uidval = atoi((char *) p); + + while (*p && isdigit(*p)) + p++; + + if (*p != ':') { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no : after uid)\n")); + continue; + } + + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + + /* Skip the ':' */ + p++; + + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("getsmbfilepwent: entry invalidated for user %s\n", user_name)); + pw_buf.smb_nt_passwd = NULL; + pw_buf.smb_passwd = NULL; + pw_buf.acct_ctrl |= ACB_DISABLED; + return &pw_buf; + } + + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("getsmbfilepwent: malformed password entry (passwd too short)\n")); + continue; + } + + if (p[32] != ':') { + DEBUG(0, ("getsmbfilepwent: malformed password entry (no terminating :)\n")); + continue; + } + + if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { + pw_buf.smb_passwd = NULL; + pw_buf.acct_ctrl |= ACB_PWNOTREQ; + } else { + if (!pdb_gethexpwd((char *)p, smbpwd)) { + DEBUG(0, ("getsmbfilepwent: Malformed Lanman password entry (non hex chars)\n")); + continue; + } + pw_buf.smb_passwd = smbpwd; + } + + /* + * Now check if the NT compatible password is + * available. + */ + pw_buf.smb_nt_passwd = NULL; + + p += 33; /* Move to the first character of the line after + the lanman password. */ + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + if (*p != '*' && *p != 'X') { + if(pdb_gethexpwd((char *)p,smbntpwd)) + pw_buf.smb_nt_passwd = smbntpwd; + } + p += 33; /* Move to the first character of the line after + the NT password. */ + } + + DEBUG(5,("getsmbfilepwent: returning passwd entry for user %s, uid %ld\n", + user_name, uidval)); + + if (*p == '[') + { + unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']'); + pw_buf.acct_ctrl = pdb_decode_acct_ctrl((char*)p); + + /* Must have some account type set. */ + if(pw_buf.acct_ctrl == 0) + pw_buf.acct_ctrl = ACB_NORMAL; + + /* Now try and get the last change time. */ + if(end_p) + p = end_p + 1; + if(*p == ':') { + p++; + if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) { + int i; + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) + break; + } + if(i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + pw_buf.pass_last_set_time = (time_t)strtol((char *)p, NULL, 16); + } + } + } + } else { + /* 'Old' style file. Fake up based on user name. */ + /* + * Currently trust accounts are kept in the same + * password file as 'normal accounts'. If this changes + * we will have to fix this code. JRA. + */ + if(pw_buf.smb_name[strlen(pw_buf.smb_name) - 1] == '$') { + pw_buf.acct_ctrl &= ~ACB_NORMAL; + pw_buf.acct_ctrl |= ACB_WSTRUST; + } + } + + return &pw_buf; + } + + DEBUG(5,("getsmbfilepwent: end of file reached.\n")); + return NULL; +} + +/************************************************************************ + Create a new smbpasswd entry - malloced space returned. +*************************************************************************/ + +static char *format_new_smbpasswd_entry(struct smb_passwd *newpwd) +{ + int new_entry_length; + char *new_entry; + char *p; + int i; + + new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2; + + if((new_entry = (char *)malloc( new_entry_length )) == NULL) { + DEBUG(0, ("format_new_smbpasswd_entry: Malloc failed adding entry for user %s.\n", newpwd->smb_name )); + return NULL; + } + + slprintf(new_entry, new_entry_length - 1, "%s:%u:", newpwd->smb_name, (unsigned)newpwd->smb_userid); + p = &new_entry[strlen(new_entry)]; + + if(newpwd->smb_passwd != NULL) { + for( i = 0; i < 16; i++) { + slprintf((char *)&p[i*2], new_entry_length - (p - new_entry) - 1, "%02X", newpwd->smb_passwd[i]); + } + } else { + i=0; + if(newpwd->acct_ctrl & ACB_PWNOTREQ) + safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + else + safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + } + + p += 32; + + *p++ = ':'; + + if(newpwd->smb_nt_passwd != NULL) { + for( i = 0; i < 16; i++) { + slprintf((char *)&p[i*2], new_entry_length - 1 - (p - new_entry), "%02X", newpwd->smb_nt_passwd[i]); + } + } else { + if(newpwd->acct_ctrl & ACB_PWNOTREQ) + safe_strcpy((char *)p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + else + safe_strcpy((char *)p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", new_entry_length - 1 - (p - new_entry)); + } + + p += 32; + + *p++ = ':'; + + /* Add the account encoding and the last change time. */ + slprintf((char *)p, new_entry_length - 1 - (p - new_entry), "%s:LCT-%08X:\n", + pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32)newpwd->pass_last_set_time); + + return new_entry; +} + +/************************************************************************ + Routine to add an entry to the smbpasswd file. +*************************************************************************/ + +static BOOL add_smbfilepwd_entry(struct smb_passwd *newpwd) +{ + char *pfile = lp_smb_passwd_file(); + struct smb_passwd *pwd = NULL; + FILE *fp = NULL; + int wr_len; + int fd; + size_t new_entry_length; + char *new_entry; + SMB_OFF_T offpos; + + /* Open the smbpassword file - for update. */ + fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth); + + if (fp == NULL && errno == ENOENT) { + /* Try again - create. */ + fp = startsmbfilepwent(pfile, PWF_CREATE, &pw_file_lock_depth); + } + + if (fp == NULL) { + DEBUG(0, ("add_smbfilepwd_entry: unable to open file.\n")); + return False; + } + + /* + * Scan the file, a line at a time and check if the name matches. + */ + + while ((pwd = getsmbfilepwent(fp)) != NULL) + { + if (strequal(newpwd->smb_name, pwd->smb_name)) + { + DEBUG(0, ("add_smbfilepwd_entry: entry with name %s already exists\n", pwd->smb_name)); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + } + + /* Ok - entry doesn't exist. We can add it */ + + /* Create a new smb passwd entry and set it to the given password. */ + /* + * The add user write needs to be atomic - so get the fd from + * the fp and do a raw write() call. + */ + fd = fileno(fp); + + if((offpos = sys_lseek(fd, 0, SEEK_END)) == -1) + { + DEBUG(0, ("add_smbfilepwd_entry(sys_lseek): Failed to add entry for user %s to file %s. \ +Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + if((new_entry = format_new_smbpasswd_entry(newpwd)) == NULL) + { + DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \ +Error was %s\n", newpwd->smb_name, pfile, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + new_entry_length = strlen(new_entry); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d made line |%s|", + fd, new_entry_length, new_entry)); +#endif + + if ((wr_len = write(fd, new_entry, new_entry_length)) != new_entry_length) + { + DEBUG(0, ("add_smbfilepwd_entry(write): %d Failed to add entry for user %s to file %s. \ +Error was %s\n", wr_len, newpwd->smb_name, pfile, strerror(errno))); + + /* Remove the entry we just wrote. */ + if(sys_ftruncate(fd, offpos) == -1) + { + DEBUG(0, ("add_smbfilepwd_entry: ERROR failed to ftruncate file %s. \ +Error was %s. Password file may be corrupt ! Please examine by hand !\n", + newpwd->smb_name, strerror(errno))); + } + + endsmbfilepwent(fp, &pw_file_lock_depth); + free(new_entry); + return False; + } + + free(new_entry); + endsmbfilepwent(fp, &pw_file_lock_depth); + return True; +} + +/************************************************************************ + Routine to search the smbpasswd file for an entry matching the username. + and then modify its password entry. We can't use the startsmbpwent()/ + getsmbpwent()/endsmbpwent() interfaces here as we depend on looking + in the actual file to decide how much room we have to write data. + override = False, normal + override = True, override XXXXXXXX'd out password or NO PASS +************************************************************************/ + +static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override) +{ + /* Static buffers we will return. */ + static pstring user_name; + + char linebuf[256]; + char readbuf[1024]; + unsigned char c; + fstring ascii_p16; + fstring encode_bits; + unsigned char *p = NULL; + size_t linebuf_len = 0; + FILE *fp; + int lockfd; + char *pfile = lp_smb_passwd_file(); + BOOL found_entry = False; + BOOL got_pass_last_set_time = False; + + SMB_OFF_T pwd_seekpos = 0; + + int i; + int wr_len; + int fd; + + if (!*pfile) { + DEBUG(0, ("No SMB password file set\n")); + return False; + } + DEBUG(10, ("mod_smbfilepwd_entry: opening file %s\n", pfile)); + + fp = sys_fopen(pfile, "r+"); + + if (fp == NULL) { + DEBUG(0, ("mod_smbfilepwd_entry: unable to open file %s\n", pfile)); + return False; + } + /* Set a buffer to do more efficient reads */ + setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); + + lockfd = fileno(fp); + + if (!pw_file_lock(lockfd, F_WRLCK, 5, &pw_file_lock_depth)) { + DEBUG(0, ("mod_smbfilepwd_entry: unable to lock file %s\n", pfile)); + fclose(fp); + return False; + } + + /* Make sure it is only rw by the owner */ + chmod(pfile, 0600); + + /* We have a write lock on the file. */ + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + pwd_seekpos = sys_ftell(fp); + + linebuf[0] = '\0'; + + fgets(linebuf, sizeof(linebuf), fp); + if (ferror(fp)) { + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + linebuf_len = strlen(linebuf); + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') { + break; + } + } + } else { + linebuf[linebuf_len - 1] = '\0'; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("mod_smbfilepwd_entry: got line |%s|\n", linebuf)); +#endif + + if ((linebuf[0] == 0) && feof(fp)) { + DEBUG(4, ("mod_smbfilepwd_entry: end of file reached\n")); + break; + } + + /* + * The line we have should be of the form :- + * + * username:uid:[32hex bytes]:....other flags presently + * ignored.... + * + * or, + * + * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored. + * + * if Windows NT compatible passwords are also present. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') { + DEBUG(6, ("mod_smbfilepwd_entry: skipping comment or blank line\n")); + continue; + } + + p = (unsigned char *) strchr_m(linebuf, ':'); + + if (p == NULL) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no :)\n")); + continue; + } + + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + if (strequal(user_name, pwd->smb_name)) { + found_entry = True; + break; + } + } + + if (!found_entry) { + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + DEBUG(6, ("mod_smbfilepwd_entry: entry exists\n")); + + /* User name matches - get uid and password */ + p++; /* Go past ':' */ + + if (!isdigit(*p)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (uid not number)\n")); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + while (*p && isdigit(*p)) + p++; + if (*p != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no : after uid)\n")); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + p++; + + /* Record exact password position */ + pwd_seekpos += PTR_DIFF(p, linebuf); + + if (!override && (*p == '*' || *p == 'X')) { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("mod_smbfilepwd_entry: entry invalidated for user %s\n", user_name)); + pw_file_unlock(lockfd, &pw_file_lock_depth); + fclose(fp); + return False; + } + + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + if (p[32] != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (!override && (*p == '*' || *p == 'X')) { + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* Now check if the NT compatible password is + available. */ + p += 33; /* Move to the first character of the line after + the lanman password. */ + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (passwd too short)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + if (p[32] != ':') { + DEBUG(0, ("mod_smbfilepwd_entry: malformed password entry (no terminating :)\n")); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* + * Now check if the account info and the password last + * change time is available. + */ + p += 33; /* Move to the first character of the line after + the NT password. */ + + /* + * If both NT and lanman passwords are provided - reset password + * not required flag. + */ + + if(pwd->smb_passwd != NULL || pwd->smb_nt_passwd != NULL) { + /* Reqiure password in the future (should ACB_DISABLED also be reset?) */ + pwd->acct_ctrl &= ~(ACB_PWNOTREQ); + } + + if (*p == '[') { + + i = 0; + encode_bits[i++] = *p++; + while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']')) + encode_bits[i++] = *p++; + + encode_bits[i++] = ']'; + encode_bits[i++] = '\0'; + + if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) { + /* + * We are using a new format, space padded + * acct ctrl field. Encode the given acct ctrl + * bits into it. + */ + fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN)); + } else { + /* + * If using the old format and the ACB_DISABLED or + * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL + * here as we have no space to encode the change. + */ + if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) { + pwd->smb_passwd = NULL; + pwd->smb_nt_passwd = NULL; + } + } + + /* Go past the ']' */ + if(linebuf_len > PTR_DIFF(p, linebuf)) + p++; + + if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) { + p++; + + /* We should be pointing at the LCT entry. */ + if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) { + + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) + break; + } + if(i == 8) { + /* + * p points at 8 characters of hex digits - + * read into a time_t as the seconds since + * 1970 that the password was last changed. + */ + got_pass_last_set_time = True; + } /* i == 8 */ + } /* *p && StrnCaseCmp() */ + } /* p == ':' */ + } /* p == '[' */ + + /* Entry is correctly formed. */ + + /* Create the 32 byte representation of the new p16 */ + if(pwd->smb_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&ascii_p16[i*2], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(ascii_p16, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(ascii_p16, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + + /* Add on the NT md4 hash */ + ascii_p16[32] = ':'; + wr_len = 66; + if (pwd->smb_nt_passwd != NULL) { + for (i = 0; i < 16; i++) { + slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]); + } + } else { + if(pwd->acct_ctrl & ACB_PWNOTREQ) + fstrcpy(&ascii_p16[33], "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX"); + else + fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + } + ascii_p16[65] = ':'; + ascii_p16[66] = '\0'; /* null-terminate the string so that strlen works */ + + /* Add on the account info bits and the time of last + password change. */ + + pwd->pass_last_set_time = time(NULL); + + if(got_pass_last_set_time) { + slprintf(&ascii_p16[strlen(ascii_p16)], + sizeof(ascii_p16)-(strlen(ascii_p16)+1), + "%s:LCT-%08X:", + encode_bits, (uint32)pwd->pass_last_set_time ); + wr_len = strlen(ascii_p16); + } + +#ifdef DEBUG_PASSWORD + DEBUG(100,("mod_smbfilepwd_entry: ")); + dump_data(100, ascii_p16, wr_len); +#endif + + if(wr_len > sizeof(linebuf)) { + DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return (False); + } + + /* + * Do an atomic write into the file at the position defined by + * seekpos. + */ + + /* The mod user write needs to be atomic - so get the fd from + the fp and do a raw write() call. + */ + + fd = fileno(fp); + + if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) { + DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + /* Sanity check - ensure the areas we are writing are framed by ':' */ + if (read(fd, linebuf, wr_len+1) != wr_len+1) { + DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) { + DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) { + DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + if (write(fd, ascii_p16, wr_len) != wr_len) { + DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile)); + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return False; + } + + pw_file_unlock(lockfd,&pw_file_lock_depth); + fclose(fp); + return True; +} + +/************************************************************************ + Routine to delete an entry in the smbpasswd file by name. +*************************************************************************/ + +static BOOL del_smbfilepwd_entry(const char *name) +{ + char *pfile = lp_smb_passwd_file(); + pstring pfile2; + struct smb_passwd *pwd = NULL; + FILE *fp = NULL; + FILE *fp_write = NULL; + int pfile2_lockdepth = 0; + + slprintf(pfile2, sizeof(pfile2)-1, "%s.%u", pfile, (unsigned)sys_getpid() ); + + /* + * Open the smbpassword file - for update. It needs to be update + * as we need any other processes to wait until we have replaced + * it. + */ + + if((fp = startsmbfilepwent(pfile, PWF_UPDATE, &pw_file_lock_depth)) == NULL) { + DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); + return False; + } + + /* + * Create the replacement password file. + */ + if((fp_write = startsmbfilepwent(pfile2, PWF_CREATE, &pfile2_lockdepth)) == NULL) { + DEBUG(0, ("del_smbfilepwd_entry: unable to open file %s.\n", pfile)); + endsmbfilepwent(fp, &pw_file_lock_depth); + return False; + } + + /* + * Scan the file, a line at a time and check if the name matches. + */ + + while ((pwd = getsmbfilepwent(fp)) != NULL) { + char *new_entry; + size_t new_entry_length; + + if (strequal(name, pwd->smb_name)) { + DEBUG(10, ("add_smbfilepwd_entry: found entry with name %s - deleting it.\n", name)); + continue; + } + + /* + * We need to copy the entry out into the second file. + */ + + if((new_entry = format_new_smbpasswd_entry(pwd)) == NULL) + { + DEBUG(0, ("del_smbfilepwd_entry(malloc): Failed to copy entry for user %s to file %s. \ +Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); + unlink(pfile2); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write, &pfile2_lockdepth); + return False; + } + + new_entry_length = strlen(new_entry); + + if(fwrite(new_entry, 1, new_entry_length, fp_write) != new_entry_length) + { + DEBUG(0, ("del_smbfilepwd_entry(write): Failed to copy entry for user %s to file %s. \ +Error was %s\n", pwd->smb_name, pfile2, strerror(errno))); + unlink(pfile2); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write, &pfile2_lockdepth); + free(new_entry); + return False; + } + + free(new_entry); + } + + /* + * Ensure pfile2 is flushed before rename. + */ + + if(fflush(fp_write) != 0) + { + DEBUG(0, ("del_smbfilepwd_entry: Failed to flush file %s. Error was %s\n", pfile2, strerror(errno))); + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write,&pfile2_lockdepth); + return False; + } + + /* + * Do an atomic rename - then release the locks. + */ + + if(rename(pfile2,pfile) != 0) { + unlink(pfile2); + } + + endsmbfilepwent(fp, &pw_file_lock_depth); + endsmbfilepwent(fp_write,&pfile2_lockdepth); + return True; +} + +/********************************************************************* + Create a smb_passwd struct from a SAM_ACCOUNT. + We will not allocate any new memory. The smb_passwd struct + should only stay around as long as the SAM_ACCOUNT does. + ********************************************************************/ +static BOOL build_smb_pass (struct smb_passwd *smb_pw, SAM_ACCOUNT *sampass) +{ + if (sampass == NULL) + return False; + + ZERO_STRUCTP(smb_pw); + + smb_pw->smb_userid=pdb_get_uid(sampass); + smb_pw->smb_name=pdb_get_username(sampass); + + smb_pw->smb_passwd=pdb_get_lanman_passwd(sampass); + smb_pw->smb_nt_passwd=pdb_get_nt_passwd(sampass); + + smb_pw->acct_ctrl=pdb_get_acct_ctrl(sampass); + smb_pw->pass_last_set_time=pdb_get_pass_last_set_time(sampass); + + return True; +} + +/********************************************************************* + Create a SAM_ACCOUNT from a smb_passwd struct + ********************************************************************/ +static BOOL build_sam_account(SAM_ACCOUNT *sam_pass, struct smb_passwd *pw_buf) +{ + struct passwd *pwfile; + + if (sam_pass==NULL) { + DEBUG(5,("build_sam_account: SAM_ACCOUNT is NULL\n")); + return False; + } + + /* Verify in system password file... + FIXME!!! This is where we should look up an internal + mapping of allocated uid for machine accounts as well + --jerry */ + pwfile = sys_getpwnam(pw_buf->smb_name); + if (pwfile == NULL) { + DEBUG(0,("build_sam_account: smbpasswd database is corrupt! username %s not in unix passwd database!\n", pw_buf->smb_name)); + return False; + } + + /* FIXME!! This doesn't belong here. Should be set in net_sam_logon() + --jerry */ + pstrcpy(samlogon_user, pw_buf->smb_name); + + pdb_set_uid (sam_pass, pwfile->pw_uid); + pdb_set_gid (sam_pass, pwfile->pw_gid); + pdb_set_fullname(sam_pass, pwfile->pw_gecos); + + pdb_set_user_rid(sam_pass, pdb_uid_to_user_rid (pwfile->pw_uid)); + + /* should check the group mapping here instead of static mappig. JFM */ + pdb_set_group_rid(sam_pass, pdb_gid_to_group_rid(pwfile->pw_gid)); + + pdb_set_username (sam_pass, pw_buf->smb_name); + pdb_set_nt_passwd (sam_pass, pw_buf->smb_nt_passwd); + pdb_set_lanman_passwd (sam_pass, pw_buf->smb_passwd); + pdb_set_acct_ctrl (sam_pass, pw_buf->acct_ctrl); + pdb_set_pass_last_set_time (sam_pass, pw_buf->pass_last_set_time); + pdb_set_pass_can_change_time (sam_pass, pw_buf->pass_last_set_time); + pdb_set_domain (sam_pass, lp_workgroup()); + + pdb_set_dir_drive (sam_pass, lp_logon_drive()); + + /* the smbpasswd format doesn't have a must change time field, so + we can't get this right. The best we can do is to set this to + some time in the future. 21 days seems as reasonable as any other value :) + */ + pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE); + + /* check if this is a user account or a machine account */ + if (samlogon_user[strlen(samlogon_user)-1] != '$') + { + pstring str; + gid_t gid = getegid(); + + sam_logon_in_ssb = True; + + pstrcpy(str, lp_logon_script()); + standard_sub_advanced(-1, pw_buf->smb_name, "", gid, str); + pdb_set_logon_script(sam_pass, str); + + pstrcpy(str, lp_logon_path()); + standard_sub_advanced(-1, pw_buf->smb_name, "", gid, str); + pdb_set_profile_path(sam_pass, str); + + pstrcpy(str, lp_logon_home()); + standard_sub_advanced(-1, pw_buf->smb_name, "", gid, str); + pdb_set_homedir(sam_pass, str); + + sam_logon_in_ssb = False; + } else { + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + pdb_set_group_rid (sam_pass, DOMAIN_GROUP_RID_USERS); + } + + return True; +} +/***************************************************************** + Functions to be implemented by the new passdb API + ****************************************************************/ +BOOL pdb_setsampwent (BOOL update) +{ + global_vp = startsmbfilepwent(lp_smb_passwd_file(), + update ? PWF_UPDATE : PWF_READ, + &pw_file_lock_depth); + + /* did we fail? Should we try to create it? */ + if (!global_vp && update && errno == ENOENT) + { + FILE *fp; + /* slprintf(msg_str,msg_str_len-1, + "smbpasswd file did not exist - attempting to create it.\n"); */ + DEBUG(0,("smbpasswd file did not exist - attempting to create it.\n")); + fp = sys_fopen(lp_smb_passwd_file(), "w"); + if (fp) + { + fprintf(fp, "# Samba SMB password file\n"); + fclose(fp); + } + + global_vp = startsmbfilepwent(lp_smb_passwd_file(), + update ? PWF_UPDATE : PWF_READ, + &pw_file_lock_depth); + } + + return (global_vp != NULL); +} + +void pdb_endsampwent (void) +{ + endsmbfilepwent(global_vp, &pw_file_lock_depth); +} + +/***************************************************************** + ****************************************************************/ +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + struct smb_passwd *pw_buf=NULL; + BOOL done = False; + DEBUG(5,("pdb_getsampwent\n")); + + if (user==NULL) { + DEBUG(5,("pdb_getsampwent: user is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwent\n"); +#endif + return False; + } + + while (!done) + { + /* do we have an entry? */ + pw_buf = getsmbfilepwent(global_vp); + if (pw_buf == NULL) + return False; + + /* build the SAM_ACCOUNT entry from the smb_passwd struct. + We loop in case the user in the pdb does not exist in + the local system password file */ + if (build_sam_account(user, pw_buf)) + done = True; + } + + DEBUG(5,("pdb_getsampwent:done\n")); + + /* success */ + return True; +} + + +/**************************************************************** + Search smbpasswd file by iterating over the entries. Do not + call getpwnam() for unix account information until we have found + the correct entry + ***************************************************************/ +BOOL pdb_getsampwnam(SAM_ACCOUNT *sam_acct, char *username) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + char *domain = NULL; + char *user = NULL; + fstring name; + + DEBUG(10, ("pdb_getsampwnam: search by name: %s\n", username)); + + + /* break the username from the domain if we have + been given a string in the form 'DOMAIN\user' */ + fstrcpy (name, username); + if ((user=strchr_m(name, '\\')) != NULL) { + domain = name; + *user = '\0'; + user++; + } + + /* if a domain was specified and it wasn't ours + then there is no chance of matching */ + if ( domain && !StrCaseCmp(domain, lp_workgroup()) ) + return False; + + /* startsmbfilepwent() is used here as we don't want to lookup + the UNIX account in the local system password file until + we have a match. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + /* if we have a domain name, then we should map it to a UNIX + username first */ + if ( domain ) + map_username(user); + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL)&& (!strequal(smb_pw->smb_name, username)) ) + /* do nothing....another loop */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwnam: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwnam:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwnam\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account(sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + + +BOOL pdb_getsampwuid (SAM_ACCOUNT *sam_acct, uid_t uid) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + + DEBUG(10, ("pdb_getsampwuid: search by uid: %d\n", uid)); + + /* Open the sam password file - not for update. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (smb_pw->smb_userid != uid) ) + /* do nothing */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwuid: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwuid:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwuid\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account(sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + +BOOL pdb_getsampwrid(SAM_ACCOUNT *sam_acct,uint32 rid) +{ + struct smb_passwd *smb_pw; + void *fp = NULL; + + DEBUG(10, ("pdb_getsampwrid: search by rid: %d\n", rid)); + + /* Open the sam password file - not for update. */ + fp = startsmbfilepwent(lp_smb_passwd_file(), PWF_READ, &pw_file_lock_depth); + + if (fp == NULL) { + DEBUG(0, ("unable to open passdb database.\n")); + return False; + } + + while ( ((smb_pw=getsmbfilepwent(fp)) != NULL) && (pdb_uid_to_user_rid(smb_pw->smb_userid) != rid) ) + /* do nothing */ ; + + endsmbfilepwent(fp, &pw_file_lock_depth); + + + /* did we locate the username in smbpasswd */ + if (smb_pw == NULL) + return False; + + DEBUG(10, ("pdb_getsampwrid: found by name: %s\n", smb_pw->smb_name)); + + if (!sam_acct) { + DEBUG(10,("pdb_getsampwrid:SAM_ACCOUNT is NULL\n")); +#if 0 + smb_panic("NULL pointer passed to pdb_getsampwrid\n"); +#endif + return False; + } + + /* now build the SAM_ACCOUNT */ + if (!build_sam_account (sam_acct, smb_pw)) + return False; + + /* success */ + return True; +} + +BOOL pdb_add_sam_account(SAM_ACCOUNT *sampass) +{ + struct smb_passwd smb_pw; + + /* convert the SAM_ACCOUNT */ + build_smb_pass(&smb_pw, sampass); + + /* add the entry */ + if(!add_smbfilepwd_entry(&smb_pw)) + return False; + + return True; +} + +BOOL pdb_update_sam_account(SAM_ACCOUNT *sampass, BOOL override) +{ + struct smb_passwd smb_pw; + + /* convert the SAM_ACCOUNT */ + build_smb_pass(&smb_pw, sampass); + + /* update the entry */ + if(!mod_smbfilepwd_entry(&smb_pw, override)) + return False; + + return True; +} + +BOOL pdb_delete_sam_account (char* username) +{ + return del_smbfilepwd_entry(username); +} + +#else + /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ + void smbpass_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WTH_SMBPASSWD_SAM*/ diff --git a/source/passdb/pdb_tdb.c b/source/passdb/pdb_tdb.c new file mode 100644 index 00000000000..43eefa5c7a7 --- /dev/null +++ b/source/passdb/pdb_tdb.c @@ -0,0 +1,830 @@ +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Simo Sorce 2000 + * Copyright (C) Gerald Carter 2000 + * Copyright (C) Jeremy Allison 2001 + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program 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. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 675 + * Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +#ifdef WITH_TDB_SAM + +#define PDB_VERSION "20010830" +#define PASSDB_FILE_NAME "/passdb.tdb" +#define TDB_FORMAT_STRING "ddddddBBBBBBBBBBBBddBBwdwdBdd" +#define USERPREFIX "USER_" +#define RIDPREFIX "RID_" + +extern int DEBUGLEVEL; +extern pstring samlogon_user; +extern BOOL sam_logon_in_ssb; + +struct tdb_enum_info { + TDB_CONTEXT *passwd_tdb; + TDB_DATA key; +}; + +static struct tdb_enum_info global_tdb_ent; +/*static SAM_ACCOUNT global_sam_pass;*/ + +/********************************************************************** + Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len + *********************************************************************/ + +static BOOL init_sam_from_buffer (SAM_ACCOUNT *sampass, uint8 *buf, uint32 buflen) +{ + + /* times are stored as 32bit integer + take care on system with 64bit wide time_t + --SSS */ + uint32 logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + char *username; + char *domain; + char *nt_username; + char *dir_drive; + char *unknown_str; + char *munged_dial; + char *fullname; + char *homedir; + char *logon_script; + char *profile_path; + char *acct_desc; + char *workstations; + uint32 username_len, domain_len, nt_username_len, + dir_drive_len, unknown_str_len, munged_dial_len, + fullname_len, homedir_len, logon_script_len, + profile_path_len, acct_desc_len, workstations_len; + + uint32 /* uid, gid,*/ user_rid, group_rid, unknown_3, hours_len, unknown_5, unknown_6; + uint16 acct_ctrl, logon_divs; + uint8 *hours; + static uint8 *lm_pw_ptr, *nt_pw_ptr; + uint32 len = 0; + uint32 lmpwlen, ntpwlen, hourslen; + BOOL ret = True; + + /* unpack the buffer into variables */ + len = tdb_unpack (buf, buflen, TDB_FORMAT_STRING, + &logon_time, + &logoff_time, + &kickoff_time, + &pass_last_set_time, + &pass_can_change_time, + &pass_must_change_time, + &username_len, &username, + &domain_len, &domain, + &nt_username_len, &nt_username, + &fullname_len, &fullname, + &homedir_len, &homedir, + &dir_drive_len, &dir_drive, + &logon_script_len, &logon_script, + &profile_path_len, &profile_path, + &acct_desc_len, &acct_desc, + &workstations_len, &workstations, + &unknown_str_len, &unknown_str, + &munged_dial_len, &munged_dial, + &user_rid, + &group_rid, + &lmpwlen, &lm_pw_ptr, + &ntpwlen, &nt_pw_ptr, + &acct_ctrl, + &unknown_3, + &logon_divs, + &hours_len, + &hourslen, &hours, + &unknown_5, + &unknown_6); + + if (len == -1) { + ret = False; + goto done; + } + + pdb_set_logon_time(sampass, logon_time); + pdb_set_logoff_time(sampass, logoff_time); + pdb_set_kickoff_time(sampass, kickoff_time); + pdb_set_pass_can_change_time(sampass, pass_can_change_time); + pdb_set_pass_must_change_time(sampass, pass_must_change_time); + pdb_set_pass_last_set_time(sampass, pass_last_set_time); + + pdb_set_username (sampass, username_len?username:NULL); + pdb_set_domain (sampass, domain_len?domain:NULL); + pdb_set_nt_username (sampass, nt_username_len?nt_username:NULL); + pdb_set_fullname (sampass, fullname_len?fullname:NULL); + pdb_set_homedir (sampass, homedir_len?homedir:NULL); + pdb_set_dir_drive (sampass, dir_drive_len?dir_drive:NULL); + pdb_set_logon_script (sampass, logon_script_len?logon_script:NULL); + pdb_set_profile_path (sampass, profile_path_len?profile_path:NULL); + pdb_set_acct_desc (sampass, acct_desc_len?acct_desc:NULL); + pdb_set_workstations (sampass, workstations_len?workstations:NULL); + pdb_set_munged_dial (sampass, munged_dial_len?munged_dial:NULL); + pdb_set_lanman_passwd(sampass, lmpwlen?lm_pw_ptr:NULL); + pdb_set_nt_passwd (sampass, ntpwlen?nt_pw_ptr:NULL); + + /*pdb_set_uid(sampass, uid); + pdb_set_gid(sampass, gid);*/ + pdb_set_user_rid(sampass, user_rid); + pdb_set_group_rid(sampass, group_rid); + pdb_set_unknown_3(sampass, unknown_3); + pdb_set_hours_len(sampass, hours_len); + pdb_set_unknown_5(sampass, unknown_5); + pdb_set_unknown_6(sampass, unknown_6); + pdb_set_acct_ctrl(sampass, acct_ctrl); + pdb_set_logons_divs(sampass, logon_divs); + pdb_set_hours(sampass, hours); + +done: + + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(nt_username); + SAFE_FREE(fullname); + SAFE_FREE(homedir); + SAFE_FREE(dir_drive); + SAFE_FREE(logon_script); + SAFE_FREE(profile_path); + SAFE_FREE(acct_desc); + SAFE_FREE(workstations); + SAFE_FREE(munged_dial); + + return ret; +} + +/********************************************************************** + Intialize a BYTE buffer from a SAM_ACCOUNT struct + *********************************************************************/ +static uint32 init_buffer_from_sam (uint8 **buf, SAM_ACCOUNT *sampass) +{ + size_t len, buflen; + + /* times are stored as 32bit integer + take care on system with 64bit wide time_t + --SSS */ + uint32 logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time; + char *username; + char *domain; + char *nt_username; + char *dir_drive; + char *unknown_str; + char *munged_dial; + char *fullname; + char *homedir; + char *logon_script; + char *profile_path; + char *acct_desc; + char *workstations; + uint32 username_len, domain_len, nt_username_len, + dir_drive_len, unknown_str_len, munged_dial_len, + fullname_len, homedir_len, logon_script_len, + profile_path_len, acct_desc_len, workstations_len; + + uint8 *lm_pw; + uint8 *nt_pw; + uint32 lm_pw_len = 16; + uint32 nt_pw_len = 16; + + /* do we have a valid SAM_ACCOUNT pointer? */ + if (sampass == NULL) + return -1; + + *buf = NULL; + buflen = 0; + + logon_time = (uint32)pdb_get_logon_time(sampass); + logoff_time = (uint32)pdb_get_logoff_time(sampass); + kickoff_time = (uint32)pdb_get_kickoff_time(sampass); + pass_can_change_time = (uint32)pdb_get_pass_can_change_time(sampass); + pass_must_change_time = (uint32)pdb_get_pass_must_change_time(sampass); + pass_last_set_time = (uint32)pdb_get_pass_last_set_time(sampass); + + + username = pdb_get_username(sampass); + if (username) + username_len = strlen(username) +1; + else + username_len = 0; + domain = pdb_get_domain(sampass); + if (domain) + domain_len = strlen(domain) +1; + else + domain_len = 0; + nt_username = pdb_get_nt_username(sampass); + if (nt_username) + nt_username_len = strlen(nt_username) +1; + else + nt_username_len = 0; + dir_drive = pdb_get_dirdrive(sampass); + if (dir_drive) + dir_drive_len = strlen(dir_drive) +1; + else + dir_drive_len = 0; + unknown_str = NULL; + unknown_str_len = 0; + munged_dial = pdb_get_munged_dial(sampass); + if (munged_dial) + munged_dial_len = strlen(munged_dial) +1; + else + munged_dial_len = 0; + + fullname = pdb_get_fullname(sampass); + if (fullname) + fullname_len = strlen(fullname) +1; + else + fullname_len = 0; + homedir = pdb_get_homedir(sampass); + if (homedir) + homedir_len = strlen(homedir) +1; + else + homedir_len = 0; + logon_script = pdb_get_logon_script(sampass); + if (logon_script) + logon_script_len = strlen(logon_script) +1; + else + logon_script_len = 0; + profile_path = pdb_get_profile_path(sampass); + if (profile_path) + profile_path_len = strlen(profile_path) +1; + else + profile_path_len = 0; + acct_desc = pdb_get_acct_desc(sampass); + if (acct_desc) + acct_desc_len = strlen(acct_desc) +1; + else + acct_desc_len = 0; + workstations = pdb_get_workstations(sampass); + if (workstations) + workstations_len = strlen(workstations) +1; + else + workstations_len = 0; + + lm_pw = pdb_get_lanman_passwd(sampass); + if (!lm_pw) + lm_pw_len = 0; + + nt_pw = pdb_get_nt_passwd(sampass); + if (!nt_pw) + nt_pw_len = 0; + + /* one time to get the size needed */ + len = tdb_pack(NULL, 0, TDB_FORMAT_STRING, + logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time, + username_len, username, + domain_len, domain, + nt_username_len, nt_username, + fullname_len, fullname, + homedir_len, homedir, + dir_drive_len, dir_drive, + logon_script_len, logon_script, + profile_path_len, profile_path, + acct_desc_len, acct_desc, + workstations_len, workstations, + unknown_str_len, unknown_str, + munged_dial_len, munged_dial, + pdb_get_user_rid(sampass), + pdb_get_group_rid(sampass), + lm_pw_len, lm_pw, + nt_pw_len, nt_pw, + pdb_get_acct_ctrl(sampass), + pdb_get_unknown3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown5(sampass), + pdb_get_unknown6(sampass)); + + + /* malloc the space needed */ + if ( (*buf=(uint8*)malloc(len)) == NULL) { + DEBUG(0,("init_buffer_from_sam: Unable to malloc() memory for buffer!\n")); + return (-1); + } + + /* now for the real call to tdb_pack() */ + buflen = tdb_pack(*buf, len, TDB_FORMAT_STRING, + logon_time, + logoff_time, + kickoff_time, + pass_last_set_time, + pass_can_change_time, + pass_must_change_time, + username_len, username, + domain_len, domain, + nt_username_len, nt_username, + fullname_len, fullname, + homedir_len, homedir, + dir_drive_len, dir_drive, + logon_script_len, logon_script, + profile_path_len, profile_path, + acct_desc_len, acct_desc, + workstations_len, workstations, + unknown_str_len, unknown_str, + munged_dial_len, munged_dial, + pdb_get_user_rid(sampass), + pdb_get_group_rid(sampass), + lm_pw_len, lm_pw, + nt_pw_len, nt_pw, + pdb_get_acct_ctrl(sampass), + pdb_get_unknown3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown5(sampass), + pdb_get_unknown6(sampass)); + + + /* check to make sure we got it correct */ + if (buflen != len) { + /* error */ + SAFE_FREE (*buf); + return (-1); + } + + return (buflen); +} + +/*************************************************************** + Open the TDB passwd database for SAM account enumeration. +****************************************************************/ + +BOOL pdb_setsampwent(BOOL update) +{ + pstring tdbfile; + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* Open tdb passwd */ + if (!(global_tdb_ent.passwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, update?(O_RDWR|O_CREAT):O_RDONLY, 0600))) + { + DEBUG(0, ("Unable to open/create TDB passwd\n")); + return False; + } + + global_tdb_ent.key = tdb_firstkey(global_tdb_ent.passwd_tdb); + + return True; +} + +/*************************************************************** + End enumeration of the TDB passwd list. +****************************************************************/ + +void pdb_endsampwent(void) +{ + if (global_tdb_ent.passwd_tdb) { + tdb_close(global_tdb_ent.passwd_tdb); + global_tdb_ent.passwd_tdb = NULL; + } + + DEBUG(7, ("endtdbpwent: closed password file.\n")); +} + +/***************************************************************** + Get one SAM_ACCOUNT from the TDB (next in line) +*****************************************************************/ + +BOOL pdb_getsampwent(SAM_ACCOUNT *user) +{ + TDB_DATA data; + struct passwd *pw; + uid_t uid; + gid_t gid; + char *prefix = USERPREFIX; + int prefixlen = strlen (prefix); + + if (user==NULL) { + DEBUG(0,("pdb_get_sampwent: SAM_ACCOUNT is NULL.\n")); + return False; + } + + /* skip all RID entries */ + while ((global_tdb_ent.key.dsize != 0) && (strncmp (global_tdb_ent.key.dptr, prefix, prefixlen))) + /* increment to next in line */ + global_tdb_ent.key = tdb_nextkey (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + + /* do we have an valid interation pointer? */ + if(global_tdb_ent.passwd_tdb == NULL) { + DEBUG(0,("pdb_get_sampwent: Bad TDB Context pointer.\n")); + return False; + } + + data = tdb_fetch (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwent: database entry not found.\n")); + return False; + } + + /* unpack the buffer */ + if (!init_sam_from_buffer (user, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + /* validate the account and fill in UNIX uid and gid. sys_getpwnam() + is used instaed of Get_Pwnam() as we do not need to try case + permutations */ + if ((pw=sys_getpwnam(pdb_get_username(user))) == NULL) { + DEBUG(0,("pdb_getsampwent: getpwnam(%s) return NULL. User does not exist!\n", + pdb_get_username(user))); + return False; + } + + uid = pw->pw_uid; + gid = pw->pw_gid; + pdb_set_uid (user, uid); + pdb_set_gid (user, gid); + + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_logon_script(user)); + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_profile_path(user)); + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_homedir(user)); + + /* increment to next in line */ + global_tdb_ent.key = tdb_nextkey (global_tdb_ent.passwd_tdb, global_tdb_ent.key); + + return True; +} + +/****************************************************************** + Lookup a name in the SAM TDB +******************************************************************/ + +BOOL pdb_getsampwnam (SAM_ACCOUNT *user, char *sname) +{ + TDB_CONTEXT *pwd_tdb; + TDB_DATA data, key; + fstring keystr; + struct passwd *pw; + pstring tdbfile; + fstring name; + uid_t uid; + gid_t gid; + + + if (user==NULL) { + DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n")); + return False; + } + + /* Data is stored in all lower-case */ + unix_strlower(sname, -1, name, sizeof(name)); + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* set search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* open the accounts TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + DEBUG(0, ("pdb_getsampwnam: Unable to open TDB passwd!\n")); + return False; + } + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + /* unpack the buffer */ + if (!init_sam_from_buffer (user, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + /* validate the account and fill in UNIX uid and gid. sys_getpwnam() + is used instaed of Get_Pwnam() as we do not need to try case + permutations */ + if ((pw=sys_getpwnam(pdb_get_username(user))) == NULL) { + DEBUG(0,("pdb_getsampwent: getpwnam(%s) return NULL. User does not exist!\n", + pdb_get_username(user))); + return False; + } + + uid = pw->pw_uid; + gid = pw->pw_gid; + pdb_set_uid (user, uid); + pdb_set_gid (user, gid); + + /* 21 days from present */ + pdb_set_pass_must_change_time(user, time(NULL)+1814400); + + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_logon_script(user)); + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_profile_path(user)); + standard_sub_advanced(-1, pdb_get_username(user), "", gid, pdb_get_homedir(user)); + + /* cleanup */ + tdb_close (pwd_tdb); + + return True; +} + +/*************************************************************************** + Search by uid + **************************************************************************/ + +BOOL pdb_getsampwuid (SAM_ACCOUNT* user, uid_t uid) +{ + struct passwd *pw; + fstring name; + + if (user==NULL) { + DEBUG(0,("pdb_getsampwuid: SAM_ACCOUNT is NULL.\n")); + return False; + } + + pw = sys_getpwuid(uid); + if (pw == NULL) { + DEBUG(0,("pdb_getsampwuid: getpwuid(%d) return NULL. User does not exist!\n", uid)); + return False; + } + fstrcpy (name, pw->pw_name); + + return pdb_getsampwnam (user, name); + +} + +/*************************************************************************** + Search by rid + **************************************************************************/ + +BOOL pdb_getsampwrid (SAM_ACCOUNT *user, uint32 rid) +{ + TDB_CONTEXT *pwd_tdb; + TDB_DATA data, key; + fstring keystr; + pstring tdbfile; + fstring name; + + if (user==NULL) { + DEBUG(0,("pdb_getsampwrid: SAM_ACCOUNT is NULL.\n")); + return False; + } + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* set search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* open the accounts TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + DEBUG(0, ("pdb_getsampwrid: Unable to open TDB rid database!\n")); + return False; + } + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwrid (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + fstrcpy (name, data.dptr); + SAFE_FREE(data.dptr); + + tdb_close (pwd_tdb); + + return pdb_getsampwnam (user, name); +} + +/*************************************************************************** + Delete a SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_delete_sam_account(char *sname) +{ + SAM_ACCOUNT *sam_pass = NULL; + TDB_CONTEXT *pwd_tdb; + TDB_DATA key, data; + fstring keystr; + pstring tdbfile; + uint32 rid; + fstring name; + + unix_strlower(sname, -1, name, sizeof(name)); + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + /* open the TDB */ + if (!(pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR, 0600))) { + DEBUG(0, ("Unable to open TDB passwd!")); + return False; + } + + /* set the search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* get the record */ + data = tdb_fetch (pwd_tdb, key); + if (!data.dptr) { + DEBUG(5,("pdb_delete_sam_account (TDB): error fetching database.\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + return False; + } + + /* unpack the buffer */ + if (!pdb_init_sam (&sam_pass)) { + tdb_close (pwd_tdb); + return False; + } + + if (!init_sam_from_buffer (sam_pass, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + tdb_close (pwd_tdb); + SAFE_FREE(data.dptr); + return False; + } + SAFE_FREE(data.dptr); + + rid = pdb_get_user_rid(sam_pass); + + pdb_free_sam (&sam_pass); + + /* it's outaa here! 8^) */ + if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) { + DEBUG(5, ("Error deleting entry from tdb passwd database!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close(pwd_tdb); + return False; + } + + /* delete also the RID key */ + + /* set the search key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* it's outaa here! 8^) */ + if (tdb_delete(pwd_tdb, key) != TDB_SUCCESS) { + DEBUG(5, ("Error deleting entry from tdb rid database!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close(pwd_tdb); + return False; + } + + tdb_close(pwd_tdb); + + return True; +} + +/*************************************************************************** + Update the TDB SAM +****************************************************************************/ + +static BOOL tdb_update_sam(SAM_ACCOUNT* newpwd, BOOL override, int flag) +{ + TDB_CONTEXT *pwd_tdb = NULL; + TDB_DATA key, data; + uint8 *buf = NULL; + fstring keystr; + pstring tdbfile; + fstring name; + BOOL ret = True; + + get_private_directory(tdbfile); + pstrcat (tdbfile, PASSDB_FILE_NAME); + + if ( (!newpwd->uid) || (!newpwd->gid) ) + DEBUG (0,("tdb_update_sam: Storing a SAM_ACCOUNT for [%s] with uid %d and gid %d!\n", + newpwd->username, newpwd->uid, newpwd->gid)); + + /* if we don't have a RID, then generate one */ + if (!newpwd->user_rid) + pdb_set_user_rid (newpwd, pdb_uid_to_user_rid (newpwd->uid)); + if (!newpwd->group_rid) + pdb_set_group_rid (newpwd, pdb_gid_to_group_rid (newpwd->gid)); + + /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */ + if ((data.dsize=init_buffer_from_sam (&buf, newpwd)) == -1) { + DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n")); + ret = False; + goto done; + } + data.dptr = buf; + + unix_strlower(pdb_get_username(newpwd), -1, name, sizeof(name)); + + /* setup the USER index key */ + slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* invalidate the existing TDB iterator if it is open */ + if (global_tdb_ent.passwd_tdb) { + tdb_close(global_tdb_ent.passwd_tdb); + global_tdb_ent.passwd_tdb = NULL; + } + + /* open the account TDB passwd*/ + pwd_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600); + if (!pwd_tdb) + { + DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd!\n")); + return False; + } + + /* add the account */ + if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) { + DEBUG(0, ("Unable to modify passwd TDB!")); + DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + tdb_close (pwd_tdb); + ret = False; + goto done; + } + + /* setup RID data */ + data.dsize = sizeof(fstring); + data.dptr = name; + + /* setup the RID index key */ + slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, pdb_get_user_rid(newpwd)); + key.dptr = keystr; + key.dsize = strlen (keystr) + 1; + + /* add the reference */ + if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) { + DEBUG(0, ("Unable to modify TDB passwd !")); + DEBUGADD(0, (" Error: %s\n", tdb_errorstr(pwd_tdb))); + ret = False; + goto done; + } + +done: + /* cleanup */ + tdb_close (pwd_tdb); + SAFE_FREE(buf); + + return (ret); +} + +/*************************************************************************** + Modifies an existing SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_update_sam_account (SAM_ACCOUNT *newpwd, BOOL override) +{ + return (tdb_update_sam(newpwd, override, TDB_MODIFY)); +} + +/*************************************************************************** + Adds an existing SAM_ACCOUNT +****************************************************************************/ + +BOOL pdb_add_sam_account (SAM_ACCOUNT *newpwd) +{ + return (tdb_update_sam(newpwd, True, TDB_INSERT)); +} + +#else + /* Do *NOT* make this function static. It breaks the compile on gcc. JRA */ + void samtdb_dummy_function(void) { } /* stop some compilers complaining */ +#endif /* WITH_TDB_SAM */ diff --git a/source/rpc_client/cli_trust.c b/source/rpc_client/cli_trust.c new file mode 100644 index 00000000000..d7faf4975f6 --- /dev/null +++ b/source/rpc_client/cli_trust.c @@ -0,0 +1,247 @@ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-1997, + * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, + * Copyright (C) Paul Ashton 1997. + * Copyright (C) Jeremy Allison 1998. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +extern pstring global_myname; + +/********************************************************* + Change the domain password on the PDC. +**********************************************************/ + +static BOOL modify_trust_password( char *domain, char *remote_machine, + unsigned char orig_trust_passwd_hash[16], + unsigned char new_trust_passwd_hash[16]) +{ + struct cli_state cli; + NTSTATUS result; + + ZERO_STRUCT(cli); + if(cli_initialise(&cli) == NULL) { + DEBUG(0,("modify_trust_password: unable to initialize client connection.\n")); + return False; + } + + if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) { + DEBUG(0,("modify_trust_password: Can't resolve address for %s\n", remote_machine)); + cli_shutdown(&cli); + return False; + } + + if (ismyip(cli.dest_ip)) { + DEBUG(0,("modify_trust_password: Machine %s is one of our addresses. Cannot add \ +to ourselves.\n", remote_machine)); + cli_shutdown(&cli); + return False; + } + + if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) { + DEBUG(0,("modify_trust_password: unable to connect to SMB server on \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + return False; + } + + if (!attempt_netbios_session_request(&cli, global_myname, remote_machine, &cli.dest_ip)) { + DEBUG(0,("modify_trust_password: machine %s rejected the NetBIOS \ +session request. Error was %s\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + return False; + } + + cli.protocol = PROTOCOL_NT1; + + if (!cli_negprot(&cli)) { + DEBUG(0,("modify_trust_password: machine %s rejected the negotiate protocol. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + return False; + } + + if (cli.protocol != PROTOCOL_NT1) { + DEBUG(0,("modify_trust_password: machine %s didn't negotiate NT protocol.\n", + remote_machine)); + cli_shutdown(&cli); + return False; + } + + /* + * Do an anonymous session setup. + */ + + if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) { + DEBUG(0,("modify_trust_password: machine %s rejected the session setup. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + return False; + } + + if (!(cli.sec_mode & 1)) { + DEBUG(0,("modify_trust_password: machine %s isn't in user level security mode\n", + remote_machine)); + cli_shutdown(&cli); + return False; + } + + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) { + DEBUG(0,("modify_trust_password: machine %s rejected the tconX on the IPC$ share. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + cli_shutdown(&cli); + return False; + } + + /* + * Ok - we have an anonymous connection to the IPC$ share. + * Now start the NT Domain stuff :-). + */ + + if(cli_lsa_get_domain_sid(&cli, remote_machine) == False) { + DEBUG(0,("modify_trust_password: unable to obtain domain sid from %s. Error was : %s.\n", remote_machine, cli_errstr(&cli))); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } + + if(cli_nt_session_open(&cli, PIPE_NETLOGON) == False) { + DEBUG(0,("modify_trust_password: unable to open the domain client session to \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } + + result = cli_nt_setup_creds(&cli, orig_trust_passwd_hash); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("modify_trust_password: unable to setup the PDC credentials to machine \ +%s. Error was : %s.\n", remote_machine, get_nt_error_msg(result))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } + + if( cli_nt_srv_pwset( &cli,new_trust_passwd_hash ) == False) { + DEBUG(0,("modify_trust_password: unable to change password for machine %s in domain \ +%s to Domain controller %s. Error was %s.\n", global_myname, domain, remote_machine, + cli_errstr(&cli))); + cli_close(&cli, cli.nt_pipe_fnum); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } + + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + + return True; +} + +/************************************************************************ + Change the trust account password for a domain. + The user of this function must have locked the trust password file for + update. +************************************************************************/ + +BOOL change_trust_account_password( char *domain, char *remote_machine_list) +{ + fstring remote_machine; + unsigned char old_trust_passwd_hash[16]; + unsigned char new_trust_passwd_hash[16]; + time_t lct; + BOOL res = False; + + if(!secrets_fetch_trust_account_password(domain, old_trust_passwd_hash, &lct)) { + DEBUG(0,("change_trust_account_password: unable to read the machine \ +account password for domain %s.\n", domain)); + return False; + } + + /* + * Create the new (random) password. + */ + generate_random_buffer( new_trust_passwd_hash, 16, True); + + while(remote_machine_list && + next_token(&remote_machine_list, remote_machine, + LIST_SEP, sizeof(remote_machine))) { + strupper(remote_machine); + if(strequal(remote_machine, "*")) { + + /* + * We have been asked to dynamcially determine the IP addresses of the PDC. + */ + + struct in_addr *ip_list = NULL; + int count = 0; + int i; + + /* Use the PDC *only* for this. */ + if(!get_dc_list(True, domain, &ip_list, &count)) + continue; + + /* + * Try and connect to the PDC/BDC list in turn as an IP + * address used as a string. + */ + + for(i = 0; i < count; i++) { + fstring dc_name; + if(!lookup_pdc_name(global_myname, domain, &ip_list[i], dc_name)) + continue; + if((res = modify_trust_password( domain, dc_name, + old_trust_passwd_hash, new_trust_passwd_hash))) + break; + } + + SAFE_FREE(ip_list); + + } else { + res = modify_trust_password( domain, remote_machine, + old_trust_passwd_hash, new_trust_passwd_hash); + } + + if(res) { + DEBUG(0,("%s : change_trust_account_password: Changed password for \ +domain %s.\n", timestring(False), domain)); + /* + * Return the result of trying to write the new password + * back into the trust account file. + */ + res = secrets_store_trust_account_password(domain, new_trust_passwd_hash); + memset(new_trust_passwd_hash, 0, 16); + memset(old_trust_passwd_hash, 0, 16); + return res; + } + } + + memset(new_trust_passwd_hash, 0, 16); + memset(old_trust_passwd_hash, 0, 16); + + DEBUG(0,("%s : change_trust_account_password: Failed to change password for \ +domain %s.\n", timestring(False), domain)); + return False; +} diff --git a/source/utils/pdbedit.c b/source/utils/pdbedit.c new file mode 100644 index 00000000000..6e7458fb4e9 --- /dev/null +++ b/source/utils/pdbedit.c @@ -0,0 +1,709 @@ +/* + Unix SMB/Netbios implementation. + passdb editing frontend + Version 3.0 + + Copyright (C) Simo Sorce 2000 + Copyright (C) Andrew Bartlett 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* base uid for trust accounts is set to 60000 ! + * May be we should add the defines in smb.h to make it possible having + * different values on different platforms? + */ + +#define BASE_MACHINE_UID 60000 +#define MAX_MACHINE_UID 65500 /* 5500 trust accounts aren't enough? */ + +#include "includes.h" + +extern pstring global_myname; + +/* + * Next two lines needed for SunOS and don't + * hurt anything else... + */ +extern char *optarg; +extern int optind; + +/********************************************************* + Print command usage on stderr and die. +**********************************************************/ +static void usage(void) +{ + if (getuid() == 0) { + printf("tdbedit options\n"); + } else { + printf("You need to be root to use this tool!\n"); + } + printf("(actually to add a user you need to use smbpasswd)\n"); + printf("options:\n"); + printf(" -l list usernames\n"); + printf(" -v verbose output\n"); + printf(" -w smbpasswd file style\n"); + printf(" -u username print user's info\n"); + printf(" -f fullname set Full Name\n"); + printf(" -h homedir set home directory\n"); + printf(" -d drive set home dir drive\n"); + printf(" -s script set logon script\n"); + printf(" -p profile set profile path\n"); + printf(" -a create new account\n"); + printf(" -m it is a machine trust\n"); + printf(" -x delete this user\n"); + printf(" -i file import account from file (smbpasswd style)\n"); + exit(1); +} + +/********************************************************* + Print info from sam structure +**********************************************************/ + +static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle) +{ + /* TODO: chaeck if entry is a user or a workstation */ + if (!sam_pwent) return -1; + + if (verbosity) { + printf ("username: %s\n", sam_pwent->username); + printf ("user ID/Group: %d/%d\n", sam_pwent->uid, + sam_pwent->gid); + printf ("user RID/GRID: %d/%d\n", sam_pwent->user_rid, + sam_pwent->group_rid); + printf ("Full Name: %s\n", sam_pwent->full_name); + printf ("Home Directory: %s\n", sam_pwent->home_dir); + printf ("HomeDir Drive: %s\n", sam_pwent->dir_drive); + printf ("Logon Script: %s\n", sam_pwent->logon_script); + printf ("Profile Path: %s\n", sam_pwent->profile_path); + } else if (smbpwdstyle) { + char lm_passwd[33]; + char nt_passwd[33]; + pdb_sethexpwd(lm_passwd, + pdb_get_lanman_passwd(sam_pwent), + pdb_get_acct_ctrl(sam_pwent)); + pdb_sethexpwd(nt_passwd, + pdb_get_nt_passwd(sam_pwent), + pdb_get_acct_ctrl(sam_pwent)); + + printf("%s:%d:%s:%s:%s:LCT-%08X:\n", + pdb_get_username(sam_pwent), + pdb_get_uid(sam_pwent), + lm_passwd, + nt_passwd, + pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32)pdb_get_pass_last_set_time(sam_pwent)); + } else { + printf ("%s:%d:%s\n", sam_pwent->username, sam_pwent->uid, sam_pwent->full_name); + } + + return 0; +} + +/********************************************************* + Get an Print User Info +**********************************************************/ + +static int print_user_info (char *username, BOOL verbosity, BOOL smbpwdstyle) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + + pdb_init_sam(&sam_pwent); + + ret = pdb_getsampwnam (sam_pwent, username); + + if (ret==False) { + fprintf (stderr, "Username not found!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle); + pdb_free_sam(&sam_pwent); + + return ret; +} + +/********************************************************* + List Users +**********************************************************/ +static int print_users_list (BOOL verbosity, BOOL smbpwdstyle) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + + pdb_init_sam(&sam_pwent); + + ret = pdb_setsampwent(False); + if (ret && errno == ENOENT) { + fprintf (stderr,"Password database not found!\n"); + pdb_free_sam(&sam_pwent); + exit(1); + } + + while ((ret = pdb_getsampwent (sam_pwent))) { + if (verbosity) + printf ("---------------\n"); + print_sam_info (sam_pwent, verbosity, smbpwdstyle); + pdb_reset_sam(sam_pwent); + } + + pdb_endsampwent (); + pdb_free_sam(&sam_pwent); + return 0; +} + +/********************************************************* + Set User Info +**********************************************************/ + +static int set_user_info (char *username, char *fullname, char *homedir, char *drive, char *script, char *profile) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + + pdb_init_sam(&sam_pwent); + + ret = pdb_getsampwnam (sam_pwent, username); + if (ret==False) { + fprintf (stderr, "Username not found!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + if (fullname) + pdb_set_fullname(sam_pwent, fullname); + if (homedir) + pdb_set_homedir(sam_pwent, homedir); + if (drive) + pdb_set_dir_drive(sam_pwent,drive); + if (script) + pdb_set_logon_script(sam_pwent, script); + if (profile) + pdb_set_profile_path (sam_pwent, profile); + + if (pdb_update_sam_account (sam_pwent, True)) + print_user_info (username, True, False); + else { + fprintf (stderr, "Unable to modify entry!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + pdb_free_sam(&sam_pwent); + return 0; +} + +/********************************************************* + Add New User +**********************************************************/ +static int new_user (char *username, char *fullname, char *homedir, char *drive, char *script, char *profile) +{ + SAM_ACCOUNT *sam_pwent=NULL; + struct passwd *pwd = NULL; + char *password1, *password2; + + ZERO_STRUCT(sam_pwent); + + pdb_init_sam (&sam_pwent); + + if (!(pwd = sys_getpwnam(username))) { + fprintf (stderr, "User %s does not exist in system passwd!\n", username); + pdb_free_sam (&sam_pwent); + return -1; + } + + password1 = getpass("new password:"); + password2 = getpass("retype new password:"); + if (strcmp (password1, password2)) { + fprintf (stderr, "Passwords does not match!\n"); + pdb_free_sam (&sam_pwent); + return -1; + } + + pdb_set_plaintext_passwd(sam_pwent, password1); + + pdb_set_username(sam_pwent, username); + if (fullname) + pdb_set_fullname(sam_pwent, fullname); + if (homedir) + pdb_set_homedir (sam_pwent, homedir); + if (drive) + pdb_set_dir_drive (sam_pwent, drive); + if (script) + pdb_set_logon_script(sam_pwent, script); + if (profile) + pdb_set_profile_path (sam_pwent, profile); + + /* TODO: Check uid not being in MACHINE UID range!! */ + pdb_set_uid (sam_pwent, pwd->pw_uid); + pdb_set_gid (sam_pwent, pwd->pw_gid); + pdb_set_user_rid (sam_pwent, pdb_uid_to_user_rid (pwd->pw_uid)); + pdb_set_group_rid (sam_pwent, pdb_gid_to_group_rid (pwd->pw_gid)); + + pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL); + + if (pdb_add_sam_account (sam_pwent)) { + print_user_info (username, True, False); + } else { + fprintf (stderr, "Unable to add user! (does it alredy exist?)\n"); + pdb_free_sam (&sam_pwent); + return -1; + } + pdb_free_sam (&sam_pwent); + return 0; +} + +/********************************************************* + Add New Machine +**********************************************************/ + +static int new_machine (char *machinename) +{ + SAM_ACCOUNT *sam_pwent=NULL; + SAM_ACCOUNT *sam_trust=NULL; + char name[16]; + char *password = NULL; + uid_t uid; + + pdb_init_sam (&sam_pwent); + + if (machinename[strlen (machinename) -1] == '$') + machinename[strlen (machinename) -1] = '\0'; + + safe_strcpy (name, machinename, 16); + safe_strcat (name, "$", 16); + + string_set (&password, machinename); + strlower_m(password); + + pdb_set_plaintext_passwd (sam_pwent, password); + + pdb_set_username (sam_pwent, name); + + for (uid=BASE_MACHINE_UID; uid<=MAX_MACHINE_UID; uid++) { + pdb_init_sam (&sam_trust); + if (pdb_getsampwuid (sam_trust, uid)) { + pdb_free_sam (&sam_trust); + } else { + break; + } + } + + if (uid>MAX_MACHINE_UID) { + fprintf (stderr, "No more free UIDs available to Machine accounts!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + pdb_set_uid (sam_pwent, uid); + pdb_set_gid (sam_pwent, BASE_MACHINE_UID); /* TODO: set there more appropriate value!! */ + pdb_set_user_rid (sam_pwent,pdb_uid_to_user_rid (uid)); + pdb_set_group_rid (sam_pwent, pdb_gid_to_group_rid (BASE_MACHINE_UID)); + pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST); + + if (pdb_add_sam_account (sam_pwent)) { + print_user_info (name, True, False); + } else { + fprintf (stderr, "Unable to add machine! (does it already exist?)\n"); + pdb_free_sam (&sam_pwent); + return -1; + } + pdb_free_sam (&sam_pwent); + return 0; +} + +/********************************************************* + Delete user entry +**********************************************************/ + +static int delete_user_entry (char *username) +{ + return pdb_delete_sam_account (username); +} + +/********************************************************* + Delete machine entry +**********************************************************/ + +static int delete_machine_entry (char *machinename) +{ + char name[16]; + + safe_strcpy (name, machinename, 16); + if (name[strlen(name)] != '$') + safe_strcat (name, "$", 16); + return pdb_delete_sam_account (name); +} + +/********************************************************* + Import smbpasswd style file +**********************************************************/ + +static int import_users (char *filename) +{ + FILE *fp = NULL; + SAM_ACCOUNT *sam_pwent = NULL; + static pstring user_name; + static unsigned char smbpwd[16]; + static unsigned char smbntpwd[16]; + char linebuf[256]; + size_t linebuf_len; + unsigned char c; + unsigned char *p; + long uidval; + int line = 0; + int good = 0; + + if (!pdb_init_sam (&sam_pwent)) { + fprintf (stderr, "pdb_init_sam FAILED!\n"); + } + + if((fp = sys_fopen(filename, "rb")) == NULL) { + fprintf (stderr, "%s\n", strerror (ferror (fp))); + return -1; + } + + while (!feof(fp)) { + /*Get a new line*/ + linebuf[0] = '\0'; + fgets(linebuf, 256, fp); + if (ferror(fp)) { + fprintf (stderr, "%s\n", strerror (ferror (fp))); + pdb_free_sam(&sam_pwent); + return -1; + } + if ((linebuf_len = strlen(linebuf)) == 0) { + line++; + continue; + } + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') break; + } + } else + linebuf[linebuf_len - 1] = '\0'; + linebuf[linebuf_len] = '\0'; + if ((linebuf[0] == 0) && feof(fp)) { + /*end of file!!*/ + pdb_free_sam(&sam_pwent); + return 0; + } + line++; + if (linebuf[0] == '#' || linebuf[0] == '\0') + continue; + + pdb_set_acct_ctrl (sam_pwent,ACB_NORMAL); + + /* Get user name */ + p = (unsigned char *) strchr_m(linebuf, ':'); + if (p == NULL) { + fprintf (stderr, "Error: malformed password entry at line %d !!\n", line); + pdb_reset_sam (sam_pwent); + continue; + } + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + + /* Get smb uid. */ + p++; + if(*p == '-') { + fprintf (stderr, "Error: negative uid at line %d\n", line); + pdb_reset_sam (sam_pwent); + continue; + } + if (!isdigit(*p)) { + fprintf (stderr, "Error: malformed password entry at line %d (uid not number)\n", line); + pdb_reset_sam (sam_pwent); + continue; + } + uidval = atoi((char *) p); + while (*p && isdigit(*p)) p++; + if (*p != ':') { + fprintf (stderr, "Error: malformed password entry at line %d (no : after uid)\n", line); + pdb_reset_sam (sam_pwent); + continue; + } + + pdb_set_username(sam_pwent, user_name); + pdb_set_uid (sam_pwent, uidval); + + /* Get passwords */ + p++; + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid */ + fprintf (stderr, "Warning: entry invalidated for user %s\n", user_name); + pdb_set_lanman_passwd(sam_pwent, NULL); + pdb_set_nt_passwd(sam_pwent,NULL); + pdb_set_acct_ctrl(sam_pwent, pdb_get_acct_ctrl(sam_pwent) | ACB_DISABLED); + } else { + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + fprintf (stderr, "Error: malformed password entry at line %d (password too short)\n",line); + pdb_reset_sam (sam_pwent); + continue; + } + if (p[32] != ':') { + fprintf (stderr, "Error: malformed password entry at line %d (no terminating :)\n",line); + pdb_reset_sam (sam_pwent); + continue; + } + if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { + pdb_set_lanman_passwd(sam_pwent, NULL); + pdb_set_acct_ctrl(sam_pwent, pdb_get_acct_ctrl(sam_pwent) | ACB_PWNOTREQ); + } else { + if (!pdb_gethexpwd((char *)p, smbpwd)) { + fprintf (stderr, "Error: malformed Lanman password entry at line %d (non hex chars)\n", line); + pdb_reset_sam (sam_pwent); + continue; + } + pdb_set_lanman_passwd(sam_pwent, smbpwd); + } + /* NT password */ + p += 33; + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + if (*p != '*' && *p != 'X') { + if (pdb_gethexpwd((char *)p,smbntpwd)) { + pdb_set_nt_passwd(sam_pwent, smbntpwd); + } + } + p += 33; + } + } + + /* Get ACCT_CTRL field if any */ + if (*p == '[') { + uint16 acct_ctrl; + unsigned char *end_p = (unsigned char *)strchr_m((char *)p, ']'); + + acct_ctrl = pdb_decode_acct_ctrl((char*)p); + if (acct_ctrl) + acct_ctrl = ACB_NORMAL; + + pdb_set_acct_ctrl(sam_pwent, acct_ctrl); + + /* Get last change time */ + if(end_p) + p = end_p + 1; + if(*p == ':') { + p++; + if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) { + int i; + + p += 4; + for(i = 0; i < 8; i++) { + if(p[i] == '\0' || !isxdigit(p[i])) break; + } + if(i == 8) { + pdb_set_pass_last_set_time (sam_pwent, (time_t)strtol((char *)p, NULL, 16)); + } + } + } + } + + /* Old-style workstation account code droped. */ + + if (pdb_get_acct_ctrl(sam_pwent) & ACB_WSTRUST) { + if ((uidval < BASE_MACHINE_UID) || (uidval > MAX_MACHINE_UID)) { + fprintf (stderr, "Warning: Machine UID out of normal range %d-%d\n", + BASE_MACHINE_UID, + MAX_MACHINE_UID); + } + pdb_set_uid(sam_pwent, BASE_MACHINE_UID); + } + + /* Test if user is valid */ + if (pdb_get_acct_ctrl(sam_pwent) & ACB_NORMAL) { + struct passwd *pwd = NULL; + + if (!(pwd = sys_getpwnam(user_name))) { + fprintf (stderr, "Error: User %s does not exist in system passwd!\n", user_name); + continue; + } + pdb_set_gid(sam_pwent, pwd->pw_gid); + } + + /* Fill in sam_pwent structure */ + pdb_set_user_rid(sam_pwent, pdb_uid_to_user_rid (pdb_get_uid(sam_pwent))); + pdb_set_group_rid(sam_pwent, pdb_gid_to_group_rid (pdb_get_gid(sam_pwent))); + + /* TODO: set also full_name, home_dir, dir_drive, logon_script, profile_path, ecc... + * when defaults will be available (after passdb redesign) + * let them blank just now they are not used anyway + */ + + /* Now ADD the entry */ + if (!(pdb_add_sam_account (sam_pwent))) { + fprintf (stderr, "Unable to add user entry!\n"); + pdb_reset_sam (sam_pwent); + continue; + } + printf ("%s imported!\n", user_name); + good++; + pdb_reset_sam (sam_pwent); + } + printf ("%d lines read.\n%d entryes imported\n", line, good); + pdb_free_sam(&sam_pwent); + return 0; +} + +/********************************************************* + Start here. +**********************************************************/ + +int main (int argc, char **argv) +{ + int ch; + static pstring servicesf = CONFIGFILE; + BOOL list_users = False; + BOOL verbose = False; + BOOL spstyle = False; + BOOL setparms = False; + BOOL machine = False; + BOOL add_user = False; + BOOL delete_user = False; + BOOL import = False; + char *user_name = NULL; + char *full_name = NULL; + char *home_dir = NULL; + char *home_drive = NULL; + char *logon_script = NULL; + char *profile_path = NULL; + char *smbpasswd = NULL; + + TimeInit(); + + setup_logging("tdbedit", True); + + if (argc < 2) { + usage(); + return 0; + } + + if(!initialize_password_db(True)) { + fprintf(stderr, "Can't setup password database vectors.\n"); + exit(1); + } + + if (!lp_load(servicesf,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", + servicesf); + exit(1); + } + + while ((ch = getopt(argc, argv, "ad:f:h:i:lmp:s:u:vwx")) != EOF) { + switch(ch) { + case 'a': + add_user = True; + break; + case 'm': + machine = True; + break; + case 'l': + list_users = True; + break; + case 'v': + verbose = True; + break; + case 'w': + spstyle = True; + break; + case 'u': + user_name = optarg; + break; + case 'f': + setparms = True; + full_name = optarg; + break; + case 'h': + setparms = True; + home_dir = optarg; + break; + case 'd': + setparms = True; + home_drive = optarg; + break; + case 's': + setparms = True; + logon_script = optarg; + break; + case 'p': + setparms = True; + profile_path = optarg; + break; + case 'x': + delete_user = True; + break; + case 'i': + import = True; + smbpasswd = optarg; + break; + default: + usage(); + } + } + if (((add_user?1:0) + (delete_user?1:0) + (list_users?1:0) + (import?1:0) + (setparms?1:0)) > 1) { + fprintf (stderr, "Incompatible options on command line!\n"); + usage(); + exit(1); + } + + if (add_user) { + if (!user_name) { + fprintf (stderr, "Username not specified! (use -u option)\n"); + return -1; + } + if (machine) + return new_machine (user_name); + else + return new_user (user_name, full_name, home_dir, home_drive, logon_script, profile_path); + } + + if (delete_user) { + if (!user_name) { + fprintf (stderr, "Username not specified! (use -u option)\n"); + return -1; + } + if (machine) + return delete_machine_entry (user_name); + else + return delete_user_entry (user_name); + } + + if (user_name) { + if (setparms) + set_user_info ( user_name, full_name, + home_dir, + home_drive, + logon_script, + profile_path); + else + return print_user_info (user_name, verbose, spstyle); + + return 0; + } + + + if (list_users) + return print_users_list (verbose, spstyle); + + if (import) + return import_users (smbpasswd); + + usage(); + + return 0; +} |