Basic (auth_rewrite branch on SourceForge).
Contributors:
Andy Doran
Robert Collins
Chemolli Francesco
Henrik Nordstrom
For details about the new API's, see Programmers Guide.
As part of this change everything from auth_modules has been moved to
src/auth/basic/helpers
Changes to squid-2.5
+ - Major rewrite of proxy authentication to support other schemes
+ than basic. First in the line is NTLM support but others can
+ easily be added (digest is on the way). See Programmers Guide.
+ (Robert Collins & Chemolli Francesco)
+ - Reworked how request bodies are passed down to the protocols.
+ Now all client side processing is inside client_side.c, and
+ the pass and pump modules is no longer used.
- Optimized searching in proxy_auth and ident ACL types. Should
now handle large access lists a lot more efficient.
(Francesco Chemolli)
dnl
dnl Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
dnl
-dnl $Id: configure.in,v 1.211 2001/01/04 21:53:57 hno Exp $
+dnl $Id: configure.in,v 1.212 2001/01/07 23:36:27 hno Exp $
dnl
dnl
dnl
AC_INIT(src/main.c)
AC_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.211 $)dnl
+AC_REVISION($Revision: 1.212 $)dnl
AC_PREFIX_DEFAULT(/usr/local/squid)
AC_CONFIG_AUX_DIR(cfgaux)
])
AC_SUBST(LEAKFINDER_OBJS)
-dnl Disable HTTP violations
AC_ARG_ENABLE(ident-lookups,
[ --disable-ident-lookups
This allows you to remove code that performs
fi
])
-dnl Select auth modules to build
-AUTH_MODULES=
+
+dnl Select auth schemes modules to build
+AC_ARG_ENABLE(auth,
+[ --enable-auth=\"list of auth scheme modules\"
+ Build support for the list of authentication schemes.
+ The default is to build support for the Basic scheme.
+ See src/auth for a list of available modules, or
+ Programmers Guide section authentication schemes
+ for details on how to build your custom auth scheme
+ module],
+[ case $enableval in
+ yes)
+ for module in $srcdir/src/auth/*; do
+ if test -f $module/Makefile.in; then
+ AUTH_MODULES="$AUTH_MODULES `basename $module`"
+ fi
+ done
+ ;;
+ no)
+ ;;
+ *) AUTH_MODULES="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`"
+ ;;
+ esac
+],
+[ if test -z "$AUTH_MODULES"; then
+ AUTH_MODULES="basic"
+ fi
+])
+echo "Auth scheme modules built: $AUTH_MODULES"
+AC_SUBST(AUTH_MODULES)
+AUTH_OBJS="auth/`echo $AUTH_MODULES|sed -e's% %.a auth/%g'`.a"
+AC_SUBST(AUTH_OBJS)
+AUTH_LIBS="`echo $AUTH_OBJS|sed -e's%auth/%%g'`"
+AC_SUBST(AUTH_LIBS)
+
+dnl Select basic auth scheme helpers to build
+BASIC_AUTH_HELPERS=""
AC_ARG_ENABLE(auth-modules,
-[ --enable-auth-modules=\"list of modules\"
- This option selects wich proxy_auth helper modules
- to build and install as part of the normal build
+[ --enable-auth-modules=\"list of helpers\"
+ Backwards compability alias for
+ --enable-basic-auth-helpers],
+[ echo "--enable-auth-modules is obsolete. Please use the new"
+ echo "option --enable-basic-auth-helpers"
+ sleep 5
+ case "$enableval" in
+ yes)
+ for helper in $srcdir/src/auth/basic/helpers/*; do
+ if test -f $helper/Makefile.in; then
+ AUTH_BASIC_HELPERS="$AUTH_BASIC_HELPERS `basename $helper`"
+ fi
+ done
+ ;;
+ no)
+ ;;
+ *)
+ AUTH_BASIC_HELPERS="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`"
+ esac
+])
+AC_ARG_ENABLE(basic-auth-helpers,
+[ --enable-basic-auth-helpers=\"list of helpers\"
+ This option selects which basic scheme proxy_auth
+ helpers to build and install as part of the normal
+ build process. For a list of available
+ helpers see the src/auth/basic/helpers directory.],
+[ case "$enableval" in
+ yes)
+ BASIC_AUTH_HELPERS=""
+ for helper in $srcdir/src/auth/basic/helpers/*; do
+ if test -f $helper/Makefile.in; then
+ AUTH_BASIC_HELPERS="$AUTH_BASIC_HELPERS `basename $helper`"
+ fi
+ done
+ ;;
+ no)
+ ;;
+ *)
+ AUTH_BASIC_HELPERS="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`"
+ esac
+])
+if test -n "$AUTH_BASIC_HELPERS"; then
+ echo "Basic auth helpers built: $AUTH_BASIC_HELPERS"
+fi
+AC_SUBST(AUTH_BASIC_HELPERS)
+
+dnl Select ntlm auth helpers to build
+NTLM_AUTH_HELPERS=
+AC_ARG_ENABLE(ntlm-auth-helpers,
+[ --enable-ntlm-auth-helpers=\"list of helpers\"
+ This option selects which proxy_auth ntlm helpers
+ to build and install as part of the normal build
process. For a list of available modules see
- the auth_modules directory.],
+ the src/auth/ntlm/helpers directory.],
[ case "$enableval" in
yes)
- for module in $srcdir/auth_modules/*; do
- if test -f $module/Makefile.in; then
- AUTH_MODULES="$AUTH_MODULES `basename $module`"
+ for helper in $srcdir/src/auth/ntlm/helpers/*; do
+ if test -f $helper/Makefile.in; then
+ NTLM_AUTH_HELPERS="$NTLM_AUTH_HELPERS `basename $helper`"
fi
done
;;
no)
;;
*)
- AUTH_MODULES="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`"
+ NTLM_AUTH_HELPERS="`echo $enableval| sed -e 's/,/ /g;s/ */ /g'`"
esac
])
-if test -n "$AUTH_MODULES"; then
- echo "Auth moules built: $AUTH_MODULES"
+if test -n "$NTLM_AUTH_HELPERS"; then
+ echo "NTLM auth helpers built: $NTLM_AUTH_HELPERS"
fi
-AC_SUBST(AUTH_MODULES)
+AC_SUBST(NTLM_AUTH_HELPERS)
+
dnl Disable "unlinkd" code
AC_ARG_ENABLE(unlinkd,
fi
done
-AUTH_MAKEFILES=""
-for module in $srcdir/auth_modules/*; do
- if test -f $module/Makefile.in; then
- AUTH_MAKEFILES="$AUTH_MAKEFILES ./auth_modules/`basename $module`/Makefile"
+AUTH_SCHEME_MAKEFILES=""
+for auth in $AUTH_MODULES none; do
+ if test $auth != none; then
+ AUTH_SCHEME_MAKEFILES="$AUTH_SCHEME_MAKEFILES ./src/auth/$auth/Makefile"
+ fi
+done
+
+BASIC_AUTH_MAKEFILES=""
+for helper in $srcdir/src/auth/basic/helpers/*; do
+ if test -f $helper/Makefile.in; then
+ BASIC_AUTH_MAKEFILES="$BASIC_AUTH_MAKEFILES ./src/auth/basic/helpers/`basename $helper`/Makefile"
fi
done
+NTLM_AUTH_MAKEFILES=""
+for helper in $srcdir/src/auth/ntlm/helpers/*; do
+ if test -f $helper/Makefile.in; then
+ NTLM_AUTH_MAKEFILES="$NTLM_AUTH_MAKEFILES ./src/auth/ntlm/helpers/`basename $helper`/Makefile"
+ for submodule in $helper/*; do
+ if test -f $submodule/Makefile.in; then
+ NTLM_AUTH_MAKEFILES="$NTLM_AUTH_MAKEFILES ./src/auth/ntlm/helpers/`basename $helper`/`basename $submodule`/Makefile"
+ fi
+ done
+ fi
+done
+
+
AC_OUTPUT(\
./makefile \
./lib/Makefile \
$FS_MAKEFILES \
./src/repl/Makefile \
$REPL_MAKEFILES \
+ ./src/auth/Makefile \
+ $AUTH_SCHEME_MAKEFILES \
+ ./src/auth/basic/helpers/Makefile \
+ $BASIC_AUTH_MAKEFILES \
+ ./src/auth/ntlm/helpers/Makefile \
+ $NTLM_AUTH_MAKEFILES \
./contrib/Makefile \
$SNMP_MAKEFILE \
./icons/Makefile \
./errors/Makefile \
- ./auth_modules/Makefile \
- $AUTH_MAKEFILES \
)
<article>
<title>Squid Programmers Guide</title>
<author>Duane Wessels, Squid Developers
-<date>$Id: prog-guide.sgml,v 1.33 2001/01/06 11:39:43 hno Exp $</date>
+<date>$Id: prog-guide.sgml,v 1.34 2001/01/07 23:36:35 hno Exp $</date>
<abstract>
Squid is a WWW Cache application developed by the National Laboratory
information and continues the access control checks when
the information is available.
+<sect1>Authentication Framework
+
+ <P>
+ These functions are responsible for handling HTTP authentication.
+ They follow a modular framework allow different auth schemes
+ to be added at will. For information on working with the auth schemes
+ See the chapter Authentication Framework.
+
<sect1>Network Communication
<P>
<P>
To be written...
+<!-- %%%% Chapter : Authentication Framework %%%% -->
+<sect>Authentication Framework
+
+ <P>
+ <enum>
+ <item>Definition of an auth scheme.
+ <item>Data types
+ <item>How to add a new auth scheme
+ <item>How to 'hook in' new functions to the API.</enum>
+
+ <P>
+ Definition of an auth scheme
+
+ <P>An auth scheme in squid is the collection of functions required to
+ manage the authentication process for a given HTTP authentication
+ scheme. Existing auth schemes in squid are Basic and NTLM. Other HTTP
+ schemes (see for example rfc 2617) have been published and could be
+ implemented in squid. The term auth scheme and auth module are
+ interchangable. An auth module is not to be confused with an
+ authentication helper, which is a scheme specific external program used
+ by a specific scheme to perform data manipulation external to squid.
+ Typically this involves comparing the browser submitted credentials with
+ those in the organisation's user directory.
+
+ <P>Auth modules SHOULD NOT perform access control functions. Squid has
+ advanced caching access control functionality already. Future work in
+ squid will allow a auth scheme helper to return group information for a
+ user, to allow Squid to more seamlessly implement access control.
+
+ <P>Data types
+
+ <P>The data types are presented in C for the simple reason that squid is
+ currently written exclusively in C.
+
+ <P>Function typedefs.
+
+ <P>Each function related to the general case of http authentication has
+ a matching typedef. There are some additional function types used to
+ register/initialise, deregister/shutdown and provide stats on auth
+ modules:
+
+ <P>typedef int AUTHSACTIVE();
+
+ <P>The Active function is used by squid to determine whether the auth
+ module has successfully configured and initialised itself. If Active
+ returns 0 no other module functions except Shutdown/Dump/Parse/FreeConfig
+ will be called by Squid.
+
+ <P>typedef void AUTHSSETUP(authscheme_entry_t *);
+
+ <P>functions of type AUTHSSETUP are used to register an auth module with
+ squid. The registration function MUST be named
+ "authSchemeSetup_SCHEME" where SCHEME is the auth_scheme as
+ defined by rfc 2617. Only one auth scheme registered in squid can
+ provide functionality for a given auth_scheme. (I.e. only one auth
+ module can handle Basic, only one can handle Digest and so forth). The
+ Setup function is responsible for registering the functions in the
+ auth module into the passed authscheme_entry_t. The authscheme_entry_t
+ will never be NULL. If it is NULL the auth module should log an error
+ and do nothing. The other functions can have any desired name that does
+ not collide with any statically linked function name within Squid. It is
+ recommended to use names of the form "authe_SCHEME_FUNCTIONNAME" (for example
+ authenticate_NTLM_Active is the Active() function for the NTLM auth
+ module.
+
+ <P>typedef void AUTHSSHUTDOWN(void);
+
+ <P>Functions of type AUTHSSHUTDOWN are responsible for freeing any
+ resources used by the auth modules. The shutdown function will be called
+ before squid reconfigures, and before squid shutsdown.
+
+ <P>typedef void AUTHSINIT(authScheme *);
+
+ <P>Functions of type AUTHSINIT are responsible for allocating any
+ needed resources for the authentication module. AUTHSINIT functions are
+ called after each configuration takes place before any new requests are
+ made.
+
+ <P>typedef void AUTHSPARSE(authScheme *, int, char *);
+
+ <P>Functions of type AUTHSPARSE are responsible for parsing
+ authentication parameters. The function currently needs a scheme scope
+ data structure to store the configuration in. The passed scheme's
+ scheme_data pointer should point to the local data structure. Future
+ development will allow all authentication schemes direct access to their
+ configuration data without a locally scope structure. The parse function
+ is called by squid's config file parser when a auth_param scheme_name
+ entry is encountered.
+
+ <P>typedef void AUTHSFREECONFIG(authScheme *);
+
+ <P>Functions of type AUTHSFREECONFIG are called by squid when freeing
+ configuration data. The auth scheme should free any memory allocated
+ that is related to parse data structures. The scheme MAY take advantage
+ of this call to remove scheme local configuration dependent data. (Ie
+ cached user details that are only relevant to a config setting).
+
+ <P>typedef void AUTHSDUMP(StoreEntry *, const char *, authScheme *);
+
+ <P>Functions of type AUTHSDUMP are responsible for writing to the
+ StoreEntry the configuration parameters that a user would put in a
+ config file to recreate the running configuration.
+
+ <P>typedef void AUTHSSTATS(StoreEntry *);
+ <P>Functions of type AUTHSSTATS are called by the cachemgr to provide
+ statistics on the authmodule. Current modules simply provide the
+ statistics from the back end helpers (number of requests, state of the
+ helpers), but more detailed statistics are possible - for example unique
+ users seen or failed authentication requests.
+ <P>The next set of functions work on the data structures used by the
+ authentication schemes.
+
+ <P>typedef void AUTHSREQFREE(auth_user_request_t *);
+
+ <P>The AUTHSREQFREE function is called when a auth_user_request is being
+ freed by the authentication framework, and scheme specific data was
+ present. The function should free any scheme related data and MUST set
+ the scheme_data pointer to NULL. Failure to unlink the scheme data will
+ result in squid dieing.
+
+ <P>typedef char *AUTHSUSERNAME(auth_user_t *);
+
+ <P>Squid does not make assumptions about where the username is stored.
+ This function must return a pointer to a NULL terminated string to be
+ used in logging the request. Return NULL if no username/usercode is
+ known. The string should NOT be allocated each time this function is
+ called.
+
+ <P>typedef int AUTHSAUTHED(auth_user_request_t *);
+
+ <P>The AUTHED function is used by squid to determine whether the auth
+ scheme has successfully authenticated the user request. If timeouts on
+ cached credentials have occured or for any reason the credentials are
+ not valid, return false.<P>The next set of
+ functions perform the actual authentication. The functions are used by
+ squid for both WWW- and Proxy- authentication. Therefore they MUST NOT
+ assume the authentication will be based on the Proxy-* Headers.
+
+ <P>typedef void AUTHSAUTHUSER(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type);
+ <P>Functions of type AUTHSAUTHUSER are called when Squid has a request
+ that needs authentication. If needed the auth scheme can alter the
+ auth_user pointer (usually to point to a previous instance of the user
+ whose name is discovered late in the auth process. For an example of
+ this see the ntlm scheme). These functions are responsible for
+ performing any in-squid routines for the authentication of the user. The
+ auth_user_request struct that is passed around is only persistent for
+ the current request. If the auth module requires access to the structure
+ in the future it MUST lock it, and implement some method for identifying
+ it in the future. For example the NTLM module implements a connection
+ based authentication scheme, so the auth_user_request struct gets
+ referenced from the ConnStateData.
+
+ <P>typedef void AUTHSDECODE(auth_user_request_t *, const char *);
+
+ <P>Functions of type AUTHSDECODE are responsible for decoding the passed
+ authentication header, creating or linking to a auth_user struct and for
+ storing any needed details to complete authentication in AUTHSAUTHUSER.
+
+ <P>typedef int AUTHSDIRECTION(auth_user_request_t *);
+
+ <P>Functions of type AUTHSDIRECTION are used by squid to determine what
+ the next step in performing authentication for a given scheme is. The
+ following are the return codes:
+
+ -2 = error in the auth module. Cannot determine request direction.
+ -1 = the auth module needs to send data to an external helper.
+ Squid will prepare for a callback on the request and call the
+ AUTHSSTART function.
+ 0 = the auth module has all the information it needs to
+ perform the authentication and provide a succeed/fail result.
+ 1 = the auth module needs to send a new challenge to the
+ request originator. Squid will return the appropriate status code
+ (401 or 407) and call the registered FixError function to allow the
+ auth module to insert it's challenge.
+
+ <P>typedef void AUTHSFIXERR(auth_user_request_t *, HttpReply *, http_hdr_type, request_t *);
+
+ <P>Functions of type AUTHSFIXERR are used by squid to add scheme
+ specific challenges when returning a 401 or 407 error code. On requests
+ where no authentication information was provided, all registered auth
+ modules will have their AUTHSFIXERR function called. When the client
+ makes a request with an authentication header, on subsequent calls only the matching
+ AUTHSFIXERR function is called (and then only if the auth module
+ indicated it had a new challenge to send the client). If no auth schemes
+ match the request, the authentication credentials in the request are
+ ignored - and all auth modules are called.
+
+ <P>typedef void AUTHSFREE(auth_user_t *);
+
+ <P>These functions are responsible for freeing scheme specific data from
+ the passed auth_user_t structure. This should only be called by squid
+ when there are no outstanding requests linked to the auth user. This includes
+ removing the user from any scheme specific memory caches.
+
+ <P>typedef void AUTHSADDHEADER(auth_user_request_t *, HttpReply *, int);
+ typedef void AUTHSADDTRAILER(auth_user_request_t *, HttpReply *, int);
+
+ <P>These functions are responsible for adding any authentication
+ specific header(s) or trailer(s) OTHER THAN the WWW-Authenticate and
+ Proxy-Authenticate headers to the passed HttpReply. The int indicates
+ whether the request was an accelerated request or a proxied request. For
+ example operation see the digest auth scheme. (Digest uses a
+ Authentication-Info header.) This function is called whenever a
+ auth_user_request exists in a request when the reply is constructed
+ after the body is sent on chunked replies respectively.
+
+ <P>typedef void AUTHSONCLOSEC(ConnStateData *);
+
+ <P>This function type is called when a auth_user_request is
+ linked into a ConnStateData struct, and the connection is closed. If any
+ scheme specific activities related to the request or connection are in
+ progress, this function MUST clear them.
+
+ <P>typedef void AUTHSSTART(auth_user_request_t * , RH * , void *);
+
+ <P>This function type is called when squid is ready to put the request
+ on hold and wait for a callback from the auth module when the auth
+ module has performed it's external activities.
+
+ <P>Structures
+
+ <P>This is used to link auth_users into the username cache. Because some
+ schemes may link in aliases to a user, the link is not part of the
+ auth_user structure itself.
+
+ <P>struct _auth_user_hash_pointer {
+ /* first two items must be same as hash_link */
+ char *key;
+ auth_user_hash_pointer *next;
+ auth_user_t *auth_user;
+ dlink_node link; /* other hash entries that point to the same auth_user */
+ };
+
+ <P>This is the main user related structure. It stores user-related data,
+ and is persistent across requests. It can even persistent across
+ multiple external authentications. One major benefit of preserving this
+ structure is the cached acl match results. This structure, is private to
+ the authentication framework.
+
+ <P>struct _auth_user_t {
+ /* extra fields for proxy_auth */
+ /* this determines what scheme owns the user data. */
+ auth_type_t auth_type;
+ /* the index +1 in the authscheme_list to the authscheme entry */
+ int auth_module;
+ /* we only have one username associated with a given auth_user struct */
+ auth_user_hash_pointer *usernamehash;
+ /* we may have many proxy-authenticate strings that decode to the same user*/
+ dlink_list proxy_auth_list;
+ dlink_list proxy_match_cache;
+ struct {
+ unsigned int credentials_ok:2; /*0=unchecked,1=ok,2=failed*/
+ } flags;
+ long expiretime;
+ /* IP addr this user authenticated from */
+ struct in_addr ipaddr;
+ time_t ip_expiretime;
+ /* how many references are outstanding to this instance*/
+ size_t references;
+ /* the auth scheme has it's own private data area */
+ void *scheme_data;
+ /* the auth_user_request structures that link to this. Yes it could be a splaytree
+ * but how many requests will a single username have in parallel? */
+ dlink_list requests;
+ };
+
+ <P>This is a short lived structure is the visible aspect of the
+ authentication framework.
+
+ <P>struct _auth_user_request_t {
+ /* this is the object passed around by client_side and acl functions */
+ /* it has request specific data, and links to user specific data */
+ /* the user */
+ auth_user_t *auth_user;
+ /* return a message on the 401/407 error pages */
+ char *message;
+ /* any scheme specific request related data */
+ void *scheme_data;
+ /* how many 'processes' are working on this data */
+ size_t references;
+ };
+
+ The authscheme_entry struct is used to store the runtime registered
+ functions that make up an auth scheme. An auth scheme module MUST implement
+ ALL functions except the
+ following functions: oncloseconnection, AddHeader, AddTrailer.. In
+ the future more optional functions may be added to this data type.
+
+ <P>
+ struct _authscheme_entry {
+ char *typestr;
+ AUTHSACTIVE *Active;
+ AUTHSADDHEADER *AddHeader;
+ AUTHSADDTRAILER *AddTrailer;
+ AUTHSAUTHED *authenticated;
+ AUTHSAUTHUSER *authAuthenticate;
+ AUTHSDUMP *dump;
+ AUTHSFIXERR *authFixHeader;
+ AUTHSFREE *FreeUser;
+ AUTHSFREECONFIG *freeconfig;
+ AUTHSUSERNAME *authUserUsername;
+ AUTHSONCLOSEC *oncloseconnection; /*optional*/
+ AUTHSDECODE *decodeauth;
+ AUTHSDIRECTION *getdirection;
+ AUTHSPARSE *parse;
+ AUTHSINIT *init;
+ AUTHSREQFREE *requestFree;
+ AUTHSSHUTDOWN *donefunc;
+ AUTHSSTART *authStart;
+ AUTHSSTATS *authStats;
+ };
+
+ <P>For information on the requirements for each of the functions, see
+ the details under the typedefs above. For reference implementations, see
+ the squid source code, /src/auth/basic for a request based stateless auth module, and
+ /src/auth/ntlm for a connection based stateful auth module.
+
+ <P>How to add a new auth scheme
+
+ <P>Copy the nearest existing auth scheme and modify to recieve the
+ approprate scheme headers. Now step through the acl.c MatchAclProxyUser
+ function's code path and see how the functions call down through
+ authenticate.c to your scheme. Write a helper to provide you scheme with
+ any backend existence it needs. Remember any blocking code must go in
+ AUTHSSTART function(s) and _MUST_ use callbacks.
+
+ <P>How to 'hook in' new functions to the API.
+
+ <P>Start of by figuring the code path that will result in the function
+ being called, and what data it will need. Then create a typedef for the
+ function, add and entry to the authscheme_entry struct. Add a wrapper
+ function to authenticate.c (or if approprate cf_cache.c) that called the
+ scheme specific function if it exists. Test it. Test it again. Now
+ port to all the existing auth schemes, or at least add a setting
+ of NULL for the function for each scheme.
+
+
<!-- %%%% Chapter : ICP %%%% -->
<sect>ICP
section 26 Secure Sockets Layer Proxy
section 27 Cache Announcer
section 28 Access Control
-section 29 Redirector
+section 29 Redirector & Authentication
section 30 Ident (RFC 931)
section 31 Hypertext Caching Protocol
section 32 Asynchronous Disk I/O
--- /dev/null
+
+OBJS = squid_ldap_auth.o
+LIBS = -lldap -llber
+LDAP_EXE = squid_ldap_auth
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+all: $(LDAP_EXE)
+
+$(LDAP_EXE): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(XTRA_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(libexecdir); then \
+ echo "mkdir $(libexecdir)"; \
+ mkdir $(libexecdir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(LDAP_EXE); do \
+ if test -f $(libexecdir)/$$f; then \
+ echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(libexecdir); \
+ $(INSTALL_BIN) $$f $(libexecdir); \
+ if test -f $(libexecdir)/-$$f; then \
+ echo $(RM) -f $(libexecdir)/-$$f; \
+ $(RM) -f $(libexecdir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -$(RM) -f $(OBJS)
+ -$(RM) -f $(LDAP_EXE)
+
+distclean: clean
+ -$(RM) -f Makefile
--- /dev/null
+This LDAP Authentication code is written by Glen Newton
+<gnewton@wapiti.cisti.nrc.ca>.
+
+Please see his Web page at:
+http://orca.cisti.nrc.ca/~gnewton/opensource/squid_ldap_auth/
+
+In order to use squid_ldap_auth, you will also need to install
+the OpenLDAP libraries (ldap lber) from http://www.openldap.org.
--- /dev/null
+/*
+ *
+ * squid_ldap_auth: authentication via ldap for squid proxy server
+ *
+ * Author: Glen Newton
+ * glen.newton@nrc.ca
+ * Advanced Services
+ * CISTI
+ * National Research Council
+ *
+ * Usage: squid_ldap_auth <ldap_server_name>
+ *
+ * Dependencies: You need to get the OpenLDAP libraries
+ * from http://www.openldap.org
+ *
+ * License: squid_ldap_auth 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,
+ * or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <lber.h>
+#include <ldap_cdefs.h>
+#include <ldap.h>
+
+/* Change this to your search base */
+#define SEARCHBASE "ou=people,o=nrc.ca"
+
+int checkLDAP(LDAP * ld, char *userid, char *password);
+
+int
+main(int argc, char **argv)
+{
+ char buf[256];
+ char *user, *passwd, *p;
+ char *ldapServer;
+ LDAP *ld;
+ LDAPMessage *result, *e;
+
+ setbuf(stdout, NULL);
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: squid_ldap_auth ldap_server_name\n");
+ exit(1);
+ }
+ ldapServer = (char *) argv[1];
+
+ while (fgets(buf, 256, stdin) != NULL) {
+ /* You can put this ldap connect outside the loop, but i didn't want to
+ * have the connection open too much. If you have a site which will
+ * be doing >1 authentication per second, you should move this (and the
+ * below ldap_unbind()) outside the loop.
+ */
+ if ((ld = ldap_init(ldapServer, LDAP_PORT)) == NULL) {
+ fprintf(stderr, "\nUnable to connect to LDAP server:%s port:%d\n",
+ ldapServer, LDAP_PORT);
+ exit(1);
+ }
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+
+ if ((user = strtok(buf, " ")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ if ((passwd = strtok(NULL, "")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ if (checkLDAP(ld, user, passwd) != 0) {
+ printf("ERR\n");
+ continue;
+ } else {
+ printf("OK\n");
+ }
+ ldap_unbind(ld);
+ }
+}
+
+
+
+int
+checkLDAP(LDAP * ld, char *userid, char *password)
+{
+ char str[256];
+
+ /*sprintf(str,"uid=[%s][%s], %s",userid, password, SEARCHBASE); */
+ sprintf(str, "uid=%s, %s", userid, SEARCHBASE);
+
+ if (ldap_simple_bind_s(ld, str, password) != LDAP_SUCCESS) {
+ /*fprintf(stderr, "\nUnable to bind\n"); */
+ return 33;
+ }
+ return 0;
+}
--- /dev/null
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:44 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+AUTH_EXE = msnt_auth$(exec_suffix)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+SNMPLIB = @SNMPLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+DEFINES =
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = $(XTRA_LIBS)
+
+LIBPROGS = $(AUTH_EXE)
+OBJS = md4.o rfcnb-io.o rfcnb-util.o session.o msntauth.o \
+ smbdes.o smbencrypt.o smblib-util.o smblib.o \
+ valid.o denyusers.o allowusers.o confload.o
+
+all: $(AUTH_EXE)
+
+$(AUTH_EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(libexecdir); then \
+ echo "mkdir $(libexecdir)"; \
+ mkdir $(libexecdir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(LIBPROGS); do \
+ if test -f $(libexecdir)/$$f; then \
+ echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(libexecdir); \
+ $(INSTALL_BIN) $$f $(libexecdir); \
+ if test -f $(libexecdir)/-$$f; then \
+ echo $(RM) -f $(libexecdir)/-$$f; \
+ $(RM) -f $(libexecdir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *.a *pure_* core $(LIBPROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+tags:
+ ctags *.[ch]
+
+depend:
+ $(MAKEDEPEND) -fMakefile *.c
--- /dev/null
+<HTML>
+<HEAD>
+<TITLE>MSNTAUTH readme</TITLE>
+</HEAD>
+<BODY BGCOLOR="#FFFFFF">
+
+<!--
+If you require this document in text form, download the
+HTML-text package from http://members.tripod.com/~stellarx.
+-->
+
+<H1>
+MSNT Auth v2.0.1<BR>
+Squid web proxy Authentication module<BR>
+Antonino Iannella, Stellar-X Pty Ltd<BR>
+Fri Sep 29 15:53:33 CST 2000
+</H1>
+
+<H2>Contents</H2>
+
+<UL>
+<LI> <A HREF="#introduction">Introduction</A>
+<LI> <A HREF="#installation">Installation</A>
+<LI> <A HREF="#compiling">Other compiling issues</A>
+<LI> <A HREF="#configuration">Configuration file</A>
+<LI> <A HREF="#denying">Denying users</A>
+<LI> <A HREF="#allowing">Allowing users</A>
+<LI> <A HREF="#squid">Squid.conf changes</A>
+<LI> <A HREF="#testing">Testing</A>
+<LI> <A HREF="#contact">Contact details</A>
+<LI> <A HREF="#reported">Reported problem</A>
+<LI> <A HREF="#known">Known limitation</A>
+<LI> <A HREF="#changes">Changes since last revision</A>
+</UL>
+
+<A NAME="introduction"><H2>Introduction</H2>
+
+<P>
+This is an authentication module for the Squid proxy server
+to authenticate users on an NT domain.
+
+<P>
+It originates from the Samba and SMB packages by Andrew Tridgell
+and Richard Sharpe. This version is sourced from the Pike
+authentication module by William Welliver (hwellive@intersil.com).
+
+<P>
+Usage is simple. It accepts a username and password on standard input
+and will return OK if the username/password is valid for the domain,
+or ERR if there was some problem.
+Check syslog messages for reported problems.
+
+<P>
+Msntauth is released under the GNU General Public License and
+is available from http://stellarx.tripod.com.
+
+<A NAME="installation"><H2>Installation</H2>
+
+<P>
+Make any changes to the source code you need.
+
+<P>
+Type 'make', then 'make install', then 'make clean'.
+
+<P>
+To avoid using the makefile, it may compile with
+
+ gcc -O2 -s -o msntauth *.c
+
+<P>
+'Make install' will put 'msntauth' into
+/usr/local/squid/bin by default.
+
+<P>
+Hopefully nobody has problems compiling msntauth.
+In the future I plan to use GNU automake.
+
+<A NAME="compiling"><H2>Other compiling issues</H2>
+
+<P>
+The Makefile uses the GCC compiler, and assumes that it is in the current PATH.
+Msntauth is known to compile properly on Redhat Linux 6, and FreeBSD 3.1
+without problems. Other operating systems are untested,
+but use a recent copy of the GNU C compiler.
+Smbencrypt.c has the '#include <sys/vfs.h>' line commented out.
+Remove the comment for S5R4 systems, like Solaris.
+
+<P>
+When compiling under Solaris, the socket libraries must be linked to.
+In the Makefile, hash the default CFLAGS line, and unhash the Solaris
+CFLAGS line. It always helps to have /usr/ccs/bin in your path
+prior to compiling.
+
+<A NAME="configuration"><H2>Configuration file</H2>
+
+<P>
+Msntauth uses a configuration file which is a break from previous
+releases. The file is /usr/local/squid/etc/msntauth.conf.
+If this needs to be changed, it is defined in confload.h.
+
+<P>
+An example configuration file is provided. It looks like
+
+<PRE>
+# Sample MSNT authenticator configuration file
+# Antonino Iannella, Stellar-X Pty Ltd
+# Tue Sep 26 17:26:59 CST 2000
+
+server my_PDC my_BDC my_NTdomain
+server other_PDC other_BDC otherdomain
+
+denyusers /usr/local/squid/etc/denyusers
+allowusers /usr/local/squid/etc/allowusers
+</PRE>
+
+<P>
+All comments start with '#'.
+
+<P>
+NT servers are used to query user accounts. The 'server' lines
+are used for this, with the PDC, BDC, and NT domain as parameters.
+Up to 5 servers/domains can be queried. If this is not enough
+modify the MAXSERVERS define in confload.h.
+At least one server must be specified, or msntauth will not
+run.
+
+<P>
+When a user provides a username/password, each of these
+servers will be queried to authenticate the username.
+It stops after a user has been successfully authenticated,
+so it makes sense to specify the most commonly queried
+server first. Make sure the servers can be reached and
+are active, or else msntauth will start failing user accounts!
+
+<P>
+The 'denyusers' and 'allowusers' lines give the absolute path
+to files of user accounts. They can be used to deny or allow
+access to the proxy. Do not use these directives if you
+do not need these features.
+
+<A NAME="denying"><H2>Denying users</H2>
+
+<P>
+Users who are not allowed to access the web proxy can be added to
+the denied user list. This list is read around every minute, or when
+the msntauth process receives a SIGHUP signal.
+
+<P>
+The denied user file is set using the 'denyusers' directive
+in msntauth.h. The denied user file
+contains a list of usernames in no particular structure or form.
+If the file does not exist, no users are denied.
+The file must be readable by the web proxy user.
+
+<P>
+Msntauth will send syslog messages if a user was denied,
+at LOG_USER facility.
+
+<A NAME="allowing"><H2>Allowing users</H2>
+
+<P>
+Similar to denying users, you can allow users to access the proxy
+by username. This is useful if only a number of people are
+allowed supposed to be accessing a proxy.
+
+<P>
+The allowed user file is set using the 'allowusers' directive
+in msntauth.h.
+If the file does not exist or if empty, all users are allowed.
+
+<P>
+You could make use of the SHOWMBRS tool in Microsoft Technet.
+This gives you a list of users which are in a particular
+NT Domain Group. This list can be made into the allowed users
+file.
+
+<P>
+Some other rules -
+
+<OL>
+<LI> The operation of the denied user file is independent of the
+allowed user file. The former file is checked first.
+<LI> You can use none, one, or both files.
+<LI> If a username appears in the denied user file, they will
+be denied, even if they are in the allowed user file.
+<LI> If a username is not in either file, they will be denied,
+because they have not been allowed.
+<LI> If the allowed user file is in use and is empty, all
+users will be allowed.
+</OL>
+
+<P>
+Hopefully this wasn't too confusing.
+
+<A NAME="squid"><H2>Squid.conf changes</H2>
+
+<P>
+Refer to Squid documentation for the required changes to squid.conf.
+You will need to set the following lines to enable authentication for
+your access list -
+
+<PRE>
+ acl <yourACL> proxy_auth REQUIRED
+ http_access allow password
+ http_access allow <yourACL>
+ http_access deny all
+
+</PRE>
+
+<P>
+You will also need to review the following directives -
+
+<PRE>
+ proxy_auth_realm enterprise web gateway
+ authenticate_program /usr/local/squid/bin/msntauth
+ authenticate_ttl 5
+ authenticate_children 20
+</PRE>
+
+<A NAME="testing"><H2>Testing</H2>
+
+<P>
+I strongly urge that Msntauth is tested prior to being used in a
+production environment. It may behave differently on different platforms.
+To test it, run it from the command line. Enter username and password
+pairs separated by a space.
+
+<P>
+It should behave in the following way -
+<PRE>
+ - Press ENTER to get an OK or ERR message.
+ - Make sure pressing CTRL-D behaves the same as a carriage return.
+ - Make sure pressing CTRL-C aborts the program.
+ - Test that entering no details does not result in an OK or ERR message.
+ - Test that entering an invalid username and password results in
+ an ERR message. Note that if NT guest user access is allowed on
+ the PDC, an OK message may be returned instead of ERR.
+ - Test that entering an valid username and password results in an OK message.
+ Try usernames which are and aren't in the denied/allowed user files,
+ if they're in use.
+ - Test that entering a guest username and password returns the correct response.
+</PRE>
+
+<P>
+If the above didn't work as expected, you may need to modify the main()
+function in msntauth.c. Inform the maintainer of any problems.
+
+<A NAME="contact"><H2>Contact details</H2>
+
+<P>
+To contact the maintainer of this package, email Antonino Iannella
+at antonino@usa.net, antonino.iannella@usa.net, or
+antonino.iannella@camtech.com.au.
+
+<P>
+The latest version may be found on http://members.tripod.com/stellarx.
+It is also distributed as part of Squid.
+
+<A NAME="reported"><H2>Reported problem</H2>
+
+<P>
+For an unknown username, Msntauth returns OK.
+This is because the PDC returns guest access for unknown users,
+even if guest access is disabled.
+This problem was reported by Mr Vadim Popov (vap@iilsr.minsk.by).
+I am not able to replicate this.
+
+<P>
+The tested environment consisted of PDC on Windows NT 4, SP 6.
+Squid 2.3 and Msntauth was tested on SuSe, RedHat, and Debian Linux.
+A fix was provided in case you have this problem.
+Apply the provided patch before compiling, using
+
+<PRE>
+ patch smblib.c < smblib.c.patch
+</PRE>
+
+<A NAME="known"><H2>Known limitation</H2>
+
+<P>
+Usernames are checked if they are allowed or denied. If a username
+is found as a substring of a different username in these files,
+the user will be affected somehow. For example, if 'jpeterman' has
+been explicitly denied in the denyusers file, then 'jpeter' who
+is trying to use the proxy, will be denied. If this causes anyone
+any problems, then I'll fix it.
+
+<P>
+As of version 2.0.1, this problem has been fixed.
+
+<A NAME="changes"><H2>Changes since last revision</H2>
+
+<P>
+The following list of changes have been made to improve msntauth.
+I have not had a chance to do too much testing due
+to lack of resources. There should be no problems, though.
+
+<UL>
+<LI>Added many patches from Duane Wessels to stop compilation errors (?)
+<LI>Improved the main() function yet again
+<LI>Created a more informative Makefile
+<LI>Added an 'allowed users' feature to complement the 'denied users' feature
+<LI>Stopped the use of alarm() which was causing problems under Solaris
+<LI>Added more syslog messages for authentication problems
+<LI>Added the use of a configuration file, instead of hard-coding NT server details
+<LI>Allowed for querying multiple NT servers and domains (this was a hot issue)
+<LI>Changed README into an HTML document to improve readability
+<LI>Didn't make use of GNU autoconf. I will in future, I promise.
+<LI>Removed denied/allowed username substring search limitation.
+</UL>
+
+<P>
+Hopefully msntauth and Squid is now a more valuable product.
+Feel free to send me success or problem stories.
+
+</BODY>
+</HTML>
--- /dev/null
+
+/*
+ * allowusers.c
+ * (C) 2000 Antonino Iannella, Stellar-X Pty Ltd
+ * Released under GPL, see COPYING-2.0 for details.
+ *
+ * These routines are to allow users attempting to use the proxy which
+ * have been explicitly allowed by the system administrator.
+ * The code originated from denyusers.c.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+#define NAMELEN 50 /* Maximum username length */
+
+/* Global variables */
+
+char *AllowedUsers; /* Pointer to string of allowed users */
+off_t AllowUserSize; /* Size of allowed users file */
+struct stat FileBuf; /* Stat data buffer */
+time_t LastModTime; /* Last allowed user file modification time */
+
+char Allowuserpath[MAXPATHLEN]; /* MAXPATHLEN defined in param.h */
+
+/* Function declarations */
+
+int Read_allowusers();
+int Check_ifuserallowed(char *);
+void Checkforchange();
+void Checktimer();
+
+/*
+ * Reads the allowed users file for all users to be permitted.
+ * Returns 0 if the user list was successfully loaded,
+ * and 1 in case of error.
+ * Logs any messages to the syslog daemon.
+ */
+
+int
+Read_allowusers()
+{
+ FILE *AFile; /* Allowed users file pointer */
+ off_t APos = 0; /* File counter */
+ char AChar; /* Character buffer */
+
+ /* Stat the file. If it does not exist, save the size as zero.
+ * Clear the allowed user string. Return. */
+ if (stat(Allowuserpath, &FileBuf) == -1) {
+ if (errno == ENOENT) {
+ LastModTime = (time_t) 0;
+ AllowUserSize = 0;
+ free(AllowedUsers);
+ AllowedUsers = malloc(sizeof(char));
+ AllowedUsers[0] = '\0';
+ return 0;
+ } else {
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return 1;
+ }
+ }
+ /* If it exists, save the modification time and size */
+ LastModTime = FileBuf.st_mtime;
+ AllowUserSize = FileBuf.st_size;
+
+ /* Handle the special case of a zero length file */
+ if (AllowUserSize == 0) {
+ free(AllowedUsers);
+ AllowedUsers = malloc(sizeof(char));
+ AllowedUsers[0] = '\0';
+ return 0;
+ }
+ /* Free and allocate space for a string to store the allowed usernames */
+ free(AllowedUsers);
+
+ if ((AllowedUsers = malloc(sizeof(char) * (AllowUserSize + 3))) == NULL) {
+ syslog(LOG_USER | LOG_ERR, "Read_allowusers: malloc(AllowedUsers) failed.");
+ return 1;
+ }
+ /* Open the allowed users file. Report any errors. */
+
+ if ((AFile = fopen(Allowuserpath, "r")) == NULL) {
+ syslog(LOG_USER | LOG_ERR, "Read_allowusers: Failed to open allowed user file.");
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return 1;
+ }
+ /* Read user names into the AllowedUsers string.
+ * Make sure each string is delimited by a space. */
+
+ AllowedUsers[APos++] = ' ';
+
+ while (!feof(AFile)) {
+ if ((AChar = fgetc(AFile)) == EOF)
+ break;
+ else {
+ if (isspace(AChar))
+ AllowedUsers[APos++] = ' ';
+ else
+ AllowedUsers[APos++] = toupper(AChar);
+ }
+ }
+
+ AllowedUsers[APos++] = ' ';
+ AllowedUsers[APos] = '\0';
+ fclose(AFile);
+ return 0;
+}
+
+/*
+ * Check to see if the username provided by Squid appears in the allowed
+ * user list. Returns 0 if the user was not found, and 1 if they were.
+ */
+
+int
+Check_ifuserallowed(char *ConnectingUser)
+{
+ static char CUBuf[NAMELEN + 1];
+ static int x;
+ static char AllowMsg[256];
+
+ /* If user string is empty, allow */
+ if (ConnectingUser[0] == '\0')
+ return 1;
+
+ /* If allowed user list is empty, allow all users.
+ * If no users are supposed to be using the proxy, stop squid instead. */
+ if (AllowUserSize == 0)
+ return 1;
+
+ /* Check if username string is found in the allowed user list.
+ * If so, allow. If not, deny. Reconstruct the username
+ * to have whitespace, to avoid finding wrong string subsets. */
+
+ sscanf(ConnectingUser, " %s ", CUBuf);
+ sprintf(CUBuf, " %s ", CUBuf);
+
+ for (x = 0; x <= strlen(CUBuf); x++)
+ CUBuf[x] = toupper(CUBuf[x]);
+
+ if (strstr(AllowedUsers, CUBuf) != NULL)
+ return 1;
+ else { /* If NULL, they are not allowed to use the proxy */
+ sprintf(AllowMsg, "Denied access to user '%s'.", CUBuf);
+ syslog(LOG_USER | LOG_ERR, AllowMsg);
+ return 0;
+ }
+}
+
+/*
+ * Checks if there has been a change in the allowed users file.
+ * If the modification time has changed, then reload the allowed user list.
+ * This function is called by the SIGHUP signal handler.
+ */
+
+void
+Check_forallowchange()
+{
+ struct stat ChkBuf; /* Stat data buffer */
+
+ /* Stat the allowed users file. If it cannot be accessed, return. */
+
+ if (stat(Allowuserpath, &ChkBuf) == -1) {
+ if (errno == ENOENT) {
+ LastModTime = (time_t) 0;
+ AllowUserSize = 0;
+ free(AllowedUsers);
+ AllowedUsers = malloc(sizeof(char));
+ AllowedUsers[0] = '\0';
+ return;
+ } else { /* Report error when accessing file */
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return;
+ }
+ }
+ /* If found, compare the modification time with the previously-recorded
+ * modification time.
+ * If the modification time has changed, reload the allowed user list.
+ * Log a message of its actions. */
+
+ if (ChkBuf.st_mtime != LastModTime) {
+ syslog(LOG_USER | LOG_INFO, "Check_forallowchange: Reloading allowed user list.");
+ Read_allowusers();
+ }
+}
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * SMB Byte handling
+ * Copyright (C) Andrew Tridgell 1992-1995
+ *
+ * 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 _BYTEORDER_H_
+#define _BYTEORDER_H_
+
+/*
+ * This file implements macros for machine independent short and
+ * int manipulation
+ */
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right"
+ * byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val))
+
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+
+#if CAREFUL_ALIGNMENT
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+#else
+/* this handles things for architectures like the 386 that can handle
+ * alignment errors */
+/*
+ * WARNING: This section is dependent on the length of int16 and int32
+ * being correct
+ */
+#define SVAL(buf,pos) (*(uint16 *)((char *)(buf) + (pos)))
+#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
+#define SVALS(buf,pos) (*(int16 *)((char *)(buf) + (pos)))
+#define IVALS(buf,pos) (*(int32 *)((char *)(buf) + (pos)))
+#define SSVAL(buf,pos,val) SVAL(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS(buf,pos)=((int32)(val))
+#endif
+
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+
+#endif /* _BYTEORDER_H_ */
--- /dev/null
+
+/*
+ * confload.c
+ * (C) 2000 Antonino Iannella, Stellar-X Pty Ltd
+ * Released under GPL, see COPYING-2.0 for details.
+ *
+ * These routines load the msntauth configuration file.
+ * It stores the servers to query, sets the denied and
+ * allowed user files, and provides the
+ * authenticating function.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#define CONFIGFILE "/usr/local/squid/etc/msntauth.conf" /* Path to configuration file */
+#define DENYUSERSDEFAULT "/usr/local/squid/etc/denyusers"
+#define ALLOWUSERSDEFAULT "/usr/local/squid/etc/allowusers"
+
+#define MAXSERVERS 5 /* Maximum number of servers to query. This number can be increased. */
+#define NTHOSTLEN 65
+
+extern char Denyuserpath[MAXPATHLEN]; /* MAXPATHLEN defined in param.h */
+extern char Allowuserpath[MAXPATHLEN];
+
+typedef struct _ServerTuple {
+ char pdc[NTHOSTLEN];
+ char bdc[NTHOSTLEN];
+ char domain[NTHOSTLEN];
+} ServerTuple;
+
+ServerTuple ServerArray[MAXSERVERS]; /* Array of servers to query */
+int Serversqueried = 0; /* Number of servers queried */
+
+/* Declarations */
+
+int OpenConfigFile();
+void ProcessLine(char *);
+void AddServer(char *, char *, char *);
+int QueryServers(char *, char *);
+int QueryServerForUser(int, char *, char *);
+extern int Valid_User(char *, char *, char *, char *, char *);
+
+
+/*
+ * Opens and reads the configuration file.
+ * Returns 0 on success, or 1 for error.
+ */
+
+int
+OpenConfigFile()
+{
+ FILE *ConfigFile;
+ char Confbuf[2049]; /* Line reading buffer */
+
+ /* Initialise defaults */
+
+ Serversqueried = 0;
+ strcpy(Denyuserpath, DENYUSERSDEFAULT);
+ strcpy(Allowuserpath, ALLOWUSERSDEFAULT);
+
+ /* Open file */
+ if ((ConfigFile = fopen(CONFIGFILE, "r")) == NULL) {
+ syslog(LOG_USER | LOG_ERR, "OpenConfigFile: Failed to open %s.", CONFIGFILE);
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return 1;
+ }
+ /* Read in, one line at a time */
+
+ while (!feof(ConfigFile)) {
+ Confbuf[0] = '\0';
+ fgets(Confbuf, 2049, ConfigFile);
+ ProcessLine(Confbuf);
+ }
+
+ /* Check that at least one server is being queried. Report error if not.
+ * Denied and allowed user files are hardcoded, so it's fine if they're
+ * not set in the confugration file. */
+
+ if (Serversqueried == 0) {
+ syslog(LOG_USER | LOG_ERR, "OpenConfigFile: No servers set in %s. At least one is needed.", CONFIGFILE);
+ return 1;
+ }
+ fclose(ConfigFile);
+ return 0;
+}
+
+/* Parses a configuration file line. */
+
+void
+ProcessLine(char *Linebuf)
+{
+ char *Directive;
+ char *Param1;
+ char *Param2;
+ char *Param3;
+
+ /* Ignore empty lines */
+ if (strlen(Linebuf) == 0)
+ return;
+
+ /* Break up on whitespaces */
+ if ((Directive = strtok(Linebuf, " \t\n")) == NULL)
+ return;
+
+ /* Check for a comment line. If found, stop . */
+ if (Directive[0] == '#')
+ return;
+
+ /* Check for server line. Check for 3 parameters. */
+ if (strcasecmp(Directive, "server") == 0) {
+ Param1 = strtok(NULL, " \t\n");
+ Param2 = strtok(NULL, " \t\n");
+ Param3 = strtok(NULL, " \t\n");
+
+ if ((Param1[0] == '\0') ||
+ (Param2[0] == '\0') ||
+ (Param3[0] == '\0')) {
+ syslog(LOG_USER | LOG_ERR, "ProcessLine: A 'server' line needs PDC, BDC, and domain parameters.");
+ return;
+ }
+ AddServer(Param1, Param2, Param3);
+ return;
+ }
+ /* Check for denyusers line */
+ if (strcasecmp(Directive, "denyusers") == 0) {
+ Param1 = strtok(NULL, " \t\n");
+
+ if (Param1[0] == '\0') {
+ syslog(LOG_USER | LOG_ERR, "ProcessLine: A 'denyusers' line needs a filename parameter.");
+ return;
+ }
+ strcpy(Denyuserpath, Param1);
+ return;
+ }
+ /* Check for allowusers line */
+ if (strcasecmp(Directive, "allowusers") == 0) {
+ Param1 = strtok(NULL, " \t\n");
+
+ if (Param1[0] == '\0') {
+ syslog(LOG_USER | LOG_ERR, "ProcessLine: An 'allowusers' line needs a filename parameter.");
+ return;
+ }
+ strcpy(Allowuserpath, Param1);
+ return;
+ }
+ /* Reports error for unknown line */
+ syslog(LOG_USER | LOG_ERR, "ProcessLine: Ignoring '%s' line.", Directive);
+}
+
+/*
+ * Adds a server to query to the server array.
+ * Checks if the number of servers to query is not exceeded.
+ * Does not allow parameters longer than NTHOSTLEN.
+ */
+
+void
+AddServer(char *ParamPDC, char *ParamBDC, char *ParamDomain)
+{
+ if (Serversqueried + 1 > MAXSERVERS) {
+ syslog(LOG_USER | LOG_ERR, "ProcessLine: Ignoring '%s' server line; too many servers.", ParamPDC);
+ return;
+ }
+ Serversqueried++;
+ strncpy(ServerArray[Serversqueried].pdc, ParamPDC, NTHOSTLEN);
+ strncpy(ServerArray[Serversqueried].bdc, ParamBDC, NTHOSTLEN);
+ strncpy(ServerArray[Serversqueried].domain, ParamDomain, NTHOSTLEN);
+ ServerArray[Serversqueried].pdc[NTHOSTLEN - 1] = '\0';
+ ServerArray[Serversqueried].bdc[NTHOSTLEN - 1] = '\0';
+ ServerArray[Serversqueried].domain[NTHOSTLEN - 1] = '\0';
+}
+
+/*
+ * Cycles through all servers to query.
+ * Returns 0 if one server could authenticate the user.
+ * Returns 1 if no server authenticated the user.
+ */
+
+int
+QueryServers(char *username, char *password)
+{
+ int Queryresult = 1; /* Default result is an error */
+ int x = 1;
+
+ while (x <= Serversqueried) { /* Query one server. Change Queryresult if user passed. */
+ if (QueryServerForUser(x++, username, password) == 0) {
+ Queryresult = 0;
+ break;
+ }
+ }
+
+ return Queryresult;
+}
+
+/*
+ * Attempts to authenticate the user with one server.
+ * Logs syslog messages for different errors.
+ * Returns 0 on success, non-zero on failure.
+ */
+
+int
+QueryServerForUser(int x, char *username, char *password)
+{
+ int result = 1;
+
+ result = Valid_User(username, password, ServerArray[x].pdc,
+ ServerArray[x].bdc, ServerArray[x].domain);
+
+ switch (result) { /* Write any helpful syslog messages */
+ case 0:
+ break;
+ case 1:
+ syslog(LOG_AUTHPRIV | LOG_INFO, "Server error when checking %s.", username);
+ break;
+ case 2:
+ syslog(LOG_AUTHPRIV | LOG_INFO, "Protocol error when checking %s.", username);
+ break;
+ case 3:
+ syslog(LOG_AUTHPRIV | LOG_INFO, "Authentication failed for %s.", username);
+ }
+
+ return result;
+}
+
+/* Valid_User return codes -
+ *
+ * 0 - User authenticated successfully.
+ * 1 - Server error.
+ * 2 - Protocol error.
+ * 3 - Logon error; Incorrect password or username given.
+ */
--- /dev/null
+
+/*
+ * denyusers.c
+ * (C) 2000 Antonino Iannella, Stellar-X Pty Ltd
+ * Released under GPL, see COPYING-2.0 for details.
+ *
+ * These routines are to block users attempting to use the proxy which
+ * have been explicitly denied by the system administrator.
+ * Routines at the bottom also use the allowed user functions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+#define NAMELEN 50 /* Maximum username length */
+
+/* Global variables */
+
+char *DeniedUsers; /* Pointer to string of denied users */
+off_t DenyUserSize; /* Size of denied user file */
+struct stat FileBuf; /* Stat data buffer */
+time_t LastModTime; /* Last denied user file modification time */
+
+char Denyuserpath[MAXPATHLEN]; /* MAXPATHLEN defined in param.h */
+
+/* Function declarations */
+
+int Read_denyusers();
+int Check_ifuserdenied(char *);
+int Check_user(char *);
+void Checktimer();
+void Check_forchange();
+void Check_fordenychange();
+extern void Check_forallowchange(); /* For allowed users */
+extern int Check_ifuserallowed(char *);
+
+/*
+ * Reads Denyuserpath for all users to be excluded.
+ * Returns 0 if the user list was successfully loaded,
+ * and 1 in case of error.
+ * Logs any messages to the syslog daemon.
+ */
+
+int
+Read_denyusers()
+{
+ FILE *DFile; /* Denied user file pointer */
+ off_t DPos = 0; /* File counter */
+ char DChar; /* Character buffer */
+
+ /* Stat the file. If it does not exist, save the size as zero.
+ * Clear the denied user string. Return. */
+ if (stat(Denyuserpath, &FileBuf) == -1) {
+ if (errno == ENOENT) {
+ LastModTime = (time_t) 0;
+ DenyUserSize = 0;
+ free(DeniedUsers);
+ DeniedUsers = malloc(sizeof(char));
+ DeniedUsers[0] = '\0';
+ return 0;
+ } else {
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return 1;
+ }
+ }
+ /* If it exists, save the modification time and size */
+ LastModTime = FileBuf.st_mtime;
+ DenyUserSize = FileBuf.st_size;
+
+ /* Handle the special case of a zero length file */
+ if (DenyUserSize == 0) {
+ free(DeniedUsers);
+ DeniedUsers = malloc(sizeof(char));
+ DeniedUsers[0] = '\0';
+ return 0;
+ }
+ /* Free and allocate space for a string to store the denied usernames */
+ free(DeniedUsers);
+
+ if ((DeniedUsers = malloc(sizeof(char) * (DenyUserSize + 3))) == NULL) {
+ syslog(LOG_USER | LOG_ERR, "Read_denyusers: malloc(DeniedUsers) failed.");
+ return 1;
+ }
+ /* Open the denied user file. Report any errors. */
+
+ if ((DFile = fopen(Denyuserpath, "r")) == NULL) {
+ syslog(LOG_USER | LOG_ERR, "Read_denyusers: Failed to open denied user file.");
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return 1;
+ }
+ /* Read user names into the DeniedUsers string.
+ * Make sure each string is delimited by a space. */
+
+ DeniedUsers[DPos++] = ' ';
+
+ while (!feof(DFile)) {
+ if ((DChar = fgetc(DFile)) == EOF)
+ break;
+ else {
+ if (isspace(DChar))
+ DeniedUsers[DPos++] = ' ';
+ else
+ DeniedUsers[DPos++] = toupper(DChar);
+ }
+ }
+
+ DeniedUsers[DPos++] = ' ';
+ DeniedUsers[DPos] = '\0';
+ fclose(DFile);
+ return 0;
+}
+
+/*
+ * Check to see if the username provided by Squid appears in the denied
+ * user list. Returns 0 if the user was not found, and 1 if they were.
+ */
+
+int
+Check_ifuserdenied(char *ConnectingUser)
+{
+ static char CUBuf[NAMELEN + 1];
+ static int x;
+ static char DenyMsg[256];
+
+ /* If user string is empty, deny */
+ if (ConnectingUser[0] == '\0')
+ return 1;
+
+ /* If denied user list is empty, allow */
+ if (DenyUserSize == 0)
+ return 0;
+
+ /* Check if username string is found in the denied user list.
+ * If so, deny. If not, allow. Reconstruct the username
+ * to have whitespace, to avoid finding wrong string subsets. */
+
+ sscanf(ConnectingUser, " %s ", CUBuf);
+ sprintf(CUBuf, " %s ", CUBuf);
+
+ for (x = 0; x <= strlen(CUBuf); x++)
+ CUBuf[x] = toupper(CUBuf[x]);
+
+ if (strstr(DeniedUsers, CUBuf) == NULL)
+ return 0;
+ else {
+ sprintf(DenyMsg, "Denied access to user '%s'.", CUBuf);
+ syslog(LOG_USER | LOG_ERR, DenyMsg);
+ return 1;
+ }
+}
+
+/*
+ * Checks if there has been a change in the denied user file.
+ * If the modification time has changed, then reload the denied user list.
+ * This function is called by the SIGHUP signal handler.
+ */
+
+void
+Check_fordenychange()
+{
+ struct stat ChkBuf; /* Stat data buffer */
+
+ /* Stat the denied user file. If it cannot be accessed, return. */
+
+ if (stat(Denyuserpath, &ChkBuf) == -1) {
+ if (errno == ENOENT) {
+ LastModTime = (time_t) 0;
+ DenyUserSize = 0;
+ free(DeniedUsers);
+ DeniedUsers = malloc(sizeof(char));
+ DeniedUsers[0] = '\0';
+ return;
+ } else { /* Report error when accessing file */
+ syslog(LOG_USER | LOG_ERR, strerror(errno));
+ return;
+ }
+ }
+ /* If found, compare the modification time with the previously-recorded
+ * modification time.
+ * If the modification time has changed, reload the denied user list.
+ * Log a message of its actions. */
+
+ if (ChkBuf.st_mtime != LastModTime) {
+ syslog(LOG_USER | LOG_INFO, "Check_fordenychange: Reloading denied user list.");
+ Read_denyusers();
+ }
+}
+
+/*
+ * Decides if a user is denied or allowed.
+ * If they have been denied, or not allowed, return 1.
+ * Else return 0.
+ */
+
+int
+Check_user(char *ConnectingUser)
+{
+ if (Check_ifuserdenied(ConnectingUser) == 1)
+ return 1;
+
+ if (Check_ifuserallowed(ConnectingUser) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Checks the denied and allowed user files for change.
+ * This function is invoked when a SIGHUP signal is received.
+ * It is also run after every 60 seconds, at the next request.
+ */
+
+void
+Check_forchange()
+{
+ Check_fordenychange();
+ Check_forallowchange();
+}
+
+/*
+ * Checks the timer. If longer than 1 minute has passed since the last
+ * time someone has accessed the proxy, then check for changes in the
+ * denied user file. If longer than one minute hasn't passed, return.
+ */
+
+void
+Checktimer()
+{
+ static time_t Lasttime; /* The last time the timer was checked */
+ static time_t Currenttime; /* The current time */
+
+ Currenttime = time(NULL);
+
+ /* If timeout has expired, check the denied user file, else return */
+ if (difftime(Currenttime, Lasttime) < 60)
+ return;
+ else {
+ Check_forchange();
+ Lasttime = Currenttime;
+ }
+}
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * a implementation of MD4 designed for use in the SMB authentication protocol
+ * Copyright (C) Andrew Tridgell 1997
+ *
+ * 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.
+ */
+
+
+/* NOTE: This code makes no attempt to be fast!
+ *
+ * It assumes that a int is at least 32 bits long
+ */
+
+typedef unsigned int uint32;
+
+static uint32 A, B, C, D;
+
+static uint32
+F(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X & Y) | ((~X) & Z);
+}
+
+static uint32
+G(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X & Y) | (X & Z) | (Y & Z);
+}
+
+static uint32
+H(uint32 X, uint32 Y, uint32 Z)
+{
+ return X ^ Y ^ Z;
+}
+
+static uint32
+lshift(uint32 x, int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void
+mdfour64(uint32 * M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+
+ for (j = 0; j < 16; j++)
+ X[j] = M[j];
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ ROUND1(A, B, C, D, 0, 3);
+ ROUND1(D, A, B, C, 1, 7);
+ ROUND1(C, D, A, B, 2, 11);
+ ROUND1(B, C, D, A, 3, 19);
+ ROUND1(A, B, C, D, 4, 3);
+ ROUND1(D, A, B, C, 5, 7);
+ ROUND1(C, D, A, B, 6, 11);
+ ROUND1(B, C, D, A, 7, 19);
+ ROUND1(A, B, C, D, 8, 3);
+ ROUND1(D, A, B, C, 9, 7);
+ ROUND1(C, D, A, B, 10, 11);
+ ROUND1(B, C, D, A, 11, 19);
+ ROUND1(A, B, C, D, 12, 3);
+ ROUND1(D, A, B, C, 13, 7);
+ ROUND1(C, D, A, B, 14, 11);
+ ROUND1(B, C, D, A, 15, 19);
+
+ ROUND2(A, B, C, D, 0, 3);
+ ROUND2(D, A, B, C, 4, 5);
+ ROUND2(C, D, A, B, 8, 9);
+ ROUND2(B, C, D, A, 12, 13);
+ ROUND2(A, B, C, D, 1, 3);
+ ROUND2(D, A, B, C, 5, 5);
+ ROUND2(C, D, A, B, 9, 9);
+ ROUND2(B, C, D, A, 13, 13);
+ ROUND2(A, B, C, D, 2, 3);
+ ROUND2(D, A, B, C, 6, 5);
+ ROUND2(C, D, A, B, 10, 9);
+ ROUND2(B, C, D, A, 14, 13);
+ ROUND2(A, B, C, D, 3, 3);
+ ROUND2(D, A, B, C, 7, 5);
+ ROUND2(C, D, A, B, 11, 9);
+ ROUND2(B, C, D, A, 15, 13);
+
+ ROUND3(A, B, C, D, 0, 3);
+ ROUND3(D, A, B, C, 8, 9);
+ ROUND3(C, D, A, B, 4, 11);
+ ROUND3(B, C, D, A, 12, 15);
+ ROUND3(A, B, C, D, 2, 3);
+ ROUND3(D, A, B, C, 10, 9);
+ ROUND3(C, D, A, B, 6, 11);
+ ROUND3(B, C, D, A, 14, 15);
+ ROUND3(A, B, C, D, 1, 3);
+ ROUND3(D, A, B, C, 9, 9);
+ ROUND3(C, D, A, B, 5, 11);
+ ROUND3(B, C, D, A, 13, 15);
+ ROUND3(A, B, C, D, 3, 3);
+ ROUND3(D, A, B, C, 11, 9);
+ ROUND3(C, D, A, B, 7, 11);
+ ROUND3(B, C, D, A, 15, 15);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+
+ A &= 0xFFFFFFFF;
+ B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF;
+ D &= 0xFFFFFFFF;
+
+ for (j = 0; j < 16; j++)
+ X[j] = 0;
+}
+
+static void
+copy64(uint32 * M, unsigned char *in)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) |
+ (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0);
+}
+
+static void
+copy4(unsigned char *out, uint32 x)
+{
+ out[0] = x & 0xFF;
+ out[1] = (x >> 8) & 0xFF;
+ out[2] = (x >> 16) & 0xFF;
+ out[3] = (x >> 24) & 0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void
+mdfour(unsigned char *out, unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b = n * 8;
+ int i;
+
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+
+ while (n > 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ }
+
+ for (i = 0; i < 128; i++)
+ buf[i] = 0;
+ memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf + 56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf + 120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf + 64);
+ mdfour64(M);
+ }
+
+ for (i = 0; i < 128; i++)
+ buf[i] = 0;
+ copy64(M, buf);
+
+ copy4(out, A);
+ copy4(out + 4, B);
+ copy4(out + 8, C);
+ copy4(out + 12, D);
+
+ A = B = C = D = 0;
+}
--- /dev/null
+Begin3
+Title: msntauth
+Version: 2.0
+Entered-date: 10OCT00
+Description: Squid web proxy NT domain authentication module
+Keywords: Squid WWW proxy SMB NT domain authentication module source
+Author: antonino.iannella@usa.net (Antonino Iannella)
+Maintained-by: antonino.iannella@usa.net (Antonino Iannella)
+Primary-site: sunsite.unc.edu /pub/Linux/system/network/misc
+ msntauth-v2.0.tgz
+Original-site: http://stellarx.tripod.com
+Copying-policy: GPL
+End
--- /dev/null
+
+/*
+ * MSNT - Microsoft Windows NT domain squid authenticator module
+ * Version 1.2 by Stellar-X Pty Ltd, Antonino Iannella
+ * Fri Sep 22 00:56:05 CST 2000
+ *
+ * Modified to act as a Squid authenticator module.
+ * Removed all Pike stuff.
+ * Returns OK for a successful authentication, or ERR upon error.
+ *
+ * Uses code from -
+ * Andrew Tridgell 1997
+ * Richard Sharpe 1996
+ * Bill Welliver 1999
+ * Duane Wessels 2000
+ *
+ * Released under GNU Public License
+ *
+ * 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 <stdio.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/time.h>
+
+extern int OpenConfigFile();
+extern int QueryServers(char *, char *);
+extern void Checktimer();
+extern void Check_forchange();
+extern int Read_denyusers(void);
+extern int Read_allowusers(void);
+extern int Check_user(char *);
+
+/* Main program for simple authentication.
+ * Reads the denied user file. Sets alarm timer.
+ * Scans and checks for Squid input, and attempts to validate the user.
+ */
+
+int
+main()
+{
+ char username[256];
+ char password[256];
+ char wstr[256];
+
+ /* Read configuration file. Abort wildly if error. */
+ if (OpenConfigFile() == 1)
+ return 1;
+
+ /* Read denied and allowed user files.
+ * If they fails, there is a serious problem.
+ * Check syslog messages. Deny all users while in this state.
+ * The msntauth process should then be killed. */
+
+ if ((Read_denyusers() == 1) || (Read_allowusers() == 1)) {
+ while (1) {
+ fgets(wstr, 255, stdin);
+ puts("ERR");
+ fflush(stdout);
+ }
+ }
+ /* Make Check_forchange() the handle for HUP signals.
+ * Don't use alarms any more. I don't think it was very
+ * portable between systems. */
+ signal(SIGHUP, Check_forchange);
+
+ while (1) {
+ /* Read whole line from standard input. Terminate on break. */
+ if (fgets(wstr, 255, stdin) == NULL)
+ break;
+
+ /* Clear any current settings */
+ username[0] = '\0';
+ password[0] = '\0';
+ sscanf(wstr, "%s %s", username, password); /* Extract parameters */
+
+ /* Check for invalid or blank entries */
+ if ((username[0] == '\0') || (password[0] == '\0')) {
+ puts("ERR");
+ fflush(stdout);
+ continue;
+ }
+ Checktimer(); /* Check if the user lists have changed */
+
+ /* Check if user is explicitly denied or allowed.
+ * If user passes both checks, they can be authenticated. */
+
+ if (Check_user(username) == 1)
+ puts("ERR");
+ else {
+ if (QueryServers(username, password) == 0)
+ puts("OK");
+ else
+ puts("ERR");
+ }
+
+ fflush(stdout);
+ }
+
+ return 0;
+}
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Common Structures etc Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 _RFCNB_COMMON_H_
+#define _RFCNB_COMMON_H_
+
+/* A data structure we need */
+
+typedef struct RFCNB_Pkt {
+
+ char *data; /* The data in this portion */
+ int len;
+ struct RFCNB_Pkt *next;
+
+} RFCNB_Pkt;
+
+
+#endif /* _RFCNB_COMMON_H_ */
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Error Response Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 _RFCNB_ERROR_H_
+#define _RFCNB_ERROR_H_
+
+/* Error responses */
+
+#define RFCNBE_Bad -1 /* Bad response */
+#define RFCNBE_OK 0
+
+/* these should follow the spec ... is there one ? */
+
+#define RFCNBE_NoSpace 1 /* Could not allocate space for a struct */
+#define RFCNBE_BadName 2 /* Could not translate a name */
+#define RFCNBE_BadRead 3 /* Read sys call failed */
+#define RFCNBE_BadWrite 4 /* Write Sys call failed */
+#define RFCNBE_ProtErr 5 /* Protocol Error */
+#define RFCNBE_ConGone 6 /* Connection dropped */
+#define RFCNBE_BadHandle 7 /* Handle passed was bad */
+#define RFCNBE_BadSocket 8 /* Problems creating socket */
+#define RFCNBE_ConnectFailed 9 /* Connect failed */
+#define RFCNBE_CallRejNLOCN 10 /* Call rejected, not listening on CN */
+#define RFCNBE_CallRejNLFCN 11 /* Call rejected, not listening for CN */
+#define RFCNBE_CallRejCNNP 12 /* Call rejected, called name not present */
+#define RFCNBE_CallRejInfRes 13 /* Call rejetced, name ok, no resources */
+#define RFCNBE_CallRejUnSpec 14 /* Call rejected, unspecified error */
+#define RFCNBE_BadParam 15 /* Bad parameters passed ... */
+#define RFCNBE_Timeout 16 /* IO Timed out */
+
+/* Text strings for the error responses */
+
+extern char *RFCNB_Error_Strings[];
+
+#endif /* _RFCNB_ERROR_H_ */
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NEtBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB IO Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 <features.h> */
+#include "std-includes.h"
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+#include <sys/uio.h>
+#include <sys/signal.h>
+
+int RFCNB_Timeout = 0; /* Timeout in seconds ... */
+
+void
+rfcnb_alarm(int sig)
+{
+
+ fprintf(stderr, "IO Timed out ...\n");
+
+}
+
+/* Set timeout value and setup signal handling */
+
+int
+RFCNB_Set_Timeout(int seconds)
+{
+#ifdef __GLIBC__
+ int temp;
+#endif
+ /* If we are on a Bezerkeley system, use sigvec, else sigaction */
+#ifndef SA_RESTART
+ struct sigvec invec, outvec;
+#else
+ struct sigaction inact, outact;
+#endif
+
+ RFCNB_Timeout = seconds;
+
+ if (RFCNB_Timeout > 0) { /* Set up handler to ignore but not restart */
+
+#ifndef SA_RESTART
+ invec.sv_handler = (void (*)()) rfcnb_alarm;
+ invec.sv_mask = 0;
+ invec.sv_flags = SV_INTERRUPT;
+
+ if (sigvec(SIGALRM, &invec, &outvec) < 0)
+ return (-1);
+#else
+ inact.sa_handler = (void (*)()) rfcnb_alarm;
+#ifdef SOLARIS
+ /* Solaris seems to have an array of vectors ... */
+ inact.sa_mask.__sigbits[0] = 0;
+ inact.sa_mask.__sigbits[1] = 0;
+ inact.sa_mask.__sigbits[2] = 0;
+ inact.sa_mask.__sigbits[3] = 0;
+#else
+#ifdef __GLIBC__
+ for (temp = 0; temp < 32; temp++)
+ inact.sa_mask.__val[temp] = 0;
+#else
+ inact.sa_mask = 0;
+#endif
+#endif
+ inact.sa_flags = 0; /* Don't restart */
+
+ if (sigaction(SIGALRM, &inact, &outact) < 0)
+ return (-1);
+
+#endif
+
+ }
+ return (0);
+
+}
+
+/* Discard the rest of an incoming packet as we do not have space for it
+ * in the buffer we allocated or were passed ... */
+
+int
+RFCNB_Discard_Rest(struct RFCNB_Con *con, int len)
+{
+ char temp[100]; /* Read into here */
+ int rest, this_read, bytes_read;
+
+ /* len is the amount we should read */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Discard_Rest called to discard: %i\n", len);
+#endif
+
+ rest = len;
+
+ while (rest > 0) {
+
+ this_read = (rest > sizeof(temp) ? sizeof(temp) : rest);
+
+ bytes_read = read(con->fd, temp, this_read);
+
+ if (bytes_read <= 0) { /* Error so return */
+
+ if (bytes_read < 0)
+ RFCNB_errno = RFCNBE_BadRead;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ rest = rest - bytes_read;
+
+ }
+
+ return (0);
+
+}
+
+
+/* Send an RFCNB packet to the connection.
+ *
+ * We just send each of the blocks linked together ...
+ *
+ * If we can, try to send it as one iovec ...
+ *
+ */
+
+int
+RFCNB_Put_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len)
+{
+ int len_sent, tot_sent, this_len;
+ struct RFCNB_Pkt *pkt_ptr;
+ char *this_data;
+ int i;
+ struct iovec io_list[10]; /* We should never have more */
+ /* If we do, this will blow up ... */
+
+ /* Try to send the data ... We only send as many bytes as len claims */
+ /* We should try to stuff it into an IOVEC and send as one write */
+
+
+ pkt_ptr = pkt;
+ len_sent = tot_sent = 0; /* Nothing sent so far */
+ i = 0;
+
+ while ((pkt_ptr != NULL) & (i < 10)) { /* Watch that magic number! */
+
+ this_len = pkt_ptr->len;
+ this_data = pkt_ptr->data;
+ if ((tot_sent + this_len) > len)
+ this_len = len - tot_sent; /* Adjust so we don't send too much */
+
+ /* Now plug into the iovec ... */
+
+ io_list[i].iov_len = this_len;
+ io_list[i].iov_base = this_data;
+ i++;
+
+ tot_sent += this_len;
+
+ if (tot_sent == len)
+ break; /* Let's not send too much */
+
+ pkt_ptr = pkt_ptr->next;
+
+ }
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Frags = %i, tot_sent = %i\n", i, tot_sent);
+#endif
+
+ /* Set up an alarm if timeouts are set ... */
+
+ if (RFCNB_Timeout > 0)
+ alarm(RFCNB_Timeout);
+
+ if ((len_sent = writev(con->fd, io_list, i)) < 0) { /* An error */
+
+ con->rfc_errno = errno;
+ if (errno == EINTR) /* We were interrupted ... */
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadWrite;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ if (len_sent < tot_sent) { /* Less than we wanted */
+ if (errno == EINTR) /* We were interrupted */
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadWrite;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ if (RFCNB_Timeout > 0)
+ alarm(0); /* Reset that sucker */
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Len sent = %i ...\n", len_sent);
+ RFCNB_Print_Pkt(stderr, "sent", pkt, len_sent); /* Print what send ... */
+
+#endif
+
+ return (len_sent);
+
+}
+
+/* Read an RFCNB packet off the connection.
+ *
+ * We read the first 4 bytes, that tells us the length, then read the
+ * rest. We should implement a timeout, but we don't just yet
+ *
+ */
+
+
+int
+RFCNB_Get_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len)
+{
+ int read_len, pkt_len;
+ char hdr[RFCNB_Pkt_Hdr_Len]; /* Local space for the header */
+ struct RFCNB_Pkt *pkt_frag;
+ int more, this_time, offset, frag_len, this_len;
+ BOOL seen_keep_alive = TRUE;
+
+ /* Read that header straight into the buffer */
+
+ if (len < RFCNB_Pkt_Hdr_Len) { /* What a bozo */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Trying to read less than a packet:");
+ perror("");
+#endif
+ RFCNB_errno = RFCNBE_BadParam;
+ return (RFCNBE_Bad);
+
+ }
+ /* We discard keep alives here ... */
+
+ if (RFCNB_Timeout > 0)
+ alarm(RFCNB_Timeout);
+
+ while (seen_keep_alive) {
+
+ if ((read_len = read(con->fd, hdr, sizeof(hdr))) < 0) { /* Problems */
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Reading the packet, we got:");
+ perror("");
+#endif
+ if (errno == EINTR)
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadRead;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ /* Now we check out what we got */
+
+ if (read_len == 0) { /* Connection closed, send back eof? */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Connection closed reading\n");
+#endif
+
+ if (errno == EINTR)
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ if (RFCNB_Pkt_Type(hdr) == RFCNB_SESSION_KEEP_ALIVE) {
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "RFCNB KEEP ALIVE received\n");
+#endif
+
+ } else {
+ seen_keep_alive = FALSE;
+ }
+
+ }
+
+ /* What if we got less than or equal to a hdr size in bytes? */
+
+ if (read_len < sizeof(hdr)) { /* We got a small packet */
+
+ /* Now we need to copy the hdr portion we got into the supplied packet */
+
+ memcpy(pkt->data, hdr, read_len); /*Copy data */
+
+#ifdef RFCNB_DEBUG
+ RFCNB_Print_Pkt(stderr, "rcvd", pkt, read_len);
+#endif
+
+ return (read_len);
+
+ }
+ /* Now, if we got at least a hdr size, alloc space for rest, if we need it */
+
+ pkt_len = RFCNB_Pkt_Len(hdr);
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Reading Pkt: Length = %i\n", pkt_len);
+#endif
+
+ /* Now copy in the hdr */
+
+ memcpy(pkt->data, hdr, sizeof(hdr));
+
+ /* Get the rest of the packet ... first figure out how big our buf is? */
+ /* And make sure that we handle the fragments properly ... Sure should */
+ /* use an iovec ... */
+
+ if (len < pkt_len) /* Only get as much as we have space for */
+ more = len - RFCNB_Pkt_Hdr_Len;
+ else
+ more = pkt_len;
+
+ this_time = 0;
+
+ /* We read for each fragment ... */
+
+ if (pkt->len == read_len) { /* If this frag was exact size */
+ pkt_frag = pkt->next; /* Stick next lot in next frag */
+ offset = 0; /* then we start at 0 in next */
+ } else {
+ pkt_frag = pkt; /* Otherwise use rest of this frag */
+ offset = RFCNB_Pkt_Hdr_Len; /* Otherwise skip the header */
+ }
+
+ frag_len = pkt_frag->len;
+
+ if (more <= frag_len) /* If len left to get less than frag space */
+ this_len = more; /* Get the rest ... */
+ else
+ this_len = frag_len - offset;
+
+ while (more > 0) {
+
+ if ((this_time = read(con->fd, (pkt_frag->data) + offset, this_len)) <= 0) { /* Problems */
+
+ if (errno == EINTR) {
+
+ RFCNB_errno = RFCNB_Timeout;
+
+ } else {
+ if (this_time < 0)
+ RFCNB_errno = RFCNBE_BadRead;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+ }
+
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Frag_Len = %i, this_time = %i, this_len = %i, more = %i\n", frag_len,
+ this_time, this_len, more);
+#endif
+
+ read_len = read_len + this_time; /* How much have we read ... */
+
+ /* Now set up the next part */
+
+ if (pkt_frag->next == NULL)
+ break; /* That's it here */
+
+ pkt_frag = pkt_frag->next;
+ this_len = pkt_frag->len;
+ offset = 0;
+
+ more = more - this_time;
+
+ }
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Pkt Len = %i, read_len = %i\n", pkt_len, read_len);
+ RFCNB_Print_Pkt(stderr, "rcvd", pkt, read_len + sizeof(hdr));
+#endif
+
+ if (read_len < (pkt_len + sizeof(hdr))) { /* Discard the rest */
+
+ return (RFCNB_Discard_Rest(con, (pkt_len + sizeof(hdr)) - read_len));
+
+ }
+ if (RFCNB_Timeout > 0)
+ alarm(0); /* Reset that sucker */
+
+ return (read_len + sizeof(RFCNB_Hdr));
+}
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB IO Routines Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+int RFCNB_Put_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len);
+
+int RFCNB_Get_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* Defines we need */
+
+
+#define GLOBAL extern
+
+#include "rfcnb-error.h"
+#include "rfcnb-common.h"
+#include "byteorder.h"
+
+#ifdef RFCNB_PORT
+#define RFCNB_Default_Port RFCNB_PORT
+#else
+#define RFCNB_Default_Port 139
+#endif
+
+#define RFCNB_MAX_STATS 1
+
+/* Protocol defines we need */
+
+#define RFCNB_SESSION_MESSAGE 0
+#define RFCNB_SESSION_REQUEST 0x81
+#define RFCNB_SESSION_ACK 0x82
+#define RFCNB_SESSION_REJ 0x83
+#define RFCNB_SESSION_RETARGET 0x84
+#define RFCNB_SESSION_KEEP_ALIVE 0x85
+
+/* Structures */
+
+typedef struct redirect_addr *redirect_ptr;
+
+struct redirect_addr {
+
+ struct in_addr ip_addr;
+ int port;
+ redirect_ptr next;
+
+};
+
+typedef struct RFCNB_Con {
+
+ int fd; /* File descripter for TCP/IP connection */
+ int rfc_errno; /* last error */
+ int timeout; /* How many milli-secs before IO times out */
+ int redirects; /* How many times we were redirected */
+ struct redirect_addr *redirect_list; /* First is first address */
+ struct redirect_addr *last_addr;
+
+} RFCNB_Con;
+
+typedef char RFCNB_Hdr[4]; /* The header is 4 bytes long with */
+ /* char[0] as the type, char[1] the */
+ /* flags, and char[2..3] the length */
+
+/* Macros to extract things from the header. These are for portability
+ * between architecture types where we are worried about byte order */
+
+#define RFCNB_Pkt_Hdr_Len 4
+#define RFCNB_Pkt_Sess_Len 72
+#define RFCNB_Pkt_Retarg_Len 10
+#define RFCNB_Pkt_Nack_Len 5
+#define RFCNB_Pkt_Type_Offset 0
+#define RFCNB_Pkt_Flags_Offset 1
+#define RFCNB_Pkt_Len_Offset 2 /* Length is 2 bytes plus a flag bit */
+#define RFCNB_Pkt_N1Len_Offset 4
+#define RFCNB_Pkt_Called_Offset 5
+#define RFCNB_Pkt_N2Len_Offset 38
+#define RFCNB_Pkt_Calling_Offset 39
+#define RFCNB_Pkt_Error_Offset 4
+#define RFCNB_Pkt_IP_Offset 4
+#define RFCNB_Pkt_Port_Offset 8
+
+/* The next macro isolates the length of a packet, including the bit in the
+ * flags */
+
+#define RFCNB_Pkt_Len(p) (PVAL(p, 3) | (PVAL(p, 2) << 8) | \
+ ((PVAL(p, RFCNB_Pkt_Flags_Offset) & 0x01) << 16))
+
+#define RFCNB_Put_Pkt_Len(p, v) (p[1] = ((v >> 16) & 1)); \
+ (p[2] = ((v >> 8) & 0xFF)); \
+ (p[3] = (v & 0xFF));
+
+#define RFCNB_Pkt_Type(p) (CVAL(p, RFCNB_Pkt_Type_Offset))
+
+/*typedef struct RFCNB_Hdr {
+ *
+ * unsigned char type;
+ * unsigned char flags;
+ * int16 len;
+ *
+ * } RFCNB_Hdr;
+ *
+ * typedef struct RFCNB_Sess_Pkt {
+ * unsigned char type;
+ * unsigned char flags;
+ * int16 length;
+ * unsigned char n1_len;
+ * char called_name[33];
+ * unsigned char n2_len;
+ * char calling_name[33];
+ * } RFCNB_Sess_Pkt;
+ *
+ *
+ * typedef struct RFCNB_Nack_Pkt {
+ *
+ * struct RFCNB_Hdr hdr;
+ * unsigned char error;
+ *
+ * } RFCNB_Nack_Pkt;
+ *
+ * typedef struct RFCNB_Retarget_Pkt {
+ *
+ * struct RFCNB_Hdr hdr;
+ * int dest_ip;
+ * unsigned char port;
+ *
+ * } RFCNB_Redir_Pkt; */
+
+/* Static variables */
+
+/* Only declare this if not defined */
+
+#ifndef RFCNB_ERRNO
+extern int RFCNB_errno;
+extern int RFCNB_saved_errno; /* Save this from point of error */
+#endif
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Utility Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "std-includes.h"
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+char *RFCNB_Error_Strings[] =
+{
+
+ "RFCNBE_OK: Routine completed successfully.",
+ "RFCNBE_NoSpace: No space available for a malloc call.",
+ "RFCNBE_BadName: NetBIOS name could not be translated to IP address.",
+ "RFCNBE_BadRead: Read system call returned an error. Check errno.",
+ "RFCNBE_BadWrite: Write system call returned an error. Check errno.",
+ "RFCNBE_ProtErr: A protocol error has occurred.",
+ "RFCNBE_ConGone: Connection dropped during a read or write system call.",
+ "RFCNBE_BadHandle: Bad connection handle passed.",
+ "RFCNBE_BadSocket: Problems creating socket.",
+ "RFCNBE_ConnectFailed: Connection failed. See errno.",
+ "RFCNBE_CallRejNLOCN: Call rejected. Not listening on called name.",
+ "RFCNBE_CallRejNLFCN: Call rejected. Not listening for called name.",
+ "RFCNBE_CallRejCNNP: Call rejected. Called name not present.",
+ "RFCNBE_CallRejInfRes: Call rejected. Name present, but insufficient resources.",
+ "RFCNBE_CallRejUnSpec: Call rejected. Unspecified error.",
+ "RFCNBE_BadParam: Bad parameters passed to a routine.",
+ "RFCNBE_Timeout: IO Operation timed out ..."
+
+};
+
+extern void (*Prot_Print_Routine) (); /* Pointer to protocol print routine */
+
+/* Convert name and pad to 16 chars as needed */
+/* Name 1 is a C string with null termination, name 2 may not be */
+/* If SysName is true, then put a <00> on end, else space> */
+
+void
+RFCNB_CvtPad_Name(char *name1, char *name2)
+{
+ char c, c1, c2;
+ int i, len;
+
+ len = strlen(name1);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= len) {
+
+ c1 = 'C';
+ c2 = 'A'; /* CA is a space */
+
+ } else {
+
+ c = name1[i];
+ c1 = (char) ((int) c / 16 + (int) 'A');
+ c2 = (char) ((int) c % 16 + (int) 'A');
+ }
+
+ name2[i * 2] = c1;
+ name2[i * 2 + 1] = c2;
+
+ }
+
+ name2[32] = 0; /* Put in the nll ... */
+
+}
+
+/* Converts an Ascii NB Name (16 chars) to an RFCNB Name (32 chars)
+ * Uses the encoding in RFC1001. Each nibble of byte is added to 'A'
+ * to produce the next byte in the name.
+ *
+ * This routine assumes that AName is 16 bytes long and that NBName has
+ * space for 32 chars, so be careful ...
+ *
+ */
+
+void
+RFCNB_AName_To_NBName(char *AName, char *NBName)
+{
+ char c, c1, c2;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+
+ c = AName[i];
+
+ c1 = (char) ((c >> 4) + 'A');
+ c2 = (char) ((c & 0xF) + 'A');
+
+ NBName[i * 2] = c1;
+ NBName[i * 2 + 1] = c2;
+ }
+
+ NBName[32] = 0; /* Put in a null */
+
+}
+
+/* Do the reverse of the above ... */
+
+void
+RFCNB_NBName_To_AName(char *NBName, char *AName)
+{
+ char c, c1, c2;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+
+ c1 = NBName[i * 2];
+ c2 = NBName[i * 2 + 1];
+
+ c = (char) (((int) c1 - (int) 'A') * 16 + ((int) c2 - (int) 'A'));
+
+ AName[i] = c;
+
+ }
+
+ AName[i] = 0; /* Put a null on the end ... */
+
+}
+
+/* Print a string of bytes in HEX etc */
+
+void
+RFCNB_Print_Hex(FILE * fd, struct RFCNB_Pkt *pkt, int Offset, int Len)
+{
+ char c1, c2, outbuf1[33];
+ unsigned char c;
+ int i, j;
+ struct RFCNB_Pkt *pkt_ptr = pkt;
+ static char Hex_List[17] = "0123456789ABCDEF";
+
+ j = 0;
+
+ /* We only want to print as much as sepcified in Len */
+
+ while (pkt_ptr != NULL) {
+
+ for (i = 0;
+ i < ((Len > (pkt_ptr->len) ? pkt_ptr->len : Len) - Offset);
+ i++) {
+
+ c = pkt_ptr->data[i + Offset];
+ c1 = Hex_List[c >> 4];
+ c2 = Hex_List[c & 0xF];
+
+ outbuf1[j++] = c1;
+ outbuf1[j++] = c2;
+
+ if (j == 32) { /* Print and reset */
+ outbuf1[j] = 0;
+ fprintf(fd, " %s\n", outbuf1);
+ j = 0;
+ }
+ }
+
+ Offset = 0;
+ Len = Len - pkt_ptr->len; /* Reduce amount by this much */
+ pkt_ptr = pkt_ptr->next;
+
+ }
+
+ /* Print last lot in the buffer ... */
+
+ if (j > 0) {
+
+ outbuf1[j] = 0;
+ fprintf(fd, " %s\n", outbuf1);
+
+ }
+ fprintf(fd, "\n");
+
+}
+
+/* Get a packet of size n */
+
+struct RFCNB_Pkt *
+RFCNB_Alloc_Pkt(int n)
+{
+ RFCNB_Pkt *pkt;
+
+ if ((pkt = (struct RFCNB_Pkt *) malloc(sizeof(struct RFCNB_Pkt))) == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ pkt->next = NULL;
+ pkt->len = n;
+
+ if (n == 0)
+ return (pkt);
+
+ if ((pkt->data = (char *) malloc(n)) == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ free(pkt);
+ return (NULL);
+
+ }
+ return (pkt);
+
+}
+
+/* Free up a packet */
+
+void
+RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt)
+{
+ struct RFCNB_Pkt *pkt_next;
+ char *data_ptr;
+
+ while (pkt != NULL) {
+
+ pkt_next = pkt->next;
+
+ data_ptr = pkt->data;
+
+ if (data_ptr != NULL)
+ free(data_ptr);
+
+ free(pkt);
+
+ pkt = pkt_next;
+
+ }
+
+}
+
+/* Print an RFCNB packet */
+
+void
+RFCNB_Print_Pkt(FILE * fd, char *dirn, struct RFCNB_Pkt *pkt, int len)
+{
+ char lname[17];
+
+ /* We assume that the first fragment is the RFCNB Header */
+ /* We should loop through the fragments printing them out */
+
+ fprintf(fd, "RFCNB Pkt %s:", dirn);
+
+ switch (RFCNB_Pkt_Type(pkt->data)) {
+
+ case RFCNB_SESSION_MESSAGE:
+
+ fprintf(fd, "SESSION MESSAGE: Length = %i\n", RFCNB_Pkt_Len(pkt->data));
+ RFCNB_Print_Hex(fd, pkt, RFCNB_Pkt_Hdr_Len,
+#ifdef RFCNB_PRINT_DATA
+ RFCNB_Pkt_Len(pkt->data) - RFCNB_Pkt_Hdr_Len);
+#else
+ 40);
+#endif
+
+ if (Prot_Print_Routine != 0) { /* Print the rest of the packet */
+
+ Prot_Print_Routine(fd, strcmp(dirn, "sent"), pkt, RFCNB_Pkt_Hdr_Len,
+ RFCNB_Pkt_Len(pkt->data) - RFCNB_Pkt_Hdr_Len);
+
+ }
+ break;
+
+ case RFCNB_SESSION_REQUEST:
+
+ fprintf(fd, "SESSION REQUEST: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+ RFCNB_NBName_To_AName((char *) (pkt->data + RFCNB_Pkt_Called_Offset), lname);
+ fprintf(fd, " Called Name: %s\n", lname);
+ RFCNB_NBName_To_AName((char *) (pkt->data + RFCNB_Pkt_Calling_Offset), lname);
+ fprintf(fd, " Calling Name: %s\n", lname);
+
+ break;
+
+ case RFCNB_SESSION_ACK:
+
+ fprintf(fd, "RFCNB SESSION ACK: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ break;
+
+ case RFCNB_SESSION_REJ:
+ fprintf(fd, "RFCNB SESSION REJECT: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ if (RFCNB_Pkt_Len(pkt->data) < 1) {
+ fprintf(fd, " Protocol Error, short Reject packet!\n");
+ } else {
+ fprintf(fd, " Error = %x\n", CVAL(pkt->data, RFCNB_Pkt_Error_Offset));
+ }
+
+ break;
+
+ case RFCNB_SESSION_RETARGET:
+
+ fprintf(fd, "RFCNB SESSION RETARGET: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ /* Print out the IP address etc and the port? */
+
+ break;
+
+ case RFCNB_SESSION_KEEP_ALIVE:
+
+ fprintf(fd, "RFCNB SESSION KEEP ALIVE: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+ break;
+
+ default:
+
+ break;
+ }
+
+}
+
+/* Resolve a name into an address */
+
+int
+RFCNB_Name_To_IP(char *host, struct in_addr *Dest_IP)
+{
+ int addr; /* Assumes IP4, 32 bit network addresses */
+ struct hostent *hp;
+
+ /* Use inet_addr to try to convert the address */
+
+ if ((addr = inet_addr(host)) == INADDR_NONE) { /* Oh well, a good try :-) */
+
+ /* Now try a name look up with gethostbyname */
+
+ if ((hp = gethostbyname(host)) == NULL) { /* Not in DNS */
+
+ /* Try NetBIOS name lookup, how the hell do we do that? */
+
+ RFCNB_errno = RFCNBE_BadName; /* Is this right? */
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ } else { /* We got a name */
+
+ memcpy((void *) Dest_IP, (void *) hp->h_addr_list[0], sizeof(struct in_addr));
+
+ }
+ } else { /* It was an IP address */
+
+ memcpy((void *) Dest_IP, (void *) &addr, sizeof(struct in_addr));
+
+ }
+
+ return 0;
+
+}
+
+/* Disconnect the TCP connection to the server */
+
+int
+RFCNB_Close(int socket)
+{
+
+ close(socket);
+
+ /* If we want to do error recovery, here is where we put it */
+
+ return 0;
+
+}
+
+/* Connect to the server specified in the IP address.
+ * Not sure how to handle socket options etc. */
+
+int
+RFCNB_IP_Connect(struct in_addr Dest_IP, int port)
+{
+ struct sockaddr_in Socket;
+ int fd;
+
+ /* Create a socket */
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { /* Handle the error */
+
+ RFCNB_errno = RFCNBE_BadSocket;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ bzero((char *) &Socket, sizeof(Socket));
+ memcpy((char *) &Socket.sin_addr, (char *) &Dest_IP, sizeof(Dest_IP));
+
+ Socket.sin_port = htons(port);
+ Socket.sin_family = PF_INET;
+
+ /* Now connect to the destination */
+
+ if (connect(fd, (struct sockaddr *) &Socket, sizeof(Socket)) < 0) { /* Error */
+
+ close(fd);
+ RFCNB_errno = RFCNBE_ConnectFailed;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ return (fd);
+
+}
+
+/* handle the details of establishing the RFCNB session with remote
+ * end
+ *
+ */
+
+int
+RFCNB_Session_Req(struct RFCNB_Con *con,
+ char *Called_Name,
+ char *Calling_Name,
+ BOOL * redirect,
+ struct in_addr *Dest_IP,
+ int *port)
+{
+ char *sess_pkt;
+
+ /* Response packet should be no more than 9 bytes, make 16 jic */
+
+ char resp[16];
+ int len;
+ struct RFCNB_Pkt *pkt, res_pkt;
+
+ /* We build and send the session request, then read the response */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Sess_Len);
+
+ if (pkt == NULL) {
+
+ return (RFCNBE_Bad); /* Leave the error that RFCNB_Alloc_Pkt gives) */
+
+ }
+ sess_pkt = pkt->data; /* Get pointer to packet proper */
+
+ sess_pkt[RFCNB_Pkt_Type_Offset] = RFCNB_SESSION_REQUEST;
+ RFCNB_Put_Pkt_Len(sess_pkt, (RFCNB_Pkt_Sess_Len - RFCNB_Pkt_Hdr_Len));
+ sess_pkt[RFCNB_Pkt_N1Len_Offset] = 32;
+ sess_pkt[RFCNB_Pkt_N2Len_Offset] = 32;
+
+ RFCNB_CvtPad_Name(Called_Name, (sess_pkt + RFCNB_Pkt_Called_Offset));
+ RFCNB_CvtPad_Name(Calling_Name, (sess_pkt + RFCNB_Pkt_Calling_Offset));
+
+ /* Now send the packet */
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Sending packet: ");
+
+#endif
+
+ if ((len = RFCNB_Put_Pkt(con, pkt, RFCNB_Pkt_Sess_Len)) < 0) {
+
+ return (RFCNBE_Bad); /* Should be able to write that lot ... */
+
+ }
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Getting packet.\n");
+
+#endif
+
+ res_pkt.data = resp;
+ res_pkt.len = sizeof(resp);
+ res_pkt.next = NULL;
+
+ if ((len = RFCNB_Get_Pkt(con, &res_pkt, sizeof(resp))) < 0) {
+
+ return (RFCNBE_Bad);
+
+ }
+ /* Now analyze the packet ... */
+
+ switch (RFCNB_Pkt_Type(resp)) {
+
+ case RFCNB_SESSION_REJ: /* Didnt like us ... too bad */
+
+ /* Why did we get rejected ? */
+
+ switch (CVAL(resp, RFCNB_Pkt_Error_Offset)) {
+
+ case 0x80:
+ RFCNB_errno = RFCNBE_CallRejNLOCN;
+ break;
+ case 0x81:
+ RFCNB_errno = RFCNBE_CallRejNLFCN;
+ break;
+ case 0x82:
+ RFCNB_errno = RFCNBE_CallRejCNNP;
+ break;
+ case 0x83:
+ RFCNB_errno = RFCNBE_CallRejInfRes;
+ break;
+ case 0x8F:
+ RFCNB_errno = RFCNBE_CallRejUnSpec;
+ break;
+ default:
+ RFCNB_errno = RFCNBE_ProtErr;
+ break;
+ }
+
+ return (RFCNBE_Bad);
+ break;
+
+ case RFCNB_SESSION_ACK: /* Got what we wanted ... */
+
+ return (0);
+ break;
+
+ case RFCNB_SESSION_RETARGET: /* Go elsewhere */
+
+ *redirect = TRUE; /* Copy port and ip addr */
+
+ memcpy(Dest_IP, (resp + RFCNB_Pkt_IP_Offset), sizeof(struct in_addr));
+ *port = SVAL(resp, RFCNB_Pkt_Port_Offset);
+
+ return (0);
+ break;
+
+ default: /* A protocol error */
+
+ RFCNB_errno = RFCNBE_ProtErr;
+ return (RFCNBE_Bad);
+ break;
+ }
+}
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Utility Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+void RFCNB_CvtPad_Name(char *name1, char *name2);
+
+void RFCNB_AName_To_NBName(char *AName, char *NBName);
+
+void RFCNB_NBName_To_AName(char *NBName, char *AName);
+
+void RFCNB_Print_Hex(FILE * fd, struct RFCNB_Pkt *pkt, int Offset, int Len);
+
+struct RFCNB_Pkt *RFCNB_Alloc_Pkt(int n);
+
+void RFCNB_Print_Pkt(FILE * fd, char *dirn, struct RFCNB_Pkt *pkt, int len);
+
+int RFCNB_Name_To_IP(char *host, struct in_addr *Dest_IP);
+
+int RFCNB_Close(int socket);
+
+int RFCNB_IP_Connect(struct in_addr Dest_IP, int port);
+
+int RFCNB_Session_Req(RFCNB_Con * con,
+ char *Called_Name,
+ char *Calling_Name,
+ BOOL * redirect,
+ struct in_addr *Dest_IP,
+ int *port);
+
+void RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* Error responses */
+
+#include "rfcnb-error.h"
+#include "rfcnb-common.h"
+
+/* Defines we need */
+
+#define RFCNB_Default_Port 139
+
+/* Definition of routines we define */
+
+void *RFCNB_Call(char *Called_Name, char *Calling_Name, char *Called_Address,
+ int port);
+
+int RFCNB_Send(void *Con_Handle, struct RFCNB_Pkt *Data, int Length);
+
+int RFCNB_Recv(void *Con_Handle, struct RFCNB_Pkt *Data, int Length);
+
+int RFCNB_Hangup(void *con_Handle);
+
+void *RFCNB_Listen();
+
+void RFCNB_Get_Error(char *buffer, int buf_len);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * Session Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+int RFCNB_errno = 0;
+int RFCNB_saved_errno = 0;
+#define RFCNB_ERRNO
+
+#include "std-includes.h"
+#include <netinet/tcp.h>
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+int RFCNB_Stats[RFCNB_MAX_STATS];
+
+void (*Prot_Print_Routine) () = NULL; /* Pointer to print routine */
+
+/* Set up a session with a remote name. We are passed Called_Name as a
+ * string which we convert to a NetBIOS name, ie space terminated, up to
+ * 16 characters only if we need to. If Called_Address is not empty, then
+ * we use it to connect to the remote end, but put in Called_Name ... Called
+ * Address can be a DNS based name, or a TCP/IP address ...
+ */
+
+void *
+RFCNB_Call(char *Called_Name, char *Calling_Name, char *Called_Address,
+ int port)
+{
+ struct RFCNB_Con *con;
+ struct in_addr Dest_IP;
+ int Client;
+ BOOL redirect;
+ struct redirect_addr *redir_addr;
+ char *Service_Address;
+
+ /* Now, we really should look up the port in /etc/services ... */
+
+ if (port == 0)
+ port = RFCNB_Default_Port;
+
+ /* Create a connection structure first */
+
+ if ((con = (struct RFCNB_Con *) malloc(sizeof(struct RFCNB_Con))) == NULL) { /* Error in size */
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ con->fd = -0; /* no descriptor yet */
+ con->rfc_errno = 0; /* no error yet */
+ con->timeout = 0; /* no timeout */
+ con->redirects = 0;
+ con->redirect_list = NULL; /* Fix bug still in version 0.50 */
+
+ /* Resolve that name into an IP address */
+
+ Service_Address = Called_Name;
+ if (strcmp(Called_Address, "") != 0) { /* If the Called Address = "" */
+ Service_Address = Called_Address;
+ }
+ if ((errno = RFCNB_Name_To_IP(Service_Address, &Dest_IP)) < 0) { /* Error */
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_Name_To_IP */
+
+ return (NULL);
+
+ }
+ /* Now connect to the remote end */
+
+ redirect = TRUE; /* Fudge this one so we go once through */
+
+ while (redirect) { /* Connect and get session info etc */
+
+ redirect = FALSE; /* Assume all OK */
+
+ /* Build the redirect info. First one is first addr called */
+ /* And tack it onto the list of addresses we called */
+
+ if ((redir_addr = (struct redirect_addr *) malloc(sizeof(struct redirect_addr))) == NULL) { /* Could not get space */
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ memcpy((char *) &(redir_addr->ip_addr), (char *) &Dest_IP, sizeof(Dest_IP));
+ redir_addr->port = port;
+ redir_addr->next = NULL;
+
+ if (con->redirect_list == NULL) { /* Stick on head */
+
+ con->redirect_list = con->last_addr = redir_addr;
+
+ } else {
+
+ con->last_addr->next = redir_addr;
+ con->last_addr = redir_addr;
+
+ }
+
+ /* Now, make that connection */
+
+ if ((Client = RFCNB_IP_Connect(Dest_IP, port)) < 0) { /* Error */
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_IP_Connect */
+
+ return (NULL);
+
+ }
+ con->fd = Client;
+
+ /* Now send and handle the RFCNB session request */
+ /* If we get a redirect, we will comeback with redirect true
+ * and a new IP address in DEST_IP */
+
+ if ((errno = RFCNB_Session_Req(con,
+ Called_Name,
+ Calling_Name,
+ &redirect, &Dest_IP, &port)) < 0) {
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_Session.. */
+
+ return (NULL);
+
+ }
+ if (redirect) {
+
+ /* We have to close the connection, and then try again */
+
+ (con->redirects)++;
+
+ RFCNB_Close(con->fd); /* Close it */
+
+ }
+ }
+
+ return (con);
+
+}
+
+/* We send a packet to the other end ... for the moment, we treat the
+ * data as a series of pointers to blocks of data ... we should check the
+ * length ... */
+
+int
+RFCNB_Send(struct RFCNB_Con *Con_Handle, struct RFCNB_Pkt *udata, int Length)
+{
+ struct RFCNB_Pkt *pkt;
+ char *hdr;
+ int len;
+
+ /* Plug in the header and send the data */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
+
+ if (pkt == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ pkt->next = udata; /* The user data we want to send */
+
+ hdr = pkt->data;
+
+ /* Following crap is for portability across multiple UNIX machines */
+
+ *(hdr + RFCNB_Pkt_Type_Offset) = RFCNB_SESSION_MESSAGE;
+ RFCNB_Put_Pkt_Len(hdr, Length);
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Sending packet: ");
+
+#endif
+
+ if ((len = RFCNB_Put_Pkt(Con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
+
+ /* No need to change RFCNB_errno as it was done by put_pkt ... */
+
+ return (RFCNBE_Bad); /* Should be able to write that lot ... */
+
+ }
+ /* Now we have sent that lot, let's get rid of the RFCNB Header and return */
+
+ pkt->next = NULL;
+
+ RFCNB_Free_Pkt(pkt);
+
+ return (len);
+
+}
+
+/* We pick up a message from the internet ... We have to worry about
+ * non-message packets ... */
+
+int
+RFCNB_Recv(void *con_Handle, struct RFCNB_Pkt *Data, int Length)
+{
+ struct RFCNB_Pkt *pkt;
+ int ret_len;
+
+ if (con_Handle == NULL) {
+
+ RFCNB_errno = RFCNBE_BadHandle;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ /* Now get a packet from below. We allocate a header first */
+
+ /* Plug in the header and send the data */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
+
+ if (pkt == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ pkt->next = Data; /* Plug in the data portion */
+
+ if ((ret_len = RFCNB_Get_Pkt(con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Bad packet return in RFCNB_Recv... \n");
+#endif
+
+ return (RFCNBE_Bad);
+
+ }
+ /* We should check that we go a message and not a keep alive */
+
+ pkt->next = NULL;
+
+ RFCNB_Free_Pkt(pkt);
+
+ return (ret_len);
+
+}
+
+/* We just disconnect from the other end, as there is nothing in the RFCNB */
+/* protocol that specifies any exchange as far as I can see */
+
+int
+RFCNB_Hangup(struct RFCNB_Con *con_Handle)
+{
+
+ if (con_Handle != NULL) {
+ RFCNB_Close(con_Handle->fd); /* Could this fail? */
+ free(con_Handle);
+ }
+ return 0;
+
+
+}
+
+/* Set TCP_NODELAY on the socket */
+
+int
+RFCNB_Set_Sock_NoDelay(struct RFCNB_Con *con_Handle, BOOL yn)
+{
+
+ return (setsockopt(con_Handle->fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *) &yn, sizeof(yn)));
+
+}
+
+
+/* Listen for a connection on a port???, when */
+/* the connection comes in, we return with the connection */
+
+void
+RFCNB_Listen()
+{
+
+}
+
+/* Pick up the last error response as a string, hmmm, this routine should */
+/* have been different ... */
+
+void
+RFCNB_Get_Error(char *buffer, int buf_len)
+{
+
+ if (RFCNB_saved_errno <= 0) {
+ sprintf(buffer, "%s", RFCNB_Error_Strings[RFCNB_errno]);
+ } else {
+ sprintf(buffer, "%s\n\terrno:%s", RFCNB_Error_Strings[RFCNB_errno],
+ strerror(RFCNB_saved_errno));
+ }
+
+}
+
+/* Pick up the last error response and returns as a code */
+
+int
+RFCNB_Get_Last_Error()
+{
+
+ return (RFCNB_errno);
+
+}
+
+/* Pick up saved errno as well */
+
+int
+RFCNB_Get_Last_Errno()
+{
+
+ return (RFCNB_saved_errno);
+
+}
+
+/* Pick up the last error response and return in string ... */
+
+void
+RFCNB_Get_Error_Msg(int code, char *msg_buf, int len)
+{
+
+ strncpy(msg_buf, RFCNB_Error_Strings[abs(code)], len);
+
+}
+
+/* Register a higher level protocol print routine */
+
+void
+RFCNB_Register_Print_Routine(void (*fn) ())
+{
+
+ Prot_Print_Routine = fn;
+
+}
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ *
+ * a partial implementation of DES designed for use in the
+ * SMB authentication protocol
+ *
+ * Copyright (C) Andrew Tridgell 1997
+ *
+ * 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.
+ */
+
+
+/* NOTES:
+ *
+ * This code makes no attempt to be fast! In fact, it is a very
+ * slow implementation
+ *
+ * This code is NOT a complete DES implementation. It implements only
+ * the minimum necessary for SMB authentication, as used by all SMB
+ * products (including every copy of Microsoft Windows95 ever sold)
+ *
+ * In particular, it can only do a unchained forward DES pass. This
+ * means it is not possible to use this code for encryption/decryption
+ * of data, instead it is only useful as a "hash" algorithm.
+ *
+ * There is no entry point into this code that allows normal DES operation.
+ *
+ * I believe this means that this code does not come under ITAR
+ * regulations but this is NOT a legal opinion. If you are concerned
+ * about the applicability of ITAR regulations to this code then you
+ * should confirm it for yourself (and maybe let me know if you come
+ * up with a different answer to the one above)
+ */
+
+
+
+static int perm1[56] =
+{57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static int perm2[48] =
+{14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static int perm3[64] =
+{58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static int perm4[48] =
+{32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static int perm5[32] =
+{16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static int perm6[64] =
+{40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static int sc[16] =
+{1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static int sbox[8][4][16] =
+{
+ {
+ {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {
+ {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {
+ {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {
+ {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {
+ {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {
+ {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {
+ {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {
+ {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void
+permute(char *out, char *in, int *p, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = in[p[i] - 1];
+}
+
+static void
+lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = d[(i + count) % n];
+ for (i = 0; i < n; i++)
+ d[i] = out[i];
+}
+
+static void
+concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void
+xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void
+dohash(char *out, char *in, char *key)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i = 0; i < 28; i++)
+ c[i] = pk1[i];
+ for (i = 0; i < 28; i++)
+ d[i] = pk1[i + 28];
+
+ for (i = 0; i < 16; i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j = 0; j < 32; j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j + 32];
+ }
+
+ for (i = 0; i < 16; i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[i], 48);
+
+ for (j = 0; j < 8; j++)
+ for (k = 0; k < 6; k++)
+ b[j][k] = erk[j * 6 + k];
+
+ for (j = 0; j < 8; j++) {
+ int m, n;
+ m = (b[j][0] << 1) | b[j][5];
+
+ n = (b[j][1] << 3) | (b[j][2] << 2) | (b[j][3] << 1) | b[j][4];
+
+ for (k = 0; k < 4; k++)
+ b[j][k] = (sbox[j][m][n] & (1 << (3 - k))) ? 1 : 0;
+ }
+
+ for (j = 0; j < 8; j++)
+ for (k = 0; k < 4; k++)
+ cb[j * 4 + k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j = 0; j < 32; j++)
+ l[j] = r[j];
+
+ for (j = 0; j < 32; j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void
+str_to_key(unsigned char *str, unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0] >> 1;
+ key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
+ key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
+ key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
+ key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
+ key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
+ key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
+ key[7] = str[6] & 0x7F;
+ for (i = 0; i < 8; i++) {
+ key[i] = (key[i] << 1);
+ }
+}
+
+
+static void
+smbhash(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ unsigned char key2[8];
+
+ str_to_key(key, key2);
+
+ for (i = 0; i < 64; i++) {
+ inb[i] = (in[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
+ keyb[i] = (key2[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb);
+
+ for (i = 0; i < 8; i++) {
+ out[i] = 0;
+ }
+
+ for (i = 0; i < 64; i++) {
+ if (outb[i])
+ out[i / 8] |= (1 << (7 - (i % 8)));
+ }
+}
+
+void
+E_P16(unsigned char *p14, unsigned char *p16)
+{
+ unsigned char sp8[8] =
+ {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ smbhash(p16, sp8, p14);
+ smbhash(p16 + 8, sp8, p14 + 7);
+}
+
+void
+E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21);
+ smbhash(p24 + 8, c8, p21 + 7);
+ smbhash(p24 + 16, c8, p21 + 14);
+}
+
+void
+cred_hash1(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key);
+ smbhash(out, buf, key + 9);
+}
+
+void
+cred_hash2(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key);
+ key2[0] = key[7];
+ smbhash(out, buf, key2);
+}
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1997
+ * Modified by Jeremy Allison 1995.
+ *
+ * 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 <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+/* Antonino #include <sys/vfs.h> */
+
+#include "smblib-priv.h"
+#define uchar unsigned char
+extern int DEBUGLEVEL;
+
+#include "byteorder.h"
+
+char *StrnCpy(char *dest, char *src, int n);
+void strupper(char *s);
+extern void E_P16(unsigned char *, unsigned char *);
+extern void E_P24(unsigned char *, unsigned char *, unsigned char *);
+extern void mdfour(unsigned char *, unsigned char *, int);
+
+
+/*
+ * This implements the X/Open SMB password encryption
+ * It takes a password, a 8 byte "crypt key" and puts 24 bytes of
+ * encrypted password into p24 */
+void
+SMBencrypt(uchar * passwd, uchar * c8, uchar * p24)
+{
+ uchar p14[15], p21[21];
+
+ memset(p21, '\0', 21);
+ memset(p14, '\0', 14);
+ StrnCpy((char *) p14, (char *) passwd, 14);
+
+ strupper((char *) p14);
+ E_P16(p14, p21);
+ E_P24(p21, c8, p24);
+}
+
+/* Routines for Windows NT MD4 Hash functions. */
+static int
+_my_wcslen(int16 * str)
+{
+ int len = 0;
+ while (*str++ != 0)
+ len++;
+ return len;
+}
+
+/*
+ * Convert a string into an NT UNICODE string.
+ * Note that regardless of processor type
+ * this must be in intel (little-endian)
+ * format.
+ */
+
+static int
+_my_mbstowcs(int16 * dst, uchar * src, int len)
+{
+ int i;
+ int16 val;
+
+ for (i = 0; i < len; i++) {
+ val = *src;
+ SSVAL(dst, 0, val);
+ dst++;
+ src++;
+ if (val == 0)
+ break;
+ }
+ return i;
+}
+
+/*
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ */
+
+void
+E_md4hash(uchar * passwd, uchar * p16)
+{
+ int len;
+ int16 wpwd[129];
+
+ /* Password cannot be longer than 128 characters */
+ len = strlen((char *) passwd);
+ if (len > 128)
+ len = 128;
+ /* Password must be converted to NT unicode */
+ _my_mbstowcs(wpwd, passwd, len);
+ wpwd[len] = 0; /* Ensure string is null terminated */
+ /* Calculate length in bytes */
+ len = _my_wcslen(wpwd) * sizeof(int16);
+
+ mdfour(p16, (unsigned char *) wpwd, len);
+}
+
+/* Does the NT MD4 hash then des encryption. */
+
+void
+SMBNTencrypt(uchar * passwd, uchar * c8, uchar * p24)
+{
+ uchar p21[21];
+
+ memset(p21, '\0', 21);
+
+ E_md4hash(passwd, p21);
+ E_P24(p21, c8, p24);
+}
+
+/* Does both the NT and LM owfs of a user's password */
+
+void
+nt_lm_owf_gen(char *pwd, char *nt_p16, char *p16)
+{
+ char passwd[130];
+ StrnCpy(passwd, pwd, sizeof(passwd) - 1);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash((uchar *) passwd, (uchar *) nt_p16);
+
+ /* Mangle the passwords into Lanman format */
+ passwd[14] = '\0';
+ strupper(passwd);
+
+ /* Calculate the SMB (lanman) hash functions of the password */
+
+ memset(p16, '\0', 16);
+ E_P16((uchar *) passwd, (uchar *) p16);
+
+ /* clear out local copy of user's password (just being paranoid). */
+ bzero(passwd, sizeof(passwd));
+}
+
+/****************************************************************************
+line strncpy but always null terminates. Make sure there is room!
+****************************************************************************/
+char *
+StrnCpy(char *dest, char *src, int n)
+{
+ char *d = dest;
+ if (!dest)
+ return (NULL);
+ if (!src) {
+ *dest = 0;
+ return (dest);
+ }
+ while (n-- && (*d++ = *src++));
+ *d = 0;
+ return (dest);
+}
+
+void
+strupper(char *s)
+{
+ while (*s) {
+#if UNUSED_CODE
+#if !defined(KANJI_WIN95_COMPATIBILITY)
+ if (lp_client_code_page() == KANJI_CODEPAGE) {
+
+ if (is_shift_jis(*s)) {
+ if (is_sj_lower(s[0], s[1]))
+ s[1] = sj_toupper2(s[1]);
+ s += 2;
+ } else if (is_kana(*s)) {
+ s++;
+ } else {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+ } else
+#endif /* KANJI_WIN95_COMPATIBILITY */
+#endif /* UNUSED_CODE */
+ {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+ }
+}
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Common Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* To get the error class we want the first 8 bits */
+/* Because we just grab 4bytes from the SMB header, we have to re-order */
+/* here, but it makes the NtStatus part easier in future */
+
+#ifndef _SMBLIB_COMMON_H_
+#define _SMBLIB_COMMON_H_
+
+#define SMBlib_Error_Class(p) (p & 0x000000FF)
+
+/* To get the error code, we want the bottom 16 bits */
+
+#define SMBlib_Error_Code(p) (((unsigned int)p & 0xFFFF0000) >>16)
+
+/* Error CLASS codes and etc ... */
+
+#define SMBC_SUCCESS 0
+#define SMBC_ERRDOS 0x01
+#define SMBC_ERRSRV 0x02
+#define SMBC_ERRHRD 0x03
+#define SMBC_ERRCMD 0xFF
+
+/* Success error codes */
+
+#define SMBS_BUFFERED 0x54
+#define SMBS_LOGGED 0x55
+#define SMBS_DISPLAYED 0x56
+
+/* ERRDOS Error codes */
+
+#define SMBD_badfunc 0x01
+#define SMBD_badfile 0x02
+#define SMBD_badpath 0x03
+#define SMBD_nofids 0x04
+#define SMBD_noaccess 0x05
+#define SMBD_badfid 0x06
+#define SMBD_badmcb 0x07
+#define SMBD_nomem 0x08
+#define SMBD_badmem 0x09
+#define SMBD_badenv 0x0A
+#define SMBD_badformat 0x0B
+#define SMBD_badaccess 0x0C
+#define SMBD_baddata 0x0D
+#define SMBD_reserved 0x0E
+#define SMBD_baddrive 0x0F
+#define SMBD_remcd 0x10
+#define SMBD_diffdevice 0x11
+#define SMBD_nofiles 0x12
+#define SMBD_badshare 0x20
+#define SMBD_errlock 0x21
+#define SMBD_filexists 0x50
+
+/* Server errors ... */
+
+#define SMBV_error 0x01 /* Generic error */
+#define SMBV_badpw 0x02
+#define SMBV_badtype 0x03
+#define SMBV_access 0x04
+#define SMBV_invnid 0x05
+#define SMBV_invnetname 0x06
+#define SMBV_invdevice 0x07
+#define SMBV_qfull 0x31
+#define SMBV_qtoobig 0x32
+#define SMBV_qeof 0x33
+#define SMBV_invpfid 0x34
+#define SMBV_paused 0x51
+#define SMBV_msgoff 0x52
+#define SMBV_noroom 0x53
+#define SMBV_rmuns 0x57
+#define SMBV_nosupport 0xFFFF
+
+/* Hardware error codes ... */
+
+#define SMBH_nowrite 0x13
+#define SMBH_badunit 0x14
+#define SMBH_notready 0x15
+#define SMBH_badcmd 0x16
+#define SMBH_data 0x17
+#define SMBH_badreq 0x18
+#define SMBH_seek 0x19
+#define SMBH_badmedia 0x1A
+#define SMBH_badsector 0x1B
+#define SMBH_nopaper 0x1C
+#define SMBH_write 0x1D
+#define SMBH_read 0x1E
+#define SMBH_general 0x1F
+#define SMBH_badshare 0x20
+
+/* Access mode defines ... */
+
+#define SMB_AMODE_WTRU 0x4000
+#define SMB_AMODE_NOCACHE 0x1000
+#define SMB_AMODE_COMPAT 0x0000
+#define SMB_AMODE_DENYRWX 0x0010
+#define SMB_AMODE_DENYW 0x0020
+#define SMB_AMODE_DENYRX 0x0030
+#define SMB_AMODE_DENYNONE 0x0040
+#define SMB_AMODE_OPENR 0x0000
+#define SMB_AMODE_OPENW 0x0001
+#define SMB_AMODE_OPENRW 0x0002
+#define SMB_AMODE_OPENX 0x0003
+#define SMB_AMODE_FCBOPEN 0x00FF
+#define SMB_AMODE_LOCUNKN 0x0000
+#define SMB_AMODE_LOCMSEQ 0x0100
+#define SMB_AMODE_LOCMRAN 0x0200
+#define SMB_AMODE_LOCRAL 0x0300
+
+/* File attribute encoding ... */
+
+#define SMB_FA_ORD 0x00
+#define SMB_FA_ROF 0x01
+#define SMB_FA_HID 0x02
+#define SMB_FA_SYS 0x04
+#define SMB_FA_VOL 0x08
+#define SMB_FA_DIR 0x10
+#define SMB_FA_ARC 0x20
+
+/* Define the protocol types ... */
+
+#define SMB_P_Unknown -1 /* Hmmm, is this smart? */
+#define SMB_P_Core 0
+#define SMB_P_CorePlus 1
+#define SMB_P_DOSLanMan1 2
+#define SMB_P_LanMan1 3
+#define SMB_P_DOSLanMan2 4
+#define SMB_P_LanMan2 5
+#define SMB_P_DOSLanMan2_1 6
+#define SMB_P_LanMan2_1 7
+#define SMB_P_NT1 8
+
+/* SMBlib return codes */
+/* We want something that indicates whether or not the return code was a */
+/* remote error, a local error in SMBlib or returned from lower layer ... */
+/* Wonder if this will work ... */
+/* SMBlibE_Remote = 1 indicates remote error */
+/* SMBlibE_ values < 0 indicate local error with more info available */
+/* SMBlibE_ values >1 indicate local from SMBlib code errors? */
+
+#define SMBlibE_Success 0
+#define SMBlibE_Remote 1 /* Remote error, get more info from con */
+#define SMBlibE_BAD -1
+#define SMBlibE_LowerLayer 2 /* Lower layer error */
+#define SMBlibE_NotImpl 3 /* Function not yet implemented */
+#define SMBlibE_ProtLow 4 /* Protocol negotiated does not support req */
+#define SMBlibE_NoSpace 5 /* No space to allocate a structure */
+#define SMBlibE_BadParam 6 /* Bad parameters */
+#define SMBlibE_NegNoProt 7 /* None of our protocols was liked */
+#define SMBlibE_SendFailed 8 /* Sending an SMB failed */
+#define SMBlibE_RecvFailed 9 /* Receiving an SMB failed */
+#define SMBlibE_GuestOnly 10 /* Logged in as guest */
+#define SMBlibE_CallFailed 11 /* Call remote end failed */
+#define SMBlibE_ProtUnknown 12 /* Protocol unknown */
+#define SMBlibE_NoSuchMsg 13 /* Keep this up to date */
+
+typedef struct { /* A structure for a Dirent */
+
+ unsigned char resume_key[21]; /* Don't touch this */
+ unsigned char file_attributes; /* Attributes of file */
+ unsigned int date_time; /* date and time of last mod */
+ unsigned int size;
+ char filename[13]; /* The name of the file */
+
+} SMB_CP_dirent;
+
+#endif /* _SMBLIB_COMMON_H_ */
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib private Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 _SMBLIB_PRIV_H_
+#define _SMBLIB_PRIV_H_
+
+#include "std-defines.h"
+#include "smblib-common.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "byteorder.h" /* Hmmm ... hot good */
+
+#define max(a,b) (a < b ? b : a)
+
+#define SMB_DEF_IDF 0x424D53FF /* "\377SMB" */
+
+/* Core protocol commands */
+
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* CorePlus protocol */
+
+#define SMBlockread 0x13 /* Lock a range and read it */
+#define SMBwriteunlock 0x14 /* Unlock a range and then write */
+#define SMBreadbraw 0x1a /* read a block of data without smb header ohead */
+#define SMBwritebraw 0x1d /* write a block of data without smb header ohead */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file and then close it */
+
+/* DOS Extended Protocol */
+
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBtconX 0x75 /* tree connect and X */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Any more ? */
+
+#define SMBdatablockID 0x01 /* A data block identifier */
+#define SMBdialectID 0x02 /* A dialect id */
+#define SMBpathnameID 0x03 /* A pathname ID */
+#define SMBasciiID 0x04 /* An ascii string ID */
+#define SMBvariableblockID 0x05 /* A variable block ID */
+
+/* some other defines we need */
+
+/* Flags defines ... */
+
+#define SMB_FLG2_NON_DOS 0x01 /* We know non dos names */
+#define SMB_FLG2_EXT_ATR 0x02 /* We know about Extended Attributes */
+#define SMB_FLG2_LNG_NAM 0x04 /* Long names ? */
+
+typedef unsigned short WORD;
+typedef unsigned short UWORD;
+typedef unsigned int ULONG;
+typedef unsigned char BYTE;
+typedef unsigned char UCHAR;
+
+/* Some macros to allow access to actual packet data so that we */
+/* can change the underlying representation of packets. */
+/* */
+/* The current formats vying for attention are a fragment */
+/* approach where the SMB header is a fragment linked to the */
+/* data portion with the transport protocol (rfcnb or whatever) */
+/* being linked on the front. */
+/* */
+/* The other approach is where the whole packet is one array */
+/* of bytes with space allowed on the front for the packet */
+/* headers. */
+
+#define SMB_Hdr(p) (char *)(p -> data)
+
+/* SMB Hdr def for File Sharing Protocol? From MS and Intel, */
+/* Intel PN 138446 Doc Version 2.0, Nov 7, 1988. This def also */
+/* applies to LANMAN1.0 as well as the Core Protocol */
+/* The spec states that wct and bcc must be present, even if 0 */
+
+/* We define these as offsets into a char SMB[] array for the */
+/* sake of portability */
+
+/* NOTE!. Some of the lenght defines, SMB_<protreq>_len do not include */
+/* the data that follows in the SMB packet, so the code will have to */
+/* take that into account. */
+
+#define SMB_hdr_idf_offset 0 /* 0xFF,'SMB' 0-3 */
+#define SMB_hdr_com_offset 4 /* BYTE 4 */
+#define SMB_hdr_rcls_offset 5 /* BYTE 5 */
+#define SMB_hdr_reh_offset 6 /* BYTE 6 */
+#define SMB_hdr_err_offset 7 /* WORD 7 */
+#define SMB_hdr_reb_offset 9 /* BYTE 9 */
+#define SMB_hdr_flg_offset 9 /* same as reb ... */
+#define SMB_hdr_res_offset 10 /* 7 WORDs 10 */
+#define SMB_hdr_res0_offset 10 /* WORD 10 */
+#define SMB_hdr_flg2_offset 10 /* WORD */
+#define SMB_hdr_res1_offset 12 /* WORD 12 */
+#define SMB_hdr_res2_offset 14
+#define SMB_hdr_res3_offset 16
+#define SMB_hdr_res4_offset 18
+#define SMB_hdr_res5_offset 20
+#define SMB_hdr_res6_offset 22
+#define SMB_hdr_tid_offset 24
+#define SMB_hdr_pid_offset 26
+#define SMB_hdr_uid_offset 28
+#define SMB_hdr_mid_offset 30
+#define SMB_hdr_wct_offset 32
+
+#define SMB_hdr_len 33 /* 33 byte header? */
+
+#define SMB_hdr_axc_offset 33 /* AndX Command */
+#define SMB_hdr_axr_offset 34 /* AndX Reserved */
+#define SMB_hdr_axo_offset 35 /* Offset from start to WCT of AndX cmd */
+
+/* Format of the Negotiate Protocol SMB */
+
+#define SMB_negp_bcc_offset 33
+#define SMB_negp_buf_offset 35 /* Where the buffer starts */
+#define SMB_negp_len 35 /* plus the data */
+
+/* Format of the Negotiate Response SMB, for CoreProtocol, LM1.2 and */
+/* NT LM 0.12. wct will be 1 for CoreProtocol, 13 for LM 1.2, and 17 */
+/* for NT LM 0.12 */
+
+#define SMB_negrCP_idx_offset 33 /* Response to the neg req */
+#define SMB_negrCP_bcc_offset 35
+#define SMB_negrLM_idx_offset 33 /* dialect index */
+#define SMB_negrLM_sec_offset 35 /* Security mode */
+#define SMB_sec_user_mask 0x01 /* 0 = share, 1 = user */
+#define SMB_sec_encrypt_mask 0x02 /* pick out encrypt */
+#define SMB_negrLM_mbs_offset 37 /* max buffer size */
+#define SMB_negrLM_mmc_offset 39 /* max mpx count */
+#define SMB_negrLM_mnv_offset 41 /* max number of VCs */
+#define SMB_negrLM_rm_offset 43 /* raw mode support bit vec */
+#define SMB_read_raw_mask 0x01
+#define SMB_write_raw_mask 0x02
+#define SMB_negrLM_sk_offset 45 /* session key, 32 bits */
+#define SMB_negrLM_st_offset 49 /* Current server time */
+#define SMB_negrLM_sd_offset 51 /* Current server date */
+#define SMB_negrLM_stz_offset 53 /* Server Time Zone */
+#define SMB_negrLM_ekl_offset 55 /* encryption key length */
+#define SMB_negrLM_res_offset 57 /* reserved */
+#define SMB_negrLM_bcc_offset 59 /* bcc */
+#define SMB_negrLM_len 61 /* 61 bytes ? */
+#define SMB_negrLM_buf_offset 61 /* Where the fun begins */
+
+#define SMB_negrNTLM_idx_offset 33 /* Selected protocol */
+#define SMB_negrNTLM_sec_offset 35 /* Security more */
+#define SMB_negrNTLM_mmc_offset 36 /* Different format above */
+#define SMB_negrNTLM_mnv_offset 38 /* Max VCs */
+#define SMB_negrNTLM_mbs_offset 40 /* MBS now a long */
+#define SMB_negrNTLM_mrs_offset 44 /* Max raw size */
+#define SMB_negrNTLM_sk_offset 48 /* Session Key */
+#define SMB_negrNTLM_cap_offset 52 /* Capabilities */
+#define SMB_negrNTLM_stl_offset 56 /* Server time low */
+#define SMB_negrNTLM_sth_offset 60 /* Server time high */
+#define SMB_negrNTLM_stz_offset 64 /* Server time zone */
+#define SMB_negrNTLM_ekl_offset 66 /* Encrypt key len */
+#define SMB_negrNTLM_bcc_offset 67 /* Bcc */
+#define SMB_negrNTLM_len 69
+#define SMB_negrNTLM_buf_offset 69
+
+/* Offsets related to Tree Connect */
+
+#define SMB_tcon_bcc_offset 33
+#define SMB_tcon_buf_offset 35 /* where the data is for tcon */
+#define SMB_tcon_len 35 /* plus the data */
+
+#define SMB_tconr_mbs_offset 33 /* max buffer size */
+#define SMB_tconr_tid_offset 35 /* returned tree id */
+#define SMB_tconr_bcc_offset 37
+#define SMB_tconr_len 39
+
+#define SMB_tconx_axc_offset 33 /* And X Command */
+#define SMB_tconx_axr_offset 34 /* reserved */
+#define SMB_tconx_axo_offset 35 /* Next command offset */
+#define SMB_tconx_flg_offset 37 /* Flags, bit0=1 means disc TID */
+#define SMB_tconx_pwl_offset 39 /* Password length */
+#define SMB_tconx_bcc_offset 41 /* bcc */
+#define SMB_tconx_buf_offset 43 /* buffer */
+#define SMB_tconx_len 43 /* up to data ... */
+
+#define SMB_tconxr_axc_offset 33 /* Where the AndX Command is */
+#define SMB_tconxr_axr_offset 34 /* Reserved */
+#define SMB_tconxr_axo_offset 35 /* AndX offset location */
+
+/* Offsets related to tree_disconnect */
+
+#define SMB_tdis_bcc_offset 33 /* bcc */
+#define SMB_tdis_len 35 /* total len */
+
+#define SMB_tdisr_bcc_offset 33 /* bcc */
+#define SMB_tdisr_len 35
+
+/* Offsets related to Open Request */
+
+#define SMB_open_mod_offset 33 /* Mode to open with */
+#define SMB_open_atr_offset 35 /* Attributes of file */
+#define SMB_open_bcc_offset 37 /* bcc */
+#define SMB_open_buf_offset 39 /* File name */
+#define SMB_open_len 39 /* Plus the file name */
+
+#define SMB_openx_axc_offset 33 /* Next command */
+#define SMB_openx_axr_offset 34 /* Reserved */
+#define SMB_openx_axo_offset 35 /* offset of next wct */
+#define SMB_openx_flg_offset 37 /* Flags, bit0 = need more info */
+ /* bit1 = exclusive oplock */
+ /* bit2 = batch oplock */
+#define SMB_openx_mod_offset 39 /* mode to open with */
+#define SMB_openx_atr_offset 41 /* search attributes */
+#define SMB_openx_fat_offset 43 /* File attributes */
+#define SMB_openx_tim_offset 45 /* time and date of creat */
+#define SMB_openx_ofn_offset 49 /* Open function */
+#define SMB_openx_als_offset 51 /* Space to allocate on */
+#define SMB_openx_res_offset 55 /* reserved */
+#define SMB_openx_bcc_offset 63 /* bcc */
+#define SMB_openx_buf_offset 65 /* Where file name goes */
+#define SMB_openx_len 65
+
+#define SMB_openr_fid_offset 33 /* FID returned */
+#define SMB_openr_atr_offset 35 /* Attributes opened with */
+#define SMB_openr_tim_offset 37 /* Last mod time of file */
+#define SMB_openr_fsz_offset 41 /* File size 4 bytes */
+#define SMB_openr_acc_offset 45 /* Access allowed */
+#define SMB_openr_bcc_offset 47
+#define SMB_openr_len 49
+
+#define SMB_openxr_axc_offset 33 /* And X command */
+#define SMB_openxr_axr_offset 34 /* reserved */
+#define SMB_openxr_axo_offset 35 /* offset to next command */
+#define SMB_openxr_fid_offset 37 /* FID returned */
+#define SMB_openxr_fat_offset 39 /* File attributes returned */
+#define SMB_openxr_tim_offset 41 /* File creation date etc */
+#define SMB_openxr_fsz_offset 45 /* Size of file */
+#define SMB_openxr_acc_offset 49 /* Access granted */
+
+#define SMB_clos_fid_offset 33 /* FID to close */
+#define SMB_clos_tim_offset 35 /* Last mod time */
+#define SMB_clos_bcc_offset 39 /* bcc */
+#define SMB_clos_len 41
+
+/* Offsets related to Write requests */
+
+#define SMB_write_fid_offset 33 /* FID to write */
+#define SMB_write_cnt_offset 35 /* bytes to write */
+#define SMB_write_ofs_offset 37 /* location to write to */
+#define SMB_write_clf_offset 41 /* advisory count left */
+#define SMB_write_bcc_offset 43 /* bcc = data bytes + 3 */
+#define SMB_write_buf_offset 45 /* Data=0x01, len, data */
+#define SMB_write_len 45 /* plus the data ... */
+
+#define SMB_writr_cnt_offset 33 /* Count of bytes written */
+#define SMB_writr_bcc_offset 35 /* bcc */
+#define SMB_writr_len 37
+
+/* Offsets related to read requests */
+
+#define SMB_read_fid_offset 33 /* FID of file to read */
+#define SMB_read_cnt_offset 35 /* count of words to read */
+#define SMB_read_ofs_offset 37 /* Where to read from */
+#define SMB_read_clf_offset 41 /* Advisory count to go */
+#define SMB_read_bcc_offset 43
+#define SMB_read_len 45
+
+#define SMB_readr_cnt_offset 33 /* Count of bytes returned */
+#define SMB_readr_res_offset 35 /* 4 shorts reserved, 8 bytes */
+#define SMB_readr_bcc_offset 43 /* bcc */
+#define SMB_readr_bff_offset 45 /* buffer format char = 0x01 */
+#define SMB_readr_len_offset 46 /* buffer len */
+#define SMB_readr_len 45 /* length of the readr before data */
+
+/* Offsets for Create file */
+
+#define SMB_creat_atr_offset 33 /* Attributes of new file ... */
+#define SMB_creat_tim_offset 35 /* Time of creation */
+#define SMB_creat_dat_offset 37 /* 4004BCE :-) */
+#define SMB_creat_bcc_offset 39 /* bcc */
+#define SMB_creat_buf_offset 41
+#define SMB_creat_len 41 /* Before the data */
+
+#define SMB_creatr_fid_offset 33 /* FID of created file */
+
+/* Offsets for Delete file */
+
+#define SMB_delet_sat_offset 33 /* search attribites */
+#define SMB_delet_bcc_offset 35 /* bcc */
+#define SMB_delet_buf_offset 37
+#define SMB_delet_len 37
+
+/* Offsets for SESSION_SETUP_ANDX for both LM and NT LM protocols */
+
+#define SMB_ssetpLM_mbs_offset 37 /* Max buffer Size, allow for AndX */
+#define SMB_ssetpLM_mmc_offset 39 /* max multiplex count */
+#define SMB_ssetpLM_vcn_offset 41 /* VC number if new VC */
+#define SMB_ssetpLM_snk_offset 43 /* Session Key */
+#define SMB_ssetpLM_pwl_offset 47 /* password length */
+#define SMB_ssetpLM_res_offset 49 /* reserved */
+#define SMB_ssetpLM_bcc_offset 53 /* bcc */
+#define SMB_ssetpLM_len 55 /* before data ... */
+#define SMB_ssetpLM_buf_offset 55
+
+#define SMB_ssetpNTLM_mbs_offset 37 /* Max Buffer Size for NT LM 0.12 */
+ /* and above */
+#define SMB_ssetpNTLM_mmc_offset 39 /* Max Multiplex count */
+#define SMB_ssetpNTLM_vcn_offset 41 /* VC Number */
+#define SMB_ssetpNTLM_snk_offset 43 /* Session key */
+#define SMB_ssetpNTLM_cipl_offset 47 /* Case Insensitive PW Len */
+#define SMB_ssetpNTLM_cspl_offset 49 /* Unicode pw len */
+#define SMB_ssetpNTLM_res_offset 51 /* reserved */
+#define SMB_ssetpNTLM_cap_offset 55 /* server capabilities */
+#define SMB_ssetpNTLM_bcc_offset 59 /* bcc */
+#define SMB_ssetpNTLM_len 61 /* before data */
+#define SMB_ssetpNTLM_buf_offset 61
+
+#define SMB_ssetpr_axo_offset 35 /* Offset of next response ... */
+#define SMB_ssetpr_act_offset 37 /* action, bit 0 = 1 => guest */
+#define SMB_ssetpr_bcc_offset 39 /* bcc */
+#define SMB_ssetpr_buf_offset 41 /* Native OS etc */
+
+/* Offsets for SMB create directory */
+
+#define SMB_creatdir_bcc_offset 33 /* only a bcc here */
+#define SMB_creatdir_buf_offset 35 /* Where things start */
+#define SMB_creatdir_len 35
+
+/* Offsets for SMB delete directory */
+
+#define SMB_deletdir_bcc_offset 33 /* only a bcc here */
+#define SMB_deletdir_buf_offset 35 /* where things start */
+#define SMB_deletdir_len 35
+
+/* Offsets for SMB check directory */
+
+#define SMB_checkdir_bcc_offset 33 /* Only a bcc here */
+#define SMB_checkdir_buf_offset 35 /* where things start */
+#define SMB_checkdir_len 35
+
+/* Offsets for SMB search */
+
+#define SMB_search_mdc_offset 33 /* Max Dir ents to return */
+#define SMB_search_atr_offset 35 /* Search attributes */
+#define SMB_search_bcc_offset 37 /* bcc */
+#define SMB_search_buf_offset 39 /* where the action is */
+#define SMB_search_len 39
+
+#define SMB_searchr_dec_offset 33 /* Dir ents returned */
+#define SMB_searchr_bcc_offset 35 /* bcc */
+#define SMB_searchr_buf_offset 37 /* Where the action starts */
+#define SMB_searchr_len 37 /* before the dir ents */
+
+#define SMB_searchr_dirent_len 43 /* 53 bytes */
+
+/* Defines for SMB transact and transact2 calls */
+
+#define SMB_trans_tpc_offset 33 /* Total param count */
+#define SMB_trans_tdc_offset 35 /* total Data count */
+#define SMB_trans_mpc_offset 37 /* Max params bytes to return */
+#define SMB_trans_mdc_offset 39 /* Max data bytes to return */
+#define SMB_trans_msc_offset 41 /* Max setup words to return */
+#define SMB_trans_rs1_offset 42 /* Reserved byte */
+#define SMB_trans_flg_offset 43 /* flags */
+#define SMB_trans_tmo_offset 45 /* Timeout, long */
+#define SMB_trans_rs2_offset 49 /* Next reserved */
+#define SMB_trans_pbc_offset 51 /* Param Byte count in buf */
+#define SMB_trans_pbo_offset 53 /* Offset to param bytes */
+#define SMB_trans_dbc_offset 55 /* Data byte count in buf */
+#define SMB_trans_dbo_offset 57 /* Data byte offset */
+#define SMB_trans_suc_offset 59 /* Setup count - byte */
+#define SMB_trans_rs3_offset 60 /* Reserved to pad ... */
+#define SMB_trans_len 61 /* Up to setup, still need bcc */
+
+#define SMB_transr_tpc_offset 33 /* Total param bytes returned */
+#define SMB_transr_tdc_offset 35
+#define SMB_transr_rs1_offset 37
+#define SMB_transr_pbc_offset 39
+#define SMB_transr_pbo_offset 41
+#define SMB_transr_pdi_offset 43 /* parameter displacement */
+#define SMB_transr_dbc_offset 45
+#define SMB_transr_dbo_offset 47
+#define SMB_transr_ddi_offset 49
+#define SMB_transr_suc_offset 51
+#define SMB_transr_rs2_offset 52
+#define SMB_transr_len 53
+
+/* Bit masks for SMB Capabilities ... */
+
+#define SMB_cap_raw_mode 0x0001
+#define SMB_cap_mpx_mode 0x0002
+#define SMB_cap_unicode 0x0004
+#define SMB_cap_large_files 0x0008
+#define SMB_cap_nt_smbs 0x0010
+#define SMB_rpc_remote_apis 0x0020
+#define SMB_cap_nt_status 0x0040
+#define SMB_cap_level_II_oplocks 0x0080
+#define SMB_cap_lock_and_read 0x0100
+#define SMB_cap_nt_find 0x0200
+
+/* SMB LANMAN api call defines */
+
+#define SMB_LMapi_SetUserInfo 0x0072
+#define SMB_LMapi_UserPasswordSet 0x0073
+
+/* Structures and defines we use in the client interface */
+
+/* The protocols we might support. Perhaps a bit ambitious, as only RFCNB */
+/* has any support so far 0(sometimes called NBT) */
+
+typedef enum {
+ SMB_RFCNB, SMB_IPXNB, SMB_NETBEUI, SMB_X25
+} SMB_Transport_Types;
+
+typedef enum {
+ SMB_Con_FShare, SMB_Con_PShare, SMB_Con_IPC
+} SMB_Con_Types;
+
+typedef enum {
+ SMB_State_NoState, SMB_State_Stopped, SMB_State_Started
+} SMB_State_Types;
+
+/* The following two arrays need to be in step! */
+/* We must make it possible for callers to specify these ... */
+
+
+extern char *SMB_Prots[];
+extern int SMB_Types[];
+
+typedef struct SMB_Status {
+
+ union {
+ struct {
+ unsigned char ErrorClass;
+ unsigned char Reserved;
+ unsigned short Error;
+ } DosError;
+ unsigned int NtStatus;
+ } status;
+} SMB_Status;
+
+typedef struct SMB_Tree_Structure *SMB_Tree_Handle;
+
+typedef struct SMB_Connect_Def *SMB_Handle_Type;
+
+struct SMB_Connect_Def {
+
+ SMB_Handle_Type Next_Con, Prev_Con; /* Next and previous conn */
+ int protocol; /* What is the protocol */
+ int prot_IDX; /* And what is the index */
+ void *Trans_Connect; /* The connection */
+
+ /* All these strings should be malloc'd */
+
+ char service[80], username[80], password[80], desthost[80], sock_options[80];
+ char address[80], myname[80];
+
+ SMB_Tree_Handle first_tree, last_tree; /* List of trees on this server */
+
+ int gid; /* Group ID, do we need it? */
+ int mid; /* Multiplex ID? We might need one per con */
+ int pid; /* Process ID */
+
+ int uid; /* Authenticated user id. */
+
+ /* It is pretty clear that we need to bust some of */
+ /* these out into a per TCon record, as there may */
+ /* be multiple TCon's per server, etc ... later */
+
+ int port; /* port to use in case not default, this is a TCPism! */
+
+ int max_xmit; /* Max xmit permitted by server */
+ int Security; /* 0 = share, 1 = user */
+ int Raw_Support; /* bit 0 = 1 = Read Raw supported, 1 = 1 Write raw */
+ BOOL encrypt_passwords; /* FALSE = don't */
+ int MaxMPX, MaxVC, MaxRaw;
+ unsigned int SessionKey, Capabilities;
+ int SvrTZ; /* Server Time Zone */
+ int Encrypt_Key_Len;
+ char Encrypt_Key[80], Domain[80], PDomain[80], OSName[80], LMType[40];
+ char Svr_OS[80], Svr_LMType[80], Svr_PDom[80];
+
+};
+
+#define SMBLIB_DEFAULT_DOMAIN "STAFF"
+#define SMBLIB_DEFAULT_OSNAME "UNIX of some type"
+#define SMBLIB_DEFAULT_LMTYPE "SMBlib LM2.1 minus a bit"
+#define SMBLIB_MAX_XMIT 65535
+
+#define SMB_Sec_Mode_Share 0
+#define SMB_Sec_Mode_User 1
+
+/* A Tree_Structure */
+
+struct SMB_Tree_Structure {
+
+ SMB_Tree_Handle next, prev;
+ SMB_Handle_Type con;
+ char path[129];
+ char device_type[20];
+ int mbs; /* Local MBS */
+ int tid;
+
+};
+
+typedef struct SMB_File_Def SMB_File;
+
+struct SMB_File_Def {
+
+ SMB_Tree_Handle tree;
+ char filename[256]; /* We should malloc this ... */
+ UWORD fid;
+ unsigned int lastmod;
+ unsigned int size; /* Could blow up if 64bit files supported */
+ UWORD access;
+ off_t fileloc;
+
+};
+
+/* global Variables for the library */
+
+extern SMB_State_Types SMBlib_State;
+
+#ifndef SMBLIB_ERRNO
+extern int SMBlib_errno;
+extern int SMBlib_SMB_Error; /* last Error */
+#endif
+
+#endif /* _SMBLIB_PRIV_H_ */
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Utility Routines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "smblib-priv.h"
+
+#include "rfcnb.h"
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+char *SMB_Prots[] =
+{"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "DOS LANMAN1.0",
+ "LANMAN1.0",
+ "DOS LM1.2X002",
+ "LM1.2X002",
+ "DOS LANMAN2.1",
+ "LANMAN2.1",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+
+int SMB_Types[] =
+{SMB_P_Core,
+ SMB_P_CorePlus,
+ SMB_P_DOSLanMan1,
+ SMB_P_DOSLanMan1,
+ SMB_P_LanMan1,
+ SMB_P_DOSLanMan2,
+ SMB_P_LanMan2,
+ SMB_P_LanMan2_1,
+ SMB_P_LanMan2_1,
+ SMB_P_NT1,
+ SMB_P_NT1,
+ SMB_P_NT1,
+ -1};
+
+/* Print out an SMB pkt in all its gory detail ... */
+
+void
+SMB_Print_Pkt(FILE fd, RFCNB_Pkt * pkt, BOOL command, int Offset, int Len)
+{
+
+ /* Well, just how do we do this ... print it I suppose */
+
+ /* Print out the SMB header ... */
+
+ /* Print the command */
+
+ /* Print the other bits in the header */
+
+
+ /* etc */
+
+}
+
+/* Convert a DOS Date_Time to a local host type date time for printing */
+
+char *
+SMB_DOSTimToStr(int DOS_time)
+{
+ static char SMB_Time_Temp[48];
+ int DOS_sec, DOS_min, DOS_hour, DOS_day, DOS_month, DOS_year;
+
+ SMB_Time_Temp[0] = 0;
+
+ DOS_sec = (DOS_time & 0x001F) * 2;
+ DOS_min = (DOS_time & 0x07E0) >> 5;
+ DOS_hour = ((DOS_time & 0xF800) >> 11);
+
+ DOS_day = (DOS_time & 0x001F0000) >> 16;
+ DOS_month = (DOS_time & 0x01E00000) >> 21;
+ DOS_year = ((DOS_time & 0xFE000000) >> 25) + 80;
+
+ sprintf(SMB_Time_Temp, "%2d/%02d/%2d %2d:%02d:%02d", DOS_day, DOS_month,
+ DOS_year, DOS_hour, DOS_min, DOS_sec);
+
+ return (SMB_Time_Temp);
+
+}
+
+/* Convert an attribute byte/word etc to a string ... We return a pointer
+ * to a static string which we guarantee is long enough. If verbose is
+ * true, we print out long form of strings ... */
+
+char *
+SMB_AtrToStr(int attribs, BOOL verbose)
+{
+ static char SMB_Attrib_Temp[128];
+
+ SMB_Attrib_Temp[0] = 0;
+
+ if (attribs & SMB_FA_ROF)
+ strcat(SMB_Attrib_Temp, (verbose ? "Read Only " : "R"));
+
+ if (attribs & SMB_FA_HID)
+ strcat(SMB_Attrib_Temp, (verbose ? "Hidden " : "H"));
+
+ if (attribs & SMB_FA_SYS)
+ strcat(SMB_Attrib_Temp, (verbose ? "System " : "S"));
+
+ if (attribs & SMB_FA_VOL)
+ strcat(SMB_Attrib_Temp, (verbose ? "Volume " : "V"));
+
+ if (attribs & SMB_FA_DIR)
+ strcat(SMB_Attrib_Temp, (verbose ? "Directory " : "D"));
+
+ if (attribs & SMB_FA_ARC)
+ strcat(SMB_Attrib_Temp, (verbose ? "Archive " : "A"));
+
+ return (SMB_Attrib_Temp);
+
+}
+
+/* Pick up the Max Buffer Size from the Tree Structure ... */
+
+int
+SMB_Get_Tree_MBS(SMB_Tree_Handle tree)
+{
+ if (tree != NULL) {
+ return (tree->mbs);
+ } else {
+ return (SMBlibE_BAD);
+ }
+}
+
+/* Pick up the Max buffer size */
+
+int
+SMB_Get_Max_Buf_Siz(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->max_xmit);
+ } else {
+ return (SMBlibE_BAD);
+ }
+
+}
+/* Pickup the protocol index from the connection structure */
+
+int
+SMB_Get_Protocol_IDX(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->prot_IDX);
+ } else {
+ return (0xFFFF); /* Invalid protocol */
+ }
+
+}
+
+/* Pick up the protocol from the connection structure */
+
+int
+SMB_Get_Protocol(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->protocol);
+ } else {
+ return (0xFFFF); /* Invalid protocol */
+ }
+
+}
+
+/* Figure out what protocol was accepted, given the list of dialect strings */
+/* We offered, and the index back from the server. We allow for a user */
+/* supplied list, and assume that it is a subset of our list */
+
+int
+SMB_Figure_Protocol(char *dialects[], int prot_index)
+{
+ int i;
+
+ if (dialects == SMB_Prots) { /* The jobs is easy, just index into table */
+
+ return (SMB_Types[prot_index]);
+ } else { /* Search through SMB_Prots looking for a match */
+
+ for (i = 0; SMB_Prots[i] != NULL; i++) {
+
+ if (strcmp(dialects[prot_index], SMB_Prots[i]) == 0) { /* A match */
+
+ return (SMB_Types[i]);
+
+ }
+ }
+
+ /* If we got here, then we are in trouble, because the protocol was not */
+ /* One we understand ... */
+
+ return (SMB_P_Unknown);
+
+ }
+
+}
+
+
+/* Negotiate the protocol we will use from the list passed in Prots */
+/* we return the index of the accepted protocol in NegProt, -1 indicates */
+/* none acceptible, and our return value is 0 if ok, <0 if problems */
+
+int
+SMB_Negotiate(SMB_Handle_Type Con_Handle, char *Prots[])
+{
+ struct RFCNB_Pkt *pkt;
+ int prots_len, i, pkt_len, prot, alloc_len;
+ char *p;
+
+ /* Figure out how long the prot list will be and allocate space for it */
+
+ prots_len = 0;
+
+ for (i = 0; Prots[i] != NULL; i++) {
+
+ prots_len = prots_len + strlen(Prots[i]) + 2; /* Account for null etc */
+
+ }
+
+ /* The -1 accounts for the one byte smb_buf we have because some systems */
+ /* don't like char msg_buf[] */
+
+ pkt_len = SMB_negp_len + prots_len;
+
+ /* Make sure that the pkt len is long enough for the max response ... */
+ /* Which is a problem, because the encryption key len eec may be long */
+
+ if (pkt_len < (SMB_hdr_wct_offset + (19 * 2) + 40)) {
+
+ alloc_len = SMB_hdr_wct_offset + (19 * 2) + 40;
+
+ } else {
+
+ alloc_len = pkt_len;
+
+ }
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(alloc_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now plug in the bits we need */
+
+ bzero(SMB_Hdr(pkt), SMB_negp_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBnegprot;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_negp_bcc_offset, prots_len);
+
+ /* Now copy the prot strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_negp_buf_offset);
+
+ for (i = 0; Prots[i] != NULL; i++) {
+
+ *p = SMBdialectID;
+ strcpy(p + 1, Prots[i]);
+ p = p + strlen(Prots[i]) + 2; /* Adjust len of p for null plus dialectID */
+
+ }
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending negotiate protocol\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed; /* Failed, check lower layer errno */
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, alloc_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to negotiate\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed; /* Failed, check lower layer errno */
+ return (SMBlibE_BAD);
+
+ }
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_Negotiate failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+ if (SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset) == 0xFFFF) {
+
+#ifdef DEBUG
+ fprintf(stderr, "None of our protocols was accepted ... ");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_NegNoProt;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now, unpack the info from the response, if any and evaluate the proto */
+ /* selected. We must make sure it is one we like ... */
+
+ Con_Handle->prot_IDX = prot = SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset);
+ Con_Handle->protocol = SMB_Figure_Protocol(Prots, prot);
+
+ if (Con_Handle->protocol == SMB_P_Unknown) { /* No good ... */
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_ProtUnknown;
+ return (SMBlibE_BAD);
+
+ }
+ switch (CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset)) {
+
+ case 0x01: /* No more info ... */
+
+ break;
+
+ case 13: /* Up to and including LanMan 2.1 */
+
+ Con_Handle->Security = SVAL(SMB_Hdr(pkt), SMB_negrLM_sec_offset);
+ Con_Handle->encrypt_passwords = ((Con_Handle->Security & SMB_sec_encrypt_mask) != 0x00);
+ Con_Handle->Security = Con_Handle->Security & SMB_sec_user_mask;
+
+ Con_Handle->max_xmit = SVAL(SMB_Hdr(pkt), SMB_negrLM_mbs_offset);
+ Con_Handle->MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrLM_mmc_offset);
+ Con_Handle->MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrLM_mnv_offset);
+ Con_Handle->Raw_Support = SVAL(SMB_Hdr(pkt), SMB_negrLM_rm_offset);
+ Con_Handle->SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrLM_sk_offset);
+ Con_Handle->SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrLM_stz_offset);
+ Con_Handle->Encrypt_Key_Len = SVAL(SMB_Hdr(pkt), SMB_negrLM_ekl_offset);
+
+ p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset);
+ fprintf(stderr, "%d", (int) (SMB_Hdr(pkt) + SMB_negrLM_buf_offset));
+ memcpy(Con_Handle->Encrypt_Key, p, 8);
+
+ p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset + Con_Handle->Encrypt_Key_Len);
+
+ strncpy(p, Con_Handle->Svr_PDom, sizeof(Con_Handle->Svr_PDom) - 1);
+
+ break;
+
+ case 17: /* NT LM 0.12 and LN LM 1.0 */
+
+ Con_Handle->Security = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_sec_offset);
+ Con_Handle->encrypt_passwords = ((Con_Handle->Security & SMB_sec_encrypt_mask) != 0x00);
+ Con_Handle->Security = Con_Handle->Security & SMB_sec_user_mask;
+
+ Con_Handle->max_xmit = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mbs_offset);
+ Con_Handle->MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mmc_offset);
+ Con_Handle->MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mnv_offset);
+ Con_Handle->MaxRaw = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mrs_offset);
+ Con_Handle->SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_sk_offset);
+ Con_Handle->SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_stz_offset);
+ Con_Handle->Encrypt_Key_Len = CVAL(SMB_Hdr(pkt), SMB_negrNTLM_ekl_offset);
+
+ p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset);
+ memcpy(Con_Handle->Encrypt_Key, p, 8);
+ p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset + Con_Handle->Encrypt_Key_Len);
+
+ strncpy(p, Con_Handle->Svr_PDom, sizeof(Con_Handle->Svr_PDom) - 1);
+
+ break;
+
+ default:
+
+#ifdef DEBUG
+ fprintf(stderr, "Unknown NegProt response format ... Ignored\n");
+ fprintf(stderr, " wct = %i\n", CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset));
+#endif
+
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Protocol selected is: %i:%s\n", prot, Prots[prot]);
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ return (0);
+
+}
+
+/* Get our hostname */
+
+void
+SMB_Get_My_Name(char *name, int len)
+{
+
+ if (gethostname(name, len) < 0) { /* Error getting name */
+
+ strncpy(name, "unknown", len);
+
+ /* Should check the error */
+
+#ifdef DEBUG
+ fprintf(stderr, "gethostname in SMB_Get_My_Name returned error:");
+ perror("");
+#endif
+
+ }
+ /* only keep the portion up to the first "." */
+
+
+}
+
+/* Send a TCON to the remote server ... */
+
+SMB_Tree_Handle
+SMB_TreeConnect(SMB_Handle_Type Con_Handle,
+ SMB_Tree_Handle Tree_Handle,
+ char *path,
+ char *password,
+ char *device)
+{
+ struct RFCNB_Pkt *pkt;
+ int param_len, pkt_len;
+ char *p;
+ SMB_Tree_Handle tree;
+
+ /* Figure out how much space is needed for path, password, dev ... */
+
+ if ((path == NULL) | (password == NULL) | (device == NULL)) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Bad parameter passed to SMB_TreeConnect\n");
+#endif
+
+ SMBlib_errno = SMBlibE_BadParam;
+ return (NULL);
+
+ }
+ /* The + 2 is because of the \0 and the marker ... */
+
+ param_len = strlen(path) + 2 + strlen(password) + 2 + strlen(device) + 2;
+
+ /* The -1 accounts for the one byte smb_buf we have because some systems */
+ /* don't like char msg_buf[] */
+
+ pkt_len = SMB_tcon_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (NULL); /* Should handle the error */
+
+ }
+ /* Now allocate a tree for this to go into ... */
+
+ if (Tree_Handle == NULL) {
+
+ tree = (SMB_Tree_Handle) malloc(sizeof(struct SMB_Tree_Structure));
+
+ if (tree == NULL) {
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (NULL);
+
+ }
+ } else {
+
+ tree = Tree_Handle;
+
+ }
+
+ tree->next = tree->prev = NULL;
+ tree->con = Con_Handle;
+ strncpy(tree->path, path, sizeof(tree->path));
+ strncpy(tree->device_type, device, sizeof(tree->device_type));
+
+ /* Now plug in the values ... */
+
+ bzero(SMB_Hdr(pkt), SMB_tcon_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtcon;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_tcon_buf_offset);
+ *p = SMBasciiID;
+ strcpy(p + 1, path);
+ p = p + strlen(path) + 2;
+ *p = SMBasciiID;
+ strcpy(p + 1, password);
+ p = p + strlen(password) + 2;
+ *p = SMBasciiID;
+ strcpy(p + 1, device);
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending TCon request\n");
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed;
+ return (NULL);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to TCon\n");
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed;
+ return (NULL);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_TCon failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (NULL);
+
+ }
+ tree->tid = SVAL(SMB_Hdr(pkt), SMB_tconr_tid_offset);
+ tree->mbs = SVAL(SMB_Hdr(pkt), SMB_tconr_mbs_offset);
+
+#ifdef DEBUG
+ fprintf(stderr, "TConn succeeded, with TID=%i, Max Xmit=%i\n",
+ tree->tid, tree->mbs);
+#endif
+
+ /* Now link the Tree to the Server Structure ... */
+
+ if (Con_Handle->first_tree == NULL) {
+
+ Con_Handle->first_tree = tree;
+ Con_Handle->last_tree = tree;
+
+ } else {
+
+ Con_Handle->last_tree->next = tree;
+ tree->prev = Con_Handle->last_tree;
+ Con_Handle->last_tree = tree;
+
+ }
+
+ RFCNB_Free_Pkt(pkt);
+ return (tree);
+
+}
+
+int
+SMB_TreeDisconnect(SMB_Tree_Handle Tree_Handle, BOOL discard)
+{
+ struct RFCNB_Pkt *pkt;
+ int pkt_len;
+
+ pkt_len = SMB_tdis_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (SMBlibE_BAD); /* Should handle the error */
+
+ }
+ /* Now plug in the values ... */
+
+ bzero(SMB_Hdr(pkt), SMB_tdis_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtdis;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Tree_Handle->con->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Tree_Handle->con->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Tree_Handle->con->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, Tree_Handle->tid);
+ SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, 0);
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Tree_Handle->con->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending TDis request\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Tree_Handle->con->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to TCon\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_TDis failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+ Tree_Handle->tid = 0xFFFF; /* Invalid TID */
+ Tree_Handle->mbs = 0; /* Invalid */
+
+#ifdef DEBUG
+
+ fprintf(stderr, "Tree disconnect successful ...\n");
+
+#endif
+
+ /* What about the tree handle ? */
+
+ if (discard == TRUE) { /* Unlink it and free it ... */
+
+ if (Tree_Handle->next == NULL)
+ Tree_Handle->con->first_tree = Tree_Handle->prev;
+ else
+ Tree_Handle->next->prev = Tree_Handle->prev;
+
+ if (Tree_Handle->prev == NULL)
+ Tree_Handle->con->last_tree = Tree_Handle->next;
+ else
+ Tree_Handle->prev->next = Tree_Handle->next;
+
+ }
+ RFCNB_Free_Pkt(pkt);
+ return (0);
+
+}
+
+/* Pick up the last LMBlib error ... */
+
+int
+SMB_Get_Last_Error()
+{
+
+ return (SMBlib_errno);
+
+}
+
+/* Pick up the last error returned in an SMB packet */
+/* We will need macros to extract error class and error code */
+
+int
+SMB_Get_Last_SMB_Err()
+{
+
+ return (SMBlib_SMB_Error);
+
+}
+
+/* Pick up the error message associated with an error from SMBlib */
+
+/* Keep this table in sync with the message codes in smblib-common.h */
+
+static char *SMBlib_Error_Messages[] =
+{
+
+ "Request completed sucessfully.",
+ "Server returned a non-zero SMB Error Class and Code.",
+ "A lower layer protocol error occurred.",
+ "Function not yet implemented.",
+ "The protocol negotiated does not support the request.",
+ "No space available for operation.",
+ "One or more bad parameters passed.",
+ "None of the protocols we offered were accepted.",
+ "The attempt to send an SMB request failed. See protocol error info.",
+ "The attempt to get an SMB response failed. See protocol error info.",
+ "The logon request failed, but you were logged in as guest.",
+ "The attempt to call the remote server failed. See protocol error info.",
+ "The protocol dialect specified in a NegProt and accepted by the server is unknown.",
+ /* This next one simplifies error handling */
+ "No such error code.",
+ NULL};
+
+void
+SMB_Get_Error_Msg(int msg, char *msgbuf, int len)
+{
+
+ if (msg >= 0) {
+
+ strncpy(msgbuf,
+ SMBlib_Error_Messages[msg > SMBlibE_NoSuchMsg ? SMBlibE_NoSuchMsg : msg],
+ len - 1);
+ msgbuf[len - 1] = 0; /* Make sure it is a string */
+ } else { /* Add the lower layer message ... */
+
+ char prot_msg[1024];
+
+ msg = -msg; /* Make it positive */
+
+ strncpy(msgbuf,
+ SMBlib_Error_Messages[msg > SMBlibE_NoSuchMsg ? SMBlibE_NoSuchMsg : msg],
+ len - 1);
+
+ msgbuf[len - 1] = 0; /* make sure it is a string */
+
+ if (strlen(msgbuf) < len) { /* If there is space, put rest in */
+
+ strncat(msgbuf, "\n\t", len - strlen(msgbuf));
+
+ RFCNB_Get_Error(prot_msg, sizeof(prot_msg) - 1);
+
+ strncat(msgbuf, prot_msg, len - strlen(msgbuf));
+
+ }
+ }
+
+}
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Routines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+int SMBlib_errno;
+int SMBlib_SMB_Error;
+#define SMBLIB_ERRNO
+#define uchar unsigned char
+#include "smblib-priv.h"
+#include "smblib.h"
+#include "rfcnb-priv.h"
+#include "rfcnb.h"
+#include "rfcnb-util.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <signal.h>
+
+SMB_State_Types SMBlib_State;
+
+extern int RFCNB_Set_Sock_NoDelay(RFCNB_Con *, BOOL);
+extern void SMB_Get_My_Name(char *, int);
+
+/* Initialize the SMBlib package */
+
+int
+SMB_Init()
+{
+
+ SMBlib_State = SMB_State_Started;
+
+ signal(SIGPIPE, SIG_IGN); /* Ignore these ... */
+
+/* If SMBLIB_Instrument is defines, turn on the instrumentation stuff */
+#ifdef SMBLIB_INSTRUMENT
+
+ SMBlib_Instrument_Init();
+
+#endif
+
+ return 0;
+
+}
+
+int
+SMB_Term()
+{
+
+#ifdef SMBLIB_INSTRUMENT
+
+ SMBlib_Instrument_Term(); /* Clean up and print results */
+
+#endif
+
+ return 0;
+
+}
+
+/* SMB_Create: Create a connection structure and return for later use */
+/* We have other helper routines to set variables */
+
+SMB_Handle_Type
+SMB_Create_Con_Handle(void)
+{
+
+ SMBlib_errno = SMBlibE_NotImpl;
+ return (NULL);
+
+}
+
+int
+SMBlib_Set_Sock_NoDelay(SMB_Handle_Type Con_Handle, BOOL yn)
+{
+
+
+ if (RFCNB_Set_Sock_NoDelay(Con_Handle->Trans_Connect, yn) < 0) {
+
+#ifdef DEBUG
+#endif
+
+ fprintf(stderr, "Setting no-delay on TCP socket failed ...\n");
+
+ }
+ return (0);
+
+}
+
+/* SMB_Connect_Server: Connect to a server, but don't negotiate protocol */
+/* or anything else ... */
+
+SMB_Handle_Type
+SMB_Connect_Server(SMB_Handle_Type Con_Handle,
+ char *server, char *NTdomain)
+{
+ SMB_Handle_Type con;
+ char called[80], calling[80], *address;
+ int i;
+
+ /* Get a connection structure if one does not exist */
+
+ con = Con_Handle;
+
+ if (Con_Handle == NULL) {
+
+ if ((con = (struct SMB_Connect_Def *) malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
+
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return NULL;
+ }
+ }
+ /* Init some things ... */
+
+ strcpy(con->service, "");
+ strcpy(con->username, "");
+ strcpy(con->password, "");
+ strcpy(con->sock_options, "");
+ strcpy(con->address, "");
+ strcpy(con->desthost, server);
+ strcpy(con->PDomain, NTdomain);
+ strcpy(con->OSName, SMBLIB_DEFAULT_OSNAME);
+ strcpy(con->LMType, SMBLIB_DEFAULT_LMTYPE);
+ con->first_tree = con->last_tree = NULL;
+
+ SMB_Get_My_Name(con->myname, sizeof(con->myname));
+
+ con->port = 0; /* No port selected */
+
+ /* Get some things we need for the SMB Header */
+
+ con->pid = getpid();
+ con->mid = con->pid; /* This will do for now ... */
+ con->uid = 0; /* Until we have done a logon, no uid ... */
+ con->gid = getgid();
+
+ /* Now connect to the remote end, but first upper case the name of the
+ * service we are going to call, sine some servers want it in uppercase */
+
+ for (i = 0; i < strlen(server); i++)
+ called[i] = toupper(server[i]);
+
+ called[strlen(server)] = 0; /* Make it a string */
+
+ for (i = 0; i < strlen(con->myname); i++)
+ calling[i] = toupper(con->myname[i]);
+
+ calling[strlen(con->myname)] = 0; /* Make it a string */
+
+ if (strcmp(con->address, "") == 0)
+ address = con->desthost;
+ else
+ address = con->address;
+
+ con->Trans_Connect = RFCNB_Call(called,
+ calling,
+ address, /* Protocol specific */
+ con->port);
+
+ /* Did we get one? */
+
+ if (con->Trans_Connect == NULL) {
+
+ if (Con_Handle == NULL) {
+ Con_Handle = NULL;
+ free(con);
+ }
+ SMBlib_errno = -SMBlibE_CallFailed;
+ return NULL;
+
+ }
+ return (con);
+
+}
+
+/* SMB_Connect: Connect to the indicated server */
+/* If Con_Handle == NULL then create a handle and connect, otherwise */
+/* use the handle passed */
+
+char *SMB_Prots_Restrict[] =
+{"PC NETWORK PROGRAM 1.0",
+ NULL};
+
+
+SMB_Handle_Type
+SMB_Connect(SMB_Handle_Type Con_Handle,
+ SMB_Tree_Handle * tree,
+ char *service,
+ char *username,
+ char *password)
+{
+ SMB_Handle_Type con;
+ char *host, *address;
+ char temp[80], called[80], calling[80];
+ int i;
+
+ /* Get a connection structure if one does not exist */
+
+ con = Con_Handle;
+
+ if (Con_Handle == NULL) {
+
+ if ((con = (struct SMB_Connect_Def *) malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return NULL;
+ }
+ }
+ /* Init some things ... */
+
+ strcpy(con->service, service);
+ strcpy(con->username, username);
+ strcpy(con->password, password);
+ strcpy(con->sock_options, "");
+ strcpy(con->address, "");
+ strcpy(con->PDomain, SMBLIB_DEFAULT_DOMAIN);
+ strcpy(con->OSName, SMBLIB_DEFAULT_OSNAME);
+ strcpy(con->LMType, SMBLIB_DEFAULT_LMTYPE);
+ con->first_tree = con->last_tree = NULL;
+
+ SMB_Get_My_Name(con->myname, sizeof(con->myname));
+
+ con->port = 0; /* No port selected */
+
+ /* Get some things we need for the SMB Header */
+
+ con->pid = getpid();
+ con->mid = con->pid; /* This will do for now ... */
+ con->uid = 0; /* Until we have done a logon, no uid */
+ con->gid = getgid();
+
+ /* Now figure out the host portion of the service */
+
+ strcpy(temp, service);
+ /* AI - Added (char *) to stop compiler warnings */
+ host = (char *) strtok(temp, "/\\"); /* Separate host name portion */
+ strcpy(con->desthost, host);
+
+ /* Now connect to the remote end, but first upper case the name of the
+ * service we are going to call, sine some servers want it in uppercase */
+
+ for (i = 0; i < strlen(host); i++)
+ called[i] = toupper(host[i]);
+
+ called[strlen(host)] = 0; /* Make it a string */
+
+ for (i = 0; i < strlen(con->myname); i++)
+ calling[i] = toupper(con->myname[i]);
+
+ calling[strlen(con->myname)] = 0; /* Make it a string */
+
+ if (strcmp(con->address, "") == 0)
+ address = con->desthost;
+ else
+ address = con->address;
+
+ con->Trans_Connect = RFCNB_Call(called,
+ calling,
+ address, /* Protocol specific */
+ con->port);
+
+ /* Did we get one? */
+
+ if (con->Trans_Connect == NULL) {
+
+ if (Con_Handle == NULL) {
+ free(con);
+ Con_Handle = NULL;
+ }
+ SMBlib_errno = -SMBlibE_CallFailed;
+ return NULL;
+
+ }
+ /* Now, negotiate the protocol */
+
+ if (SMB_Negotiate(con, SMB_Prots_Restrict) < 0) {
+
+ /* Hmmm what should we do here ... We have a connection, but could not
+ * negotiate ... */
+
+ return NULL;
+
+ }
+ /* Now connect to the service ... */
+
+ if ((*tree = SMB_TreeConnect(con, NULL, service, password, "A:")) == NULL) {
+
+ return NULL;
+
+ }
+ return (con);
+
+}
+
+/* Logon to the server. That is, do a session setup if we can. We do not do */
+/* Unicode yet! */
+
+int
+SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
+ char *PassWord)
+{
+ struct RFCNB_Pkt *pkt;
+ int param_len, pkt_len, pass_len;
+ char *p, pword[128];
+
+ /* First we need a packet etc ... but we need to know what protocol has */
+ /* been negotiated to figure out if we can do it and what SMB format to */
+ /* use ... */
+
+ if (Con_Handle->protocol < SMB_P_LanMan1) {
+
+ SMBlib_errno = SMBlibE_ProtLow;
+ return (SMBlibE_BAD);
+
+ }
+ strcpy(pword, PassWord);
+#ifdef PAM_SMB_ENC_PASS
+ if (Con_Handle->encrypt_passwords) {
+ pass_len = 24;
+ SMBencrypt((uchar *) PassWord, (uchar *) Con_Handle->Encrypt_Key, (uchar *) pword);
+ } else
+#endif
+ pass_len = strlen(pword);
+
+
+ /* Now build the correct structure */
+
+ if (Con_Handle->protocol < SMB_P_NT1) {
+
+ param_len = strlen(UserName) + 1 + pass_len + 1 +
+ strlen(Con_Handle->PDomain) + 1 +
+ strlen(Con_Handle->OSName) + 1;
+
+ pkt_len = SMB_ssetpLM_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (SMBlibE_BAD); /* Should handle the error */
+
+ }
+ bzero(SMB_Hdr(pkt), SMB_ssetpLM_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10;
+ *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
+
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mbs_offset, SMBLIB_MAX_XMIT);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mmc_offset, 2);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_vcn_offset, Con_Handle->pid);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_snk_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_pwl_offset, pass_len + 1);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_res_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_ssetpLM_buf_offset);
+
+ /* Copy in password, then the rest. Password has a null at end */
+
+ memcpy(p, pword, pass_len);
+
+ p = p + pass_len + 1;
+
+ strcpy(p, UserName);
+ p = p + strlen(UserName);
+ *p = 0;
+
+ p = p + 1;
+
+ strcpy(p, Con_Handle->PDomain);
+ p = p + strlen(Con_Handle->PDomain);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->OSName);
+ p = p + strlen(Con_Handle->OSName);
+ *p = 0;
+
+ } else {
+
+ /* We don't admit to UNICODE support ... */
+
+ param_len = strlen(UserName) + 1 + pass_len +
+ strlen(Con_Handle->PDomain) + 1 +
+ strlen(Con_Handle->OSName) + 1 +
+ strlen(Con_Handle->LMType) + 1;
+
+ pkt_len = SMB_ssetpNTLM_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (-1); /* Should handle the error */
+
+ }
+ bzero(SMB_Hdr(pkt), SMB_ssetpNTLM_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13;
+ *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
+
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_vcn_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_snk_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cipl_offset, pass_len);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cspl_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_res_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cap_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_ssetpNTLM_buf_offset);
+
+ /* Copy in password, then the rest. Password has no null at end */
+
+ memcpy(p, pword, pass_len);
+
+ p = p + pass_len;
+
+ strcpy(p, UserName);
+ p = p + strlen(UserName);
+ *p = 0;
+
+ p = p + 1;
+
+ strcpy(p, Con_Handle->PDomain);
+ p = p + strlen(Con_Handle->PDomain);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->OSName);
+ p = p + strlen(Con_Handle->OSName);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->LMType);
+ p = p + strlen(Con_Handle->LMType);
+ *p = 0;
+
+ }
+
+ /* Now send it and get a response */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending SessSetupX request\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_SendFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to SessSetupAndX\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_RecvFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_SessSetupAndX failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+#ifdef DEBUG
+ fprintf(stderr, "SessSetupAndX response. Action = %i\n",
+ SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset));
+#endif
+
+ /* Now pick up the UID for future reference ... */
+
+ Con_Handle->uid = SVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset);
+ RFCNB_Free_Pkt(pkt);
+
+ return (0);
+
+}
+
+
+/* Disconnect from the server, and disconnect all tree connects */
+
+int
+SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle)
+{
+
+ /* We just disconnect the connection for now ... */
+
+ RFCNB_Hangup(Con_Handle->Trans_Connect);
+
+ if (!KeepHandle)
+ free(Con_Handle);
+
+ return (0);
+
+}
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "std-defines.h"
+#include "smblib-common.h"
+
+/* Just define all the entry points */
+
+/* Create a handle to allow us to set/override some parameters ... */
+
+SMB_Handle_Type SMB_Create_Con_Handle();
+
+/* Connect to a server, but do not do a tree con etc ... */
+
+SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type, char *server, char *NTdomain);
+
+/* Connect to a server and give us back a handle. If Con == NULL, create */
+/* The handle and populate it with defaults */
+
+SMB_Handle_Type SMB_Connect(SMB_Handle_Type Con_Handle,
+ SMB_Tree_Handle * tree,
+ char *service,
+ char *username,
+ char *password);
+
+/* Negotiate a protocol */
+
+int SMB_Negotiate(void *Con_Handle, char *Prots[]);
+
+/* Connect to a tree ... */
+
+void *SMB_TreeConnect(void *con_handle, void *tree_handle,
+ char *path, char *password, char *dev);
+
+/* Disconnect a tree ... */
+
+int SMB_TreeDisconect(void *tree_handle);
+
+/* Open a file */
+
+void *SMB_Open(void *tree_handle,
+ void *file_handle,
+ char *file_name,
+ unsigned short mode,
+ unsigned short search);
+
+/* Close a file */
+
+int SMB_Close(void *file_handle);
+
+/* Disconnect from server. Has flag to specify whether or not we keep the */
+/* handle. */
+
+int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle);
+
+void *SMB_Create(void *Tree_Handle,
+ void *File_Handle,
+ char *file_name,
+ short search);
+
+int SMB_Delete(void *tree, char *file_name, short search);
+
+int SMB_Create_Dir(void *tree, char *dir_name);
+
+int SMB_Delete_Dir(void *tree, char *dir_name);
+
+int SMB_Check_Dir(void *tree, char *dir_name);
+
+int SMB_Get_Last_Error();
+
+int SMB_Get_Last_SMB_Err();
+
+int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
+
+void *SMB_Logon_And_TCon(void *con, void *tree, char *user, char *pass,
+ char *service, char *st);
--- /dev/null
+/* RFCNB Standard includes ... */
+/*
+ *
+ * SMBlib Standard Includes
+ *
+ * Copyright (C) 1996, Richard Sharpe
+ */
+/* One day we will conditionalize these on OS types ... */
+
+/*
+ * 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 _STD_DEFINES_H_
+#define _STD_DEFINES_H_
+
+#define BOOL int
+typedef short int16;
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#endif /* _STD_DEFINES_H_ */
--- /dev/null
+/* RFCNB Standard includes ... */
+/*
+ *
+ * RFCNB Standard Includes
+ *
+ * Copyright (C) 1996, Richard Sharpe
+ */
+/* One day we will conditionalize these on OS types ... */
+
+/*
+ * 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.
+ */
+
+#define BOOL int
+typedef short int16;
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/* Pick up define for INADDR_NONE */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
--- /dev/null
+#include <sys/types.h>
+#include <unistd.h>
+#include <syslog.h>
+#include "smblib-priv.h"
+#include "smblib.h"
+#include "valid.h"
+
+extern int SMB_Init(void);
+extern int SMB_Logon_Server(SMB_Handle_Type, char *, char *);
+
+
+int
+Valid_User(char *USERNAME, char *PASSWORD, char *SERVER, char *BACKUP, char *DOMAIN)
+{
+ char *SMB_Prots[] =
+ {"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "LANMAN1.0",
+ "LM1.2X002",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+ void *con;
+
+ SMB_Init();
+ con = SMB_Connect_Server(NULL, SERVER, DOMAIN);
+ if (con == NULL) { /* Error ... */
+ con = SMB_Connect_Server(NULL, BACKUP, DOMAIN);
+ if (con == NULL) {
+ return (NTV_SERVER_ERROR);
+ }
+ }
+ if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */
+ SMB_Discon(con, 0);
+ return (NTV_PROTOCOL_ERROR);
+ }
+ if (SMB_Logon_Server(con, USERNAME, PASSWORD) < 0) {
+ SMB_Discon(con, 0);
+ return (NTV_LOGON_ERROR);
+ }
+ SMB_Discon(con, 0);
+ return (NTV_NO_ERROR);
+}
--- /dev/null
+#ifndef _VALID_H_
+#define _VALID_H_
+/* SMB User verification function */
+
+#define NTV_NO_ERROR 0
+#define NTV_SERVER_ERROR 1
+#define NTV_PROTOCOL_ERROR 2
+#define NTV_LOGON_ERROR 3
+
+int Valid_User(char *USERNAME, char *PASSWORD, char *SERVER, char *BACKUP, char *DOMAIN);
+
+#endif
--- /dev/null
+# Makefile for storage modules in the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:43 hno Exp $
+#
+
+# The 'nop' is in the SUBDIRS list because some Unixes that can't
+# handle empty for lists.
+
+SUBDIRS = @AUTH_BASIC_HELPERS@ nop
+
+all:
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) all" || exit 1; \
+ fi; \
+ done;
+
+clean:
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) clean"; \
+ fi; \
+ done
+
+distclean:
+ -rm -f Makefile
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) distclean"; \
+ fi; \
+ done
+
+.DEFAULT:
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $@" || exit 1; \
+ fi; \
+ done;
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:45 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+NCSA_AUTH_EXE = ncsa_auth$(exec_suffix)
+
+DEFAULT_PASSWD_FILE = $(sysconfdir)/passwd
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+SNMPLIB = @SNMPLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(NCSA_AUTH_EXE)
+OBJS = ncsa_auth.o
+
+all: $(NCSA_AUTH_EXE)
+
+$(OBJS): $(top_srcdir)/include/version.h
+
+$(NCSA_AUTH_EXE): ncsa_auth.o
+ $(CC) $(LDFLAGS) ncsa_auth.o -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+tags:
+ ctags *.[ch] ../include/*.h ../lib/*.[ch]
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+/*
+ * ncsa_auth.c
+ *
+ * AUTHOR: Arjan de Vet <Arjan.deVet@adv.iae.nl>
+ *
+ * Example authentication program for Squid, based on the original
+ * proxy_auth code from client_side.c, written by
+ * Jon Thackray <jrmt@uk.gdscorp.com>.
+ *
+ * Uses a NCSA httpd style password file for authentication with the
+ * following improvements suggested by various people:
+ *
+ * - comment lines are possible and should start with a '#';
+ * - empty or blank lines are possible;
+ * - extra fields in the password file are ignored; this makes it
+ * possible to use a Unix password file but I do not recommend that.
+ *
+ */
+
+#include "config.h"
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "util.h"
+#include "hash.h"
+
+static hash_table *hash = NULL;
+static HASHFREE my_free;
+
+typedef struct _user_data {
+ /* first two items must be same as hash_link */
+ char *user;
+ struct _user_data *next;
+ char *passwd;
+} user_data;
+
+static void
+my_free(void *p)
+{
+ user_data *u = p;
+ xfree(u->user);
+ xfree(u->passwd);
+ xfree(u);
+}
+
+static void
+read_passwd_file(const char *passwdfile)
+{
+ FILE *f;
+ char buf[8192];
+ user_data *u;
+ char *user;
+ char *passwd;
+ if (hash != NULL) {
+ hashFreeItems(hash, my_free);
+ }
+ /* initial setup */
+ hash = hash_create((HASHCMP *) strcmp, 7921, hash_string);
+ if (NULL == hash) {
+ fprintf(stderr, "ncsa_auth: cannot create hash table\n");
+ exit(1);
+ }
+ f = fopen(passwdfile, "r");
+ while (fgets(buf, 8192, f) != NULL) {
+ if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
+ (buf[0] == '\n'))
+ continue;
+ user = strtok(buf, ":\n");
+ passwd = strtok(NULL, ":\n");
+ if ((strlen(user) > 0) && passwd) {
+ u = xmalloc(sizeof(*u));
+ u->user = xstrdup(user);
+ u->passwd = xstrdup(passwd);
+ hash_join(hash, (hash_link *) u);
+ }
+ }
+ fclose(f);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct stat sb;
+ time_t change_time = 0;
+ char buf[256];
+ char *user, *passwd, *p;
+ user_data *u;
+ setbuf(stdout, NULL);
+ if (argc != 2) {
+ fprintf(stderr, "Usage: ncsa_auth <passwordfile>\n");
+ exit(1);
+ }
+ if (stat(argv[1], &sb) != 0) {
+ fprintf(stderr, "cannot stat %s\n", argv[1]);
+ exit(1);
+ }
+ while (fgets(buf, 256, stdin) != NULL) {
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+ if (stat(argv[1], &sb) == 0) {
+ if (sb.st_mtime != change_time) {
+ read_passwd_file(argv[1]);
+ change_time = sb.st_mtime;
+ }
+ }
+ if ((user = strtok(buf, " ")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ if ((passwd = strtok(NULL, "")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ u = hash_lookup(hash, user);
+ if (u == NULL) {
+ printf("ERR\n");
+ } else if (strcmp(u->passwd, (char *) crypt(passwd, u->passwd))) {
+ printf("ERR\n");
+ } else {
+ printf("OK\n");
+ }
+ }
+ exit(0);
+}
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:45 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+PAM_AUTH_EXE = pam_auth$(exec_suffix)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+SNMPLIB = @SNMPLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@ @DLLIB@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+DEFINES =
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -lpam $(XTRA_LIBS)
+
+LIBPROGS = $(PAM_AUTH_EXE)
+OBJS = pam_auth.o
+
+all: $(PAM_AUTH_EXE)
+
+$(PAM_AUTH_EXE): pam_auth.o
+ $(CC) $(LDFLAGS) pam_auth.o -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(libexecdir); then \
+ echo "mkdir $(libexecdir)"; \
+ mkdir $(libexecdir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(LIBPROGS); do \
+ if test -f $(libexecdir)/$$f; then \
+ echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(libexecdir); \
+ $(INSTALL_BIN) $$f $(libexecdir); \
+ if test -f $(libexecdir)/-$$f; then \
+ echo $(RM) -f $(libexecdir)/-$$f; \
+ $(RM) -f $(libexecdir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(LIBPROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+tags:
+ ctags *.[ch]
+
+depend:
+ $(MAKEDEPEND) -fMakefile *.c
--- /dev/null
+/*
+ * $Id: pam_auth.c,v 1.1 2001/01/07 23:36:45 hno Exp $
+ *
+ * PAM authenticator module for Squid.
+ * Copyright (C) 1999 Henrik Nordstrom <hno@hem.passagen.se>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Install instructions:
+ *
+ * This program authenticates users against a PAM configured authentication
+ * service "squid". This allows you to authenticate Squid users to any
+ * authentication source for which you have a PAM module. Commonly available
+ * PAM modules includes "UNIX", RADIUS, Kerberos and SMB, but a lot of other
+ * PAM modules are available from various sources.
+ *
+ * Example PAM configuration for standard UNIX passwd authentication:
+ * /etc/pam.conf:
+ * squid auth required /lib/security/pam_unix.so.1
+ * squid account required /lib/security/pam_unix.so.1
+ *
+ * Note that some PAM modules (for example shadow password authentication)
+ * requires the program to be installed suid root, or PAM will not allow
+ * it to authenticate other users than it runs as (this is a security
+ * limitation of PAM to avoid automated probing of passwords).
+ *
+ * Compile this program with: gcc -o pam_auth pam_auth.c -lpam -ldl
+ *
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+
+#include <security/pam_appl.h>
+
+#define BUFSIZE 8192
+
+
+/* The default PAM service name */
+#ifndef SQUID_PAM_SERVICE
+#define SQUID_PAM_SERVICE "squid"
+#endif
+
+/* How often to reinitialize PAM, in seconds. Undefined = never, 0=always */
+/* #define PAM_CONNECTION_TTL 60 */
+
+static int reset_pam = 1; /* Set to one if it is time to reset PAM processing */
+
+static char *password = NULL; /* Workaround for Solaris 2.6 brokenness */
+
+/*
+ * A simple "conversation" function returning the supplied password.
+ * Has a bit to much error control, but this is my first PAM application
+ * so I'd rather check everything than make any mistakes. The function
+ * expects a single converstation message of type PAM_PROMPT_ECHO_OFF.
+ */
+static int
+password_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
+{
+ if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
+ fprintf(stderr, "ERROR: Unexpected PAM converstaion '%d/%s'\n", msg[0]->msg_style, msg[0]->msg);
+ return PAM_CONV_ERR;
+ }
+ if (!appdata_ptr) {
+ /* Workaround for Solaris 2.6 where the PAM library is broken
+ * and does not pass appdata_ptr to the conversation routine
+ */
+ appdata_ptr = password;
+ }
+ if (!appdata_ptr) {
+ fprintf(stderr, "ERROR: No password available to password_converstation!\n");
+ return PAM_CONV_ERR;
+ }
+ *resp = calloc(num_msg, sizeof(struct pam_response));
+ if (!*resp) {
+ fprintf(stderr, "ERROR: Out of memory!\n");
+ return PAM_CONV_ERR;
+ }
+ (*resp)[0].resp = strdup((char *) appdata_ptr);
+ (*resp)[0].resp_retcode = 0;
+
+ return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
+}
+
+static struct pam_conv conv =
+{
+ &password_conversation,
+ NULL
+};
+
+void
+signal_received(int sig)
+{
+ reset_pam = 1;
+ signal(sig, signal_received);
+}
+
+int
+main(int argc, char *argv[])
+{
+ pam_handle_t *pamh = NULL;
+ int retval;
+ char *user;
+ /* char *password; */
+ char buf[BUFSIZE];
+ time_t pamh_created = 0;
+
+ signal(SIGHUP, signal_received);
+
+ /* make standard output line buffered */
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ while (retval = PAM_SUCCESS, fgets(buf, BUFSIZE, stdin)) {
+ user = buf;
+ password = strchr(buf, '\n');
+ if (!password) {
+ fprintf(stderr, "authenticator: Unexpected input '%s'\n", buf);
+ fprintf(stdout, "ERR\n");
+ continue;
+ }
+ *password = '\0';
+ password = strchr(buf, ' ');
+ if (!password) {
+ fprintf(stderr, "authenticator: Unexpected input '%s'\n", buf);
+ fprintf(stdout, "ERR\n");
+ continue;
+ }
+ *password++ = '\0';
+ conv.appdata_ptr = (char *) password; /* from buf above. not allocated */
+#ifdef PAM_CONNECTION_TTL
+ if (pamh_created + PAM_CONNECTION_TTL >= time(NULL))
+ reset_pam = 1;
+#endif
+ if (reset_pam && pamh) {
+ /* Close previous PAM connection */
+ retval = pam_end(pamh, retval);
+ if (retval != PAM_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to release PAM authenticator\n");
+ }
+ pamh = NULL;
+ }
+ if (!pamh) {
+ /* Initialize PAM connection */
+ retval = pam_start(SQUID_PAM_SERVICE, "squid@", &conv, &pamh);
+ if (retval != PAM_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to create PAM authenticator\n");
+ }
+ reset_pam = 0;
+ pamh_created = time(NULL);
+ }
+ if (retval == PAM_SUCCESS)
+ retval = pam_set_item(pamh, PAM_USER, user);
+ if (retval == PAM_SUCCESS)
+ retval = pam_set_item(pamh, PAM_CONV, &conv);
+ if (retval == PAM_SUCCESS)
+ retval = pam_authenticate(pamh, 0);
+ if (retval == PAM_SUCCESS)
+ retval = pam_acct_mgmt(pamh, 0);
+ if (retval == PAM_SUCCESS) {
+ fprintf(stdout, "OK\n");
+ } else {
+ fprintf(stdout, "ERR\n");
+ }
+ }
+
+ if (pamh) {
+ retval = pam_end(pamh, retval);
+ if (retval != PAM_SUCCESS) {
+ pamh = NULL;
+ fprintf(stderr, "ERROR: failed to release PAM authenticator\n");
+ }
+ }
+ return (retval == PAM_SUCCESS ? 0 : 1); /* indicate success */
+}
--- /dev/null
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:46 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+# SAMBAPREFIX must point to the directory where Samba has been installed.
+# By default, Samba is installed in /usr/local/samba. If you changed this
+# by using the --prefix option when configuring Samba, you need to change
+# SAMBAPREFIX accordingly.
+
+SAMBAPREFIX=/usr/local/samba
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+SMB_AUTH_EXE = smb_auth$(exec_suffix)
+SMB_AUTH_HELPER = smb_auth.sh
+SMB_AUTH_HELPER_PATH = $(libexecdir)/$(SMB_AUTH_HELPER)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+SNMPLIB = @SNMPLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+DEFINES = -DSAMBAPREFIX=\"$(SAMBAPREFIX)\" -DHELPERSCRIPT=\"$(SMB_AUTH_HELPER_PATH)\"
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = $(XTRA_LIBS)
+
+LIBPROGS = $(SMB_AUTH_EXE)
+LIBSCRIPTS = $(SMB_AUTH_HELPER)
+OBJS = smb_auth.o
+
+all: $(LIBPROGS)
+
+$(SMB_AUTH_EXE): smb_auth.o
+ $(CC) $(LDFLAGS) smb_auth.o -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(libexecdir); then \
+ echo "mkdir $(libexecdir)"; \
+ mkdir $(libexecdir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(LIBPROGS); do \
+ if test -f $(libexecdir)/$$f; then \
+ echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(libexecdir); \
+ $(INSTALL_BIN) $$f $(libexecdir); \
+ if test -f $(libexecdir)/-$$f; then \
+ echo $(RM) -f $(libexecdir)/-$$f; \
+ $(RM) -f $(libexecdir)/-$$f; \
+ fi; \
+ done
+ @for f in $(LIBSCRIPTS); do \
+ if test -f $(libexecdir)/$$f; then \
+ echo $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ $(MV) $(libexecdir)/$$f $(libexecdir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(libexecdir); \
+ $(INSTALL_BIN) $(srcdir)/$$f $(libexecdir); \
+ if test -f $(libexecdir)/-$$f; then \
+ echo $(RM) -f $(libexecdir)/-$$f; \
+ $(RM) -f $(libexecdir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(LIBPROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+tags:
+ ctags *.[ch]
+
+depend:
+ $(MAKEDEPEND) -fMakefile *.c
--- /dev/null
+For documentation, please refer to
+
+ http://www.hacom.nl/~richard/software/smb_auth.html
--- /dev/null
+/*
+ * smb_auth - SMB proxy authentication module
+ * Copyright (C) 1998 Richard Huveneers <richard@hekkihek.hacom.nl>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BUFSIZE 256
+#define NMB_UNICAST 1
+#define NMB_BROADCAST 2
+
+struct SMBDOMAIN {
+ char *name; /* domain name */
+ char *sname; /* match this with user input */
+ char *passthrough; /* pass-through authentication */
+ char *nmbaddr; /* name service address */
+ int nmbcast; /* broadcast or unicast */
+ char *authshare; /* share name of auth file */
+ char *authfile; /* pathname of auth file */
+ struct SMBDOMAIN *next; /* linked list */
+};
+
+struct SMBDOMAIN *firstdom = NULL;
+struct SMBDOMAIN *lastdom = NULL;
+
+/*
+ * escape the backslash character, since it has a special meaning
+ * to the read command of the bourne shell.
+ */
+
+void
+print_esc(FILE * p, char *s)
+{
+ char buf[256];
+ char *t;
+ int i = 0;
+
+ for (t = s; *t != '\0'; t++) {
+ if (i > 250) {
+ buf[i] = '\0';
+ (void) fputs(buf, p);
+ i = 0;
+ }
+ if (*t == '\\')
+ buf[i++] = '\\';
+
+ buf[i++] = *t;
+ }
+
+ if (i > 0) {
+ buf[i] = '\0';
+ (void) fputs(buf, p);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ char buf[BUFSIZE];
+ struct SMBDOMAIN *dom;
+ char *s;
+ char *user;
+ char *pass;
+ char *domname;
+ FILE *p;
+ int debug = 0;
+ char *shcmd;
+
+ /* make standard output line buffered */
+ if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
+ return 1;
+
+ /* parse command line arguments */
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-d") == 0) {
+ debug = 1;
+ continue;
+ }
+ /* the next options require an argument */
+ if (i + 1 == argc)
+ break;
+
+ if (strcmp(argv[i], "-W") == 0) {
+ if ((dom = (struct SMBDOMAIN *) malloc(sizeof(struct SMBDOMAIN))) == NULL)
+ return 1;
+
+ dom->name = dom->sname = argv[++i];
+ dom->passthrough = "";
+ dom->nmbaddr = "";
+ dom->nmbcast = NMB_BROADCAST;
+ dom->authshare = "NETLOGON";
+ dom->authfile = "proxyauth";
+ dom->next = NULL;
+
+ /* append to linked list */
+ if (lastdom != NULL)
+ lastdom->next = dom;
+ else
+ firstdom = dom;
+
+ lastdom = dom;
+ continue;
+ }
+ if (strcmp(argv[i], "-w") == 0) {
+ if (lastdom != NULL)
+ lastdom->sname = argv[++i];
+ continue;
+ }
+ if (strcmp(argv[i], "-P") == 0) {
+ if (lastdom != NULL)
+ lastdom->passthrough = argv[++i];
+ continue;
+ }
+ if (strcmp(argv[i], "-B") == 0) {
+ if (lastdom != NULL) {
+ lastdom->nmbaddr = argv[++i];
+ lastdom->nmbcast = NMB_BROADCAST;
+ }
+ continue;
+ }
+ if (strcmp(argv[i], "-U") == 0) {
+ if (lastdom != NULL) {
+ lastdom->nmbaddr = argv[++i];
+ lastdom->nmbcast = NMB_UNICAST;
+ }
+ continue;
+ }
+ if (strcmp(argv[i], "-S") == 0) {
+ if (lastdom != NULL) {
+ if ((lastdom->authshare = strdup(argv[++i])) == NULL)
+ return 1;
+
+ /* convert backslashes to forward slashes */
+ for (s = lastdom->authshare; *s != '\0'; s++)
+ if (*s == '\\')
+ *s = '/';
+
+ /* strip leading forward slash from share name */
+ if (*lastdom->authshare == '/')
+ lastdom->authshare++;
+
+ if ((s = strchr(lastdom->authshare, '/')) != NULL) {
+ *s = '\0';
+ lastdom->authfile = s + 1;
+ }
+ }
+ continue;
+ }
+ }
+
+ shcmd = debug ? HELPERSCRIPT : HELPERSCRIPT " > /dev/null 2>&1";
+
+ /* pass to helper script */
+ if (putenv("SAMBAPREFIX=" SAMBAPREFIX) != 0)
+ return 1;
+
+ while (1) {
+ if (fgets(buf, BUFSIZE, stdin) == NULL)
+ break;
+
+ if ((s = strchr(buf, '\n')) == NULL)
+ continue;
+ *s = '\0';
+
+ if ((s = strchr(buf, ' ')) == NULL) {
+ (void) printf("ERR\n");
+ continue;
+ }
+ *s = '\0';
+
+ user = buf;
+ pass = s + 1;
+ domname = NULL;
+
+ if ((s = strchr(user, '\\')) != NULL) {
+ *s = '\0';
+ domname = user;
+ user = s + 1;
+ }
+ /* match domname with linked list */
+ if (domname != NULL && strlen(domname) > 0) {
+ for (dom = firstdom; dom != NULL; dom = dom->next)
+ if (strcasecmp(dom->sname, domname) == 0)
+ break;
+ } else
+ dom = firstdom;
+
+ if (dom == NULL) {
+ (void) printf("ERR\n");
+ continue;
+ }
+ if ((p = popen(shcmd, "w")) == NULL) {
+ (void) printf("ERR\n");
+ continue;
+ }
+ (void) fprintf(p, "%s\n", dom->name);
+ (void) fprintf(p, "%s\n", dom->passthrough);
+ (void) fprintf(p, "%s\n", dom->nmbaddr);
+ (void) fprintf(p, "%d\n", dom->nmbcast);
+ (void) fprintf(p, "%s\n", dom->authshare);
+ (void) fprintf(p, "%s\n", dom->authfile);
+ (void) fprintf(p, "%s\n", user);
+ /* the password can contain special characters */
+ print_esc(p, pass);
+ (void) fputc('\n', p);
+ (void) fflush(p);
+
+ if (pclose(p) == 0)
+ (void) printf("OK\n");
+ else
+ (void) printf("ERR\n");
+
+ } /* while (1) */
+ return 0;
+}
--- /dev/null
+#!/bin/sh
+#
+# smb_auth - SMB proxy authentication module
+# Copyright (C) 1998 Richard Huveneers <richard@hekkihek.hacom.nl>
+#
+# 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.
+
+read DOMAINNAME
+read PASSTHROUGH
+read NMBADDR
+read NMBCAST
+read AUTHSHARE
+read AUTHFILE
+read SMBUSER
+read SMBPASS
+
+# Find domain controller
+echo "Domain name: $DOMAINNAME"
+if [ -n "$PASSTHROUGH" ]
+then
+ echo "Pass-through authentication: yes: $PASSTHROUGH"
+else
+ echo "Pass-through authentication: no"
+ PASSTHROUGH="$DOMAINNAME"
+fi
+if [ -n "$NMBADDR" ]
+then
+ if [ "$NMBCAST" = "1" ]
+ then
+ addropt="-U $NMBADDR -R"
+ else
+ addropt="-B $NMBADDR"
+ fi
+else
+ addropt=""
+fi
+echo "Query address options: $addropt"
+dcip=`$SAMBAPREFIX/bin/nmblookup $addropt "$PASSTHROUGH#1c" | awk '/^[0-9.]+ / { print $1 ; exit }'`
+echo "Domain controller IP address: $dcip"
+[ -n "$dcip" ] || exit 1
+
+# All right, we have the IP address of a domain controller,
+# but we need its name too
+dcname=`$SAMBAPREFIX/bin/nmblookup -A $dcip | awk '$2 == "<00>" { print $1 ; exit }'`
+echo "Domain controller NETBIOS name: $dcname"
+[ -n "$dcname" ] || exit 1
+
+# Pass password to smbclient through environment. Not really safe.
+USER="$SMBUSER%$SMBPASS"
+export USER
+
+# Read the contents of the file $AUTHFILE on the $AUTHSHARE share
+authfilebs=`echo "$AUTHFILE" | tr / '\\\\'`
+authinfo=`$SAMBAPREFIX/bin/smbclient "//$dcname/$AUTHSHARE" -I $dcip -d 0 -E -W "$DOMAINNAME" -c "get $authfilebs -" 2>/dev/null`
+echo "Contents of //$dcname/$AUTHSHARE/$AUTHFILE: $authinfo"
+
+# Allow for both \n and \r\n end-of-line termination
+[ "$authinfo" = "allow" -o "$authinfo" = "allow\r" ] || exit 1
+exit 0
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:46 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+cgi_suffix = @cgi_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+YP_AUTH_EXE = yp_auth$(exec_suffix)
+
+DEFAULT_PASSWD_FILE = $(sysconfdir)/passwd
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755
+RANLIB = @RANLIB@
+LN_S = @LN_S@
+PERL = @PERL@
+CRYPTLIB = @CRYPTLIB@
+REGEXLIB = @REGEXLIB@
+PTHREADLIB = @PTHREADLIB@
+SNMPLIB = @SNMPLIB@
+MALLOCLIB = @LIB_MALLOC@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(YP_AUTH_EXE)
+OBJS = yp_auth.o nis_support.o
+
+all: $(YP_AUTH_EXE)
+
+$(OBJS): $(top_srcdir)/include/version.h
+
+$(YP_AUTH_EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+# Michael Lupp <mike@nemesis.saar.de> wants to know about additions
+# to the install target.
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+tags:
+ ctags *.[ch] ../include/*.h ../lib/*.[ch]
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+/*
+ * Written By Rabellino Sergio (rabellino@di.unito.it) For Solaris 2.x
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yp_prot.h>
+
+#define NO_YPERR 0 /* There is no error */
+
+int
+get_nis_password(char *user, char *passwd, char *nisdomain, char *nismap)
+{
+ char *val = NULL;
+ char *username = NULL;
+ int vallen, res;
+
+#ifdef DEBUG
+ printf("Domain is set to %s\n", nisdomain);
+ printf("YP Map is set to %s\n", nismap);
+#endif
+
+ /* Get NIS entry */
+ res = yp_match(nisdomain, nismap, user, strlen(user), &val, &vallen);
+
+ switch (res) {
+ case NO_YPERR:
+ username = strtok(val, ":");
+ strcpy(passwd, strtok(NULL, ":"));
+ free(val);
+ break;
+ case YPERR_YPBIND:
+ syslog(LOG_ERR, "Squid Authentication through ypbind failure: can't communicate with ypbind");
+ return 1;
+ case YPERR_KEY: /* No such key in map */
+ return 1;
+ default:
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Adapted By Rabellino Sergio (rabellino@di.unito.it) For Solaris 2.x
+ * From NCSA Authentication module
+ */
+
+#include "config.h"
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "util.h"
+#include "hash.h"
+
+int get_nis_password();
+
+
+int
+main(int argc, char **argv)
+{
+ char buf[256];
+ char nispasswd[15];
+ char *nisdomain;
+ char *nismap;
+ char *user, *passwd, *p;
+ int res;
+ setbuf(stdout, NULL);
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: yp_auth <domainname> <nis map for password>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Example yp_auth mydomain.com passwd.byname\n");
+ exit(1);
+ }
+ nisdomain = argv[1];
+ nismap = argv[2];
+
+ while (fgets(buf, 256, stdin) != NULL) {
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+
+ if ((user = strtok(buf, " ")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ if ((passwd = strtok(NULL, "")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ res = get_nis_password(user, nispasswd, nisdomain, nismap);
+
+ if (res) {
+ /* User does not exist */
+ printf("ERR\n");
+ } else if (strcmp(nispasswd, (char *) crypt(passwd, nispasswd))) {
+ /* Password incorrect */
+ printf("ERR\n");
+ } else {
+ /* All ok !, thanks... */
+ printf("OK\n");
+ }
+ }
+ exit(0);
+}
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:47 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+GETPWNAM_AUTH_EXE = getpwnam_auth$(exec_suffix)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+CRYPTLIB = @CRYPTLIB@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(GETPWNAM_AUTH_EXE)
+OBJS = getpwnam_auth.o
+
+all: $(GETPWNAM_AUTH_EXE)
+
+$(OBJS): $(top_srcdir)/include/version.h
+
+$(GETPWNAM_AUTH_EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+/*
+ * getpwnam_auth.c
+ *
+ * AUTHOR: Erik Hofman <erik.hofman@a1.nl>
+ * Robin Elfrink <robin@a1.nl>
+ *
+ * Example authentication program for Squid, based on the
+ * original proxy_auth code from client_side.c, written by
+ * Jon Thackray <jrmt@uk.gdscorp.com>.
+ *
+ * Uses getpwnam() routines for authentication.
+ * This has the following advantages over the NCSA module:
+ *
+ * - Allow authentication of all know local users
+ * - Allows authentication through nsswitch.conf
+ * + can handle NIS(+) requests
+ * + can handle LDAP request
+ * + can handle PAM request
+ *
+ */
+
+#include "config.h"
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+
+#define ERR "ERR\n"
+#define OK "OK\n"
+
+int
+main()
+{
+ char buf[256];
+ struct passwd *pwd;
+ char *user, *passwd, *p;
+
+ setbuf(stdout, NULL);
+ while (fgets(buf, 256, stdin) != NULL) {
+
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+
+ if ((user = strtok(buf, " ")) == NULL) {
+ printf(ERR);
+ continue;
+ }
+ if ((passwd = strtok(NULL, "")) == NULL) {
+ printf(ERR);
+ continue;
+ }
+ pwd = getpwnam(user);
+ if (pwd == NULL) {
+ printf(ERR);
+ } else {
+ if (strcmp(pwd->pw_passwd, (char *) crypt(passwd, pwd->pw_passwd))) {
+ printf(ERR);
+ } else {
+ printf(OK);
+ }
+ }
+ }
+ exit(0);
+}
--- /dev/null
+
+From: "Chemolli Francesco (USI)" <ChemolliF@GruppoCredit.it>
+Subject: Multiple NT domains authenticator
+Date: Fri, 7 Jul 2000 15:37:32 +0200
+
+This is the multi-domain NTLM authenticator, blissfully undocumented
+(but there's a few strategic comments, so that at least the user
+is not left alone).
+
+The user is expected to enter his/her credentials as domain\username
+or domain/username (in analogy to what M$-Proxy does).
+
+Requires Authen::SMB from CPAN and Samba if you need to perform netbios
+queries.
+
+ Francesco 'Kinkie' Chemolli
+
--- /dev/null
+#!/usr/bin/perl
+
+#if you define this, debugging output will be printed to STDERR.
+$debug=1;
+
+#to force using some DC for some domains, fill in this hash.
+#the key is a regexp matched against the domain name
+# the value is an array ref with PDC and BDC.
+# the order the names are matched in is UNDEFINED.
+#i.e.:
+# %controllers = ( "domain" => ["pdc","bdc"]);
+
+#%controllers = ( ".*" => ["tlc5",undef]);
+
+#define this if you wish to use a WINS server. If undefined, broadcast
+# will be attempted.
+$wins_server="c0wins";
+
+
+# Some servers (at least mine) really really want to be called by address.
+# If this variable is defined, we'll ask nmblookup to do a reverse DNS on the
+# DC addresses. It might fail though, for instance because you have a crappy
+# DNS with no reverse zones or records. If it doesn't work, you'll have to
+# fall back to the %controllers hack.
+$try_reverse_dns=1;
+
+# Soem servers (at least mine) don't like to be called by their fully
+# qualified name. define this if you wish to call them ONLY by their
+# hostname.
+$dont_use_fqdn=1;
+
+#no more user-serviceable parts
+use Authen::Smb;
+
+#variables:
+# %pdc used to cache the domain -> pdc_ip values. IT NEVER EXPIRES!
+
+
+while (<>) {
+ if (! m;([^\\]+)(\\|/)(\S+)\s(.*); ) { #parse the line
+ print "ERR\n";
+ next;
+ }
+ $domain=$1;
+ $user=$3;
+ $pass=$4;
+ print STDERR "domain: $domain, user: $user, pass=$pass\n"
+ if (defined ($debug));
+ # check out that we know the PDC address
+ if (!$pdc{$domain}) {
+ ($pdc,$bdc)=&discover_dc($domain);
+ if ($pdc) {
+ $pdc{$domain}=$pdc;
+ $bdc{$domain}=$bdc;
+ }
+ }
+ $pdc=$pdc{$domain};
+ $bdc=$bdc{$domain};
+ if (!$pdc) {
+ #a pdc was not found
+ print "ERR\n";
+ print STDERR "No PDC found\n" if (defined($debug));
+ next;
+ }
+
+ print STDERR "querying '$pdc' and '$bdc' for user '$domain\\$user', ".
+ "pass $pass\n" if (defined($debug));
+ $result=Authen::Smb::authen($user,$pass,$pdc,$bdc,$domain);
+ print STDERR "result is: $nt_results{$result} ($result)\n"
+ if (defined($debug));
+ if ($result == NTV_NO_ERROR) {
+ print STDERR ("OK for user '$domain\\$user'\n") if (defined($debug));
+ print ("OK\n");
+ } else {
+ print STDERR "Could not authenticate user '$domain\\$user'\n";
+ print ("ERR\n");
+ }
+}
+
+#why do Microsoft servers have to be so damn picky and convoluted?
+sub discover_dc {
+ my $domain = shift @_;
+ my ($pdc, $bdc, $lookupstring, $datum);
+
+ foreach (keys %controllers) {
+ if ($domain =~ /$_/) {
+ print STDERR "DCs forced by user: $_ => ".
+ join(',',@{$controllers{$_}}).
+ "\n" if (defined($debug));
+ return @{$controllers{$_}};
+ }
+ }
+ $lookupstring="nmblookup";
+ $lookupstring.=" -R -U $wins_server" if (defined($wins_server));
+ $lookupstring.=" -T" if (defined($try_reverse_dns));
+ $lookupstring.=" '$domain#1c'";
+ print STDERR "Discovering PDC: $lookupstring\n"
+ if (defined($debug));
+ #discover the PDC address
+ open(PDC,"$lookupstring|");
+ while (<PDC>) {
+ print STDERR "response line: $_" if (defined($debug));
+ if (m|(.*), (\d+\.\d+\.\d+\.\d+)|) {
+ $datum=$1;
+ print STDERR "matched $datum\n" if (defined($debug));
+ if (defined($dont_use_fqdn) && $datum =~ /^([^.]+)\..*/) {
+ $datum=$1;
+ print STDERR "stripped domain name: $datum\n" if (defined($debug));
+ }
+ } elsif (m|^(\d+\.\d+\.\d+\.\d+)|) {
+ $datum=$1;
+ } else {
+ #no data here, go to next line
+ next;
+ }
+ if ($datum) {
+ if ($pdc) {
+ $bdc=$datum;
+ print STDERR "BDC is $datum\n" if (defined($debug));
+ last;
+ } else {
+ $pdc=$datum;
+ print STDERR "PDC is $datum\n" if (defined($debug));
+ }
+ last;
+ }
+ }
+ close(PDC);
+ return ($pdc,$bdc) if ($pdc);
+ return 0;
+}
+
--- /dev/null
+# Makefile for storage modules in the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:48 hno Exp $
+#
+
+# The 'nop' is in the SUBDIRS list because some Unixes that can't
+# handle empty for lists.
+
+SUBDIRS = @NTLM_AUTH_HELPERS@ nop
+
+all:
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) all" || exit 1; \
+ fi; \
+ done;
+
+clean:
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) clean"; \
+ fi; \
+ done
+
+distclean:
+ -rm -f Makefile
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) distclean"; \
+ fi; \
+ done
+
+.DEFAULT:
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $@" || exit 1; \
+ fi; \
+ done;
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:48 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+NTLM_AUTH_EXE = ntlm_auth$(exec_suffix)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+CRYPTLIB = @CRYPTLIB@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(srcdir)/smbval -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lntlmauth -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(NTLM_AUTH_EXE)
+OBJS = ntlm_auth.o libntlmssp.o
+
+all: $(NTLM_AUTH_EXE) smbval/smbvalid.a
+
+$(OBJS): $(top_srcdir)/include/version.h ntlm.h
+
+$(NTLM_AUTH_EXE): $(OBJS) smbval/smbvalid.a
+ $(CC) $(LDFLAGS) $(OBJS) smbval/smbvalid.a -o $@ $(AUTH_LIBS)
+
+smbval/smbvalid.a: smbval/stamp
+
+smbval smbval/stamp:
+ @sh -c "cd smbval && $(MAKE) all"
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+ cd smbval; make clean
+
+distclean: clean
+ -rm -f Makefile
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+/*
+ * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ */
+
+
+#include "ntlm.h"
+#include "util.h" /* from Squid */
+#include "valid.h"
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "smblib-priv.h" /* for SMB_Handle_Type */
+
+/* a few forward-declarations. Hackish, but I don't care right now */
+SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle,
+ char *server, char *NTdomain);
+
+/* this one is reallllly haackiish. We really should be using anything from smblib-priv.h
+ */
+static char *SMB_Prots[] =
+{"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "DOS LANMAN1.0",
+ "LANMAN1.0",
+ "DOS LM1.2X002",
+ "LM1.2X002",
+ "DOS LANMAN2.1",
+ "LANMAN2.1",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+
+#if 0
+int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle);
+int SMB_Negotiate(void *Con_Handle, char *Prots[]);
+int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
+ char *PassWord, char *Domain, int precrypted);
+#endif
+
+#ifdef DEBUG
+#define debug_dump_ntlmssp_flags dump_ntlmssp_flags
+#else /* DEBUG */
+#define debug_dump_ntlmssp_flags(X) /* empty */
+#endif /* DEBUG */
+
+
+static char challenge[NONCE_LEN];
+SMB_Handle_Type handle = NULL;
+
+/* Disconnects from the DC. A reconnection will be done upon the next request
+ */
+void
+dc_disconnect()
+{
+ if (handle != NULL)
+ SMB_Discon(handle, 0);
+ handle = NULL;
+}
+
+int
+connectedp()
+{
+ return (handle != NULL);
+}
+
+
+/* Tries to connect to a DC. Returns 0 on failure, 1 on OK */
+int
+is_dc_ok(char *domain,
+ char *domain_controller)
+{
+ SMB_Handle_Type h = SMB_Connect_Server(NULL, domain_controller, domain);
+ if (h == NULL)
+ return 0;
+ SMB_Discon(h, 0);
+ return 1;
+}
+
+
+/* returns 0 on success, > 0 on failure */
+static int
+init_challenge(char *domain, char *domain_controller)
+{
+ int smberr;
+ char errstr[100];
+
+ if (handle != NULL) {
+ return 0;
+ }
+ debug("Connecting to server %s domain %s\n", domain_controller, domain);
+ handle = SMB_Connect_Server(NULL, domain_controller, domain);
+ smberr = SMB_Get_Last_Error();
+ SMB_Get_Error_Msg(smberr, errstr, 100);
+
+
+ if (handle == NULL) { /* couldn't connect */
+ debug("Couldn't connect to SMB Server. Error:%s\n", errstr);
+ return 1;
+ }
+ if (SMB_Negotiate(handle, SMB_Prots) < 0) { /* An error */
+ debug("Error negotiating protocol with SMB Server\n");
+ SMB_Discon(handle, 0);
+ handle = NULL;
+ return 2;
+ }
+ if (handle->Security == 0) { /* share-level security, unuseable */
+ debug("SMB Server uses share-level security .. we need user sercurity.\n");
+ SMB_Discon(handle, 0);
+ handle = NULL;
+ return 3;
+ }
+ memcpy(challenge, handle->Encrypt_Key, NONCE_LEN);
+ return 0;
+}
+
+const char *
+make_challenge(char *domain, char *domain_controller)
+{
+ if (init_challenge(domain, domain_controller) > 0)
+ return NULL;
+ return ntlm_make_challenge(domain, domain_controller, challenge,
+ NONCE_LEN);
+}
+
+#define min(A,B) (A<B?A:B)
+/* returns NULL on failure, or a pointer to
+ * the user's credentials (domain\\username)
+ * upon success. WARNING. It's pointing to static storage.
+ * In case of problem sets as side-effect ntlm_errno to one of the
+ * codes defined in ntlm.h
+ */
+int ntlm_errno;
+static char credentials[1024]; /* we can afford to waste */
+char *
+ntlm_check_auth(ntlm_authenticate * auth, int auth_length)
+{
+ int rv, retries = 0;
+ char pass[25];
+ char *domain = credentials;
+ char *user;
+ lstring tmp;
+
+ if (handle == NULL) { /*if null we aren't connected, but it shouldn't happen */
+ debug("Weird, we've been disconnected\n");
+ ntlm_errno = NTLM_NOT_CONNECTED;
+ return NULL;
+ }
+ /* Authenticating against the NT response doesn't seem to work... */
+ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->lmresponse);
+ if (tmp.str == NULL) {
+ fprintf(stderr, "No auth at all. Returning no-auth\n");
+ ntlm_errno = NTLM_LOGON_ERROR;
+ return NULL;
+ }
+ memcpy(pass, tmp.str, tmp.l);
+ pass[25] = '\0';
+
+/* debug("fetching domain\n"); */
+ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain);
+ if (tmp.str == NULL) {
+ debug("No domain supplied. Returning no-auth\n");
+ ntlm_errno = NTLM_LOGON_ERROR;
+ return NULL;
+ }
+ memcpy(domain, tmp.str, tmp.l);
+ user = domain + tmp.l;
+ *user++ = '\0';
+
+/* debug("fetching user name\n"); */
+ tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user);
+ if (tmp.str == NULL) {
+ debug("No username supplied. Returning no-auth\n");
+ ntlm_errno = NTLM_LOGON_ERROR;
+ return NULL;
+ }
+ memcpy(user, tmp.str, tmp.l);
+ *(user + tmp.l) = '\0';
+
+ debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass);
+
+ rv = SMB_Logon_Server(handle, user, pass, domain, 1);
+
+ while ((rv == NTLM_BAD_PROTOCOL || rv == NTLM_SERVER_ERROR)
+ && retries < BAD_DC_RETRIES_NUMBER) {
+ retries++;
+ usleep((unsigned long) 100000);
+ rv = SMB_Logon_Server(handle, user, pass, domain, 1);
+ }
+
+ debug("\tresult is %d\n", rv);
+
+ if (rv != NTV_NO_ERROR) { /* failed */
+ ntlm_errno = rv;
+ return NULL;
+ }
+ *(user - 1) = '\\';
+
+ debug("credentials: %s\n", credentials);
+ return credentials;
+}
--- /dev/null
+/*
+ * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>,
+ * inspired by previous work by Andy Doran
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ */
+
+#ifndef _NTLM_H_
+#define _NTLM_H_
+
+#include "config.h"
+#include "ntlmauth.h"
+
+/* for time_t */
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#define DEBUG
+
+/*
+ * Number of authentication attempts to perform in case of certain errors
+ */
+#define BAD_DC_RETRIES_NUMBER 3
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+
+/* Debugging stuff */
+
+#ifdef __GNUC__ /* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+#define debug(X...) fprintf(stderr,"ntlm-auth[%d](%s:%d): ", getpid(), \
+ ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\
+ __LINE__);\
+ fprintf(stderr,X)
+#else /* DEBUG */
+#define debug(X...) /* */
+#endif /* DEBUG */
+#else /* __GNUC__ */
+#define debug(char *format, ...) {} /* Too lazy to write va_args stuff */
+#endif
+
+
+/* A couple of harmless helper macros */
+#define SEND(X) debug("sending '%s' to squid\n",X); printf(X); printf("\n");
+#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X,Y);\
+ printf("\n");
+
+extern int ntlm_errno;
+#define NTLM_NO_ERROR 0
+#define NTLM_SERVER_ERROR 1
+#define NTLM_PROTOCOL_ERROR 2
+#define NTLM_LOGON_ERROR 3
+#define NTLM_BAD_PROTOCOL -1
+#define NTLM_NOT_CONNECTED 10
+
+
+const char *make_challenge(char *domain, char *controller);
+extern char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length);
+void dc_disconnect(void);
+int connectedp(void);
+int is_dc_ok(char *domain, char *domain_controller);
+
+/* flags used for dc status */
+#define DC_OK 0x0
+#define DC_DEAD 0x1
+
+typedef struct _dc dc;
+struct _dc {
+ char *domain;
+ char *controller;
+ unsigned char status;
+ dc *next;
+};
+
+
+#endif /* _NTLM_H_ */
--- /dev/null
+/*
+ * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * Warning! We MIGHT be open to buffer overflows caused by malformed headers
+ *
+ * DONE list:
+ * use hashtable to cache authentications. Yummy performance-boost, security
+ * loss should be negligible for two reasons:
+ * - if they-re using NT, there's no security to speak of anyways
+ * - it can't get worse than basic authentication.
+ * cache expiration
+ * challenge hash expiry and renewal.
+ * PDC disconnect, after X minutes of inactivity
+ *
+ * TODO list:
+ * change syntax from options-driven to args-driven, with args domain
+ * or domain[/\]server, and an arbitrary number of backup Domain Controllers
+ * we don't really need the "status" management, it's more for debugging
+ * purposes. Remove it.
+ * Maybe we can cache the created challenge, saving more time?
+ *
+ */
+
+
+#include "config.h"
+#include "ntlmauth.h"
+#include "ntlm.h"
+#include "util.h"
+
+#define BUFFER_SIZE 10240
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0;
+
+dc *controllers = NULL;
+int numcontrollers = 0;
+dc *current_dc;
+
+/* housekeeping cycle and periodic operations */
+static unsigned char need_dc_resurrection = 0;
+static void
+resurrect_dead_dc()
+{
+ int j;
+ dc *c = controllers;
+
+ need_dc_resurrection = 0;
+ for (j = 0; j < numcontrollers; j++)
+ if (c->status != DC_OK && is_dc_ok(c->domain, c->controller))
+ c->status = DC_OK;
+}
+
+/* makes a null-terminated string upper-case. Changes CONTENTS! */
+static void
+uc(char *string)
+{
+ char *p = string, c;
+ while ((c = *p)) {
+ *p = toupper(c);
+ p++;
+ }
+}
+
+/* makes a null-terminated string lower-case. Changes CONTENTS! */
+static void
+lc(char *string)
+{
+ char *p = string, c;
+ while ((c = *p)) {
+ *p = tolower(c);
+ p++;
+ }
+}
+
+/*
+ * options:
+ * -b try load-balancing the domain-controllers
+ * -f fail-over to another DC if DC connection fails.
+ * domain\controller ...
+ */
+void
+process_options(int argc, char *argv[])
+{
+ int opt, j, had_error = 0;
+ dc *new_dc = NULL, *last_dc = NULL;
+ while (-1 != (opt = getopt(argc, argv, "bf"))) {
+ switch (opt) {
+ case 'b':
+ load_balance = 1;
+ break;
+ case 'f':
+ failover_enabled = 1;
+ break;
+ default:
+ fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
+ had_error = 1;
+ }
+ }
+ if (had_error)
+ exit(1);
+ /* Okay, now begin filling controllers up */
+ /* we can avoid memcpy-ing, and just reuse argv[] */
+ for (j = optind; j < argc; j++) {
+ char *d, *c;
+ d = argv[j];
+ if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) {
+ fprintf(stderr, "Couldn't grok domain-controller %s\n", d);
+ continue;
+ }
+ *c++ = '\0';
+ new_dc = (dc *) malloc(sizeof(dc));
+ if (!new_dc) {
+ fprintf(stderr, "Malloc error while parsing DC options\n");
+ continue;
+ }
+ /* capitalize */
+ uc(c);
+ uc(d);
+ numcontrollers++;
+ new_dc->domain = d;
+ new_dc->controller = c;
+ new_dc->status = DC_OK;
+ if (controllers == NULL) { /* first controller */
+ controllers = new_dc;
+ last_dc = new_dc;
+ } else {
+ last_dc->next = new_dc; /* can't be null */
+ last_dc = new_dc;
+ }
+ }
+ if (numcontrollers == 0) {
+ fprintf(stderr, "You must specify at least one domain-controller!\n");
+ exit(1);
+ }
+ last_dc->next = controllers; /* close the queue, now it's circular */
+}
+
+/* tries connecting to the domain controllers in the "controllers" ring,
+ * with failover if the adequate option is specified.
+ */
+const char *
+obtain_challenge()
+{
+ int j = 0;
+ const char *ch;
+ for (j = 0; j < numcontrollers; j++) {
+ if (current_dc->status == DC_OK) {
+ ch = make_challenge(current_dc->domain, current_dc->controller);
+ if (ch)
+ return ch; /* All went OK, returning */
+ /* Huston, we've got a problem. Take this DC out of the loop */
+ current_dc->status = DC_DEAD;
+ need_dc_resurrection = 1;
+ }
+ if (failover_enabled == 0) /* No failover. Just return */
+ return NULL;
+ /* Try with the next */
+ current_dc = current_dc->next;
+ }
+ return NULL;
+}
+
+void
+manage_request()
+{
+ ntlmhdr *fast_header;
+ char buf[10240];
+ const char *ch;
+ char *ch2, *decoded, *cred;
+ int plen;
+
+ if (fgets(buf, BUFFER_SIZE, stdin) == NULL)
+ exit(0); /* BIIG buffer */
+ ch2 = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */
+ if (ch2) {
+ *ch2 = '\0'; /* terminate the string at newline. */
+ ch = ch2;
+ }
+ debug("ntlm authenticator. Got '%s' from Squid\n", buf);
+
+ if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */
+ /* figure out what we got */
+ decoded = base64_decode(buf + 3);
+ /* Note: we don't need to manage memory at this point, since
+ * base64_decode returns a pointer to static storage.
+ */
+
+ if (!decoded) { /* decoding failure, return error */
+ SEND("NA Packet format error, couldn't base64-decode");
+ return;
+ }
+ /* fast-track-decode request type. */
+ fast_header = (struct _ntlmhdr *) decoded;
+
+ /* sanity-check: it IS a NTLMSSP packet, isn't it? */
+ if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) {
+ SEND("NA Broken authentication packet");
+ return;
+ }
+ switch (fast_header->type) {
+ case NTLM_NEGOTIATE:
+ SEND("NA Invalid negotiation request received");
+ return;
+ /* notreached */
+ case NTLM_CHALLENGE:
+ SEND("NA Got a challenge. We refuse to have our authority disputed");
+ return;
+ /* notreached */
+ case NTLM_AUTHENTICATE:
+ /* check against the DC */
+ plen = strlen(buf) * 3 / 4; /* we only need it here. Optimization */
+ cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
+ if (cred == NULL) {
+ switch (ntlm_errno) {
+ case NTLM_LOGON_ERROR:
+ SEND("NA authentication failure");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ case NTLM_SERVER_ERROR:
+ SEND("BH Domain Controller Error");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ case NTLM_PROTOCOL_ERROR:
+ SEND("BH Domain Controller communication error");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ case NTLM_NOT_CONNECTED:
+ SEND("BH Domain Controller (or network) died on us");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ case NTLM_BAD_PROTOCOL:
+ SEND("BH Domain controller failure");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ default:
+ SEND("BH Unhandled error while talking to Domain Controller");
+ dc_disconnect();
+ current_dc = current_dc->next;
+ return;
+ }
+ }
+ lc(cred); /* let's lowercase them for our convenience */
+ SEND2("AF %s", cred);
+ return;
+ default:
+ SEND("BH unknown authentication packet type");
+ return;
+ }
+
+
+ return;
+ }
+ if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */
+ dc_disconnect();
+ ch = obtain_challenge();
+ while (ch == NULL) {
+ sleep(30);
+ ch = obtain_challenge();
+ }
+ SEND2("TT %s", ch);
+ if (need_dc_resurrection) /* looks like a good moment... */
+ resurrect_dead_dc();
+ return;
+ }
+ SEND("BH Helper detected protocol error");
+ return;
+/********* END ********/
+
+
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ debug("starting up...\n");
+
+ process_options(argc, argv);
+
+ debug("options processed OK\n");
+
+ /* initialize FDescs */
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ /* select the first domain controller we're going to use */
+ current_dc = controllers;
+ if (load_balance != 0 && numcontrollers > 1) {
+ int n;
+ pid_t pid = getpid();
+ n = pid % numcontrollers;
+ debug("load balancing. Selected controller #%d\n", n);
+ while (n > 0) {
+ current_dc = current_dc->next;
+ n--;
+ }
+ }
+ while (1) {
+ debug("managing request\n");
+ manage_request();
+ }
+ return 0;
+}
--- /dev/null
+# makefile for smblib
+# Type make system, where system is ULTRIX, DU, DECOSF1, Solaris etc
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+INSTALL_FILE = @INSTALL_DATA@
+RANLIB = @RANLIB@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+RM = @RM@
+AR_R = @AR_R@
+
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+# CFLAGS = -fpic -g
+
+INCLUDE = -I. -I../../../../../../include -I$(top_srcdir)/include
+INCLUDES = smblib.h smblib-priv.h
+
+#RFCNB = session.o rfcnb-util.o rfcnb-io.o
+
+#OBJS = smblib.o smblib-util.o file.o smb-errors.o exper.o smblib-api.o smbencrypt.o smbdes.o md4.o
+
+VALIDATE = valid.o session.o rfcnb-util.o \
+ rfcnb-io.o smblib-util.o smblib.o smbencrypt.o smbdes.o md4.o
+
+#.SUFFIXES: .c .o .h
+
+dummy: all
+
+smbvalid.a: $(VALIDATE)
+ $(RM) -f $@
+ $(AR_R) $@ $(VALIDATE)
+ $(RANLIB) $@
+
+all: smbvalid.a
+
+#.c.o: $(INCLUDES)
+
+clean:
+ $(RM) -f *.o smbvalid.a *~
+
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * SMB Byte handling
+ * Copyright (C) Andrew Tridgell 1992-1995
+ *
+ * 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.
+ */
+
+/*
+ * This file implements macros for machine independent short and
+ * int manipulation
+ */
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right"
+ * byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8)
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+#else
+/* this handles things for architectures like the 386 that can handle
+ * alignment errors */
+/*
+ * WARNING: This section is dependent on the length of int16 and int32
+ * being correct
+ */
+#define SVAL(buf,pos) (*(uint16 *)((char *)(buf) + (pos)))
+#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos)))
+#define SVALS(buf,pos) (*(int16 *)((char *)(buf) + (pos)))
+#define IVALS(buf,pos) (*(int32 *)((char *)(buf) + (pos)))
+#define SSVAL(buf,pos,val) SVAL(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS(buf,pos)=((int32)(val))
+#endif
+
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * a implementation of MD4 designed for use in the SMB authentication protocol
+ * Copyright (C) Andrew Tridgell 1997
+ *
+ * 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.
+ */
+
+
+/* NOTE: This code makes no attempt to be fast!
+ *
+ * It assumes that a int is at least 32 bits long
+ */
+#include <string.h>
+
+typedef unsigned int uint32;
+
+static uint32 A, B, C, D;
+
+static uint32
+F(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X & Y) | ((~X) & Z);
+}
+
+static uint32
+G(uint32 X, uint32 Y, uint32 Z)
+{
+ return (X & Y) | (X & Z) | (Y & Z);
+}
+
+static uint32
+H(uint32 X, uint32 Y, uint32 Z)
+{
+ return X ^ Y ^ Z;
+}
+
+static uint32
+lshift(uint32 x, int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
+}
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void
+mdfour64(uint32 * M)
+{
+ int j;
+ uint32 AA, BB, CC, DD;
+ uint32 X[16];
+
+ for (j = 0; j < 16; j++)
+ X[j] = M[j];
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ ROUND1(A, B, C, D, 0, 3);
+ ROUND1(D, A, B, C, 1, 7);
+ ROUND1(C, D, A, B, 2, 11);
+ ROUND1(B, C, D, A, 3, 19);
+ ROUND1(A, B, C, D, 4, 3);
+ ROUND1(D, A, B, C, 5, 7);
+ ROUND1(C, D, A, B, 6, 11);
+ ROUND1(B, C, D, A, 7, 19);
+ ROUND1(A, B, C, D, 8, 3);
+ ROUND1(D, A, B, C, 9, 7);
+ ROUND1(C, D, A, B, 10, 11);
+ ROUND1(B, C, D, A, 11, 19);
+ ROUND1(A, B, C, D, 12, 3);
+ ROUND1(D, A, B, C, 13, 7);
+ ROUND1(C, D, A, B, 14, 11);
+ ROUND1(B, C, D, A, 15, 19);
+
+ ROUND2(A, B, C, D, 0, 3);
+ ROUND2(D, A, B, C, 4, 5);
+ ROUND2(C, D, A, B, 8, 9);
+ ROUND2(B, C, D, A, 12, 13);
+ ROUND2(A, B, C, D, 1, 3);
+ ROUND2(D, A, B, C, 5, 5);
+ ROUND2(C, D, A, B, 9, 9);
+ ROUND2(B, C, D, A, 13, 13);
+ ROUND2(A, B, C, D, 2, 3);
+ ROUND2(D, A, B, C, 6, 5);
+ ROUND2(C, D, A, B, 10, 9);
+ ROUND2(B, C, D, A, 14, 13);
+ ROUND2(A, B, C, D, 3, 3);
+ ROUND2(D, A, B, C, 7, 5);
+ ROUND2(C, D, A, B, 11, 9);
+ ROUND2(B, C, D, A, 15, 13);
+
+ ROUND3(A, B, C, D, 0, 3);
+ ROUND3(D, A, B, C, 8, 9);
+ ROUND3(C, D, A, B, 4, 11);
+ ROUND3(B, C, D, A, 12, 15);
+ ROUND3(A, B, C, D, 2, 3);
+ ROUND3(D, A, B, C, 10, 9);
+ ROUND3(C, D, A, B, 6, 11);
+ ROUND3(B, C, D, A, 14, 15);
+ ROUND3(A, B, C, D, 1, 3);
+ ROUND3(D, A, B, C, 9, 9);
+ ROUND3(C, D, A, B, 5, 11);
+ ROUND3(B, C, D, A, 13, 15);
+ ROUND3(A, B, C, D, 3, 3);
+ ROUND3(D, A, B, C, 11, 9);
+ ROUND3(C, D, A, B, 7, 11);
+ ROUND3(B, C, D, A, 15, 15);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+
+ A &= 0xFFFFFFFF;
+ B &= 0xFFFFFFFF;
+ C &= 0xFFFFFFFF;
+ D &= 0xFFFFFFFF;
+
+ for (j = 0; j < 16; j++)
+ X[j] = 0;
+}
+
+static void
+copy64(uint32 * M, unsigned char *in)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) |
+ (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0);
+}
+
+static void
+copy4(unsigned char *out, uint32 x)
+{
+ out[0] = x & 0xFF;
+ out[1] = (x >> 8) & 0xFF;
+ out[2] = (x >> 16) & 0xFF;
+ out[3] = (x >> 24) & 0xFF;
+}
+
+/* produce a md4 message digest from data of length n bytes */
+void
+mdfour(unsigned char *out, unsigned char *in, int n)
+{
+ unsigned char buf[128];
+ uint32 M[16];
+ uint32 b = n * 8;
+ int i;
+
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+
+ while (n > 64) {
+ copy64(M, in);
+ mdfour64(M);
+ in += 64;
+ n -= 64;
+ }
+
+ for (i = 0; i < 128; i++)
+ buf[i] = 0;
+ memcpy(buf, in, n);
+ buf[n] = 0x80;
+
+ if (n <= 55) {
+ copy4(buf + 56, b);
+ copy64(M, buf);
+ mdfour64(M);
+ } else {
+ copy4(buf + 120, b);
+ copy64(M, buf);
+ mdfour64(M);
+ copy64(M, buf + 64);
+ mdfour64(M);
+ }
+
+ for (i = 0; i < 128; i++)
+ buf[i] = 0;
+ copy64(M, buf);
+
+ copy4(out, A);
+ copy4(out + 4, B);
+ copy4(out + 8, C);
+ copy4(out + 12, D);
+
+ A = B = C = D = 0;
+}
--- /dev/null
+void mdfour(unsigned char *out, unsigned char *in, int n);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Common Structures etc Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* A data structure we need */
+
+typedef struct RFCNB_Pkt {
+
+ char *data; /* The data in this portion */
+ int len;
+ struct RFCNB_Pkt *next;
+
+} RFCNB_Pkt;
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Error Response Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* Error responses */
+
+#define RFCNBE_Bad -1 /* Bad response */
+#define RFCNBE_OK 0
+
+/* these should follow the spec ... is there one ? */
+
+#define RFCNBE_NoSpace 1 /* Could not allocate space for a struct */
+#define RFCNBE_BadName 2 /* Could not translate a name */
+#define RFCNBE_BadRead 3 /* Read sys call failed */
+#define RFCNBE_BadWrite 4 /* Write Sys call failed */
+#define RFCNBE_ProtErr 5 /* Protocol Error */
+#define RFCNBE_ConGone 6 /* Connection dropped */
+#define RFCNBE_BadHandle 7 /* Handle passed was bad */
+#define RFCNBE_BadSocket 8 /* Problems creating socket */
+#define RFCNBE_ConnectFailed 9 /* Connect failed */
+#define RFCNBE_CallRejNLOCN 10 /* Call rejected, not listening on CN */
+#define RFCNBE_CallRejNLFCN 11 /* Call rejected, not listening for CN */
+#define RFCNBE_CallRejCNNP 12 /* Call rejected, called name not present */
+#define RFCNBE_CallRejInfRes 13 /* Call rejetced, name ok, no resources */
+#define RFCNBE_CallRejUnSpec 14 /* Call rejected, unspecified error */
+#define RFCNBE_BadParam 15 /* Bad parameters passed ... */
+#define RFCNBE_Timeout 16 /* IO Timed out */
+
+/* Text strings for the error responses */
+extern char *RFCNB_Error_Strings[];
+/*
+ * static char *RFCNB_Error_Strings[] = {
+ *
+ * "RFCNBE_OK: Routine completed successfully.",
+ * "RFCNBE_NoSpace: No space available for a malloc call.",
+ * "RFCNBE_BadName: NetBIOS name could not be translated to IP address.",
+ * "RFCNBE_BadRead: Read system call returned an error. Check errno.",
+ * "RFCNBE_BadWrite: Write system call returned an error. Check errno.",
+ * "RFCNBE_ProtErr: A protocol error has occurred.",
+ * "RFCNBE_ConGone: Connection dropped during a read or write system call.",
+ * "RFCNBE_BadHandle: Bad connection handle passed.",
+ * "RFCNBE_BadSocket: Problems creating socket.",
+ * "RFCNBE_ConnectFailed: Connection failed. See errno.",
+ * "RFCNBE_CallRejNLOCN: Call rejected. Not listening on called name.",
+ * "RFCNBE_CallRejNLFCN: Call rejected. Not listening for called name.",
+ * "RFCNBE_CallRejCNNP: Call rejected. Called name not present.",
+ * "RFCNBE_CallRejInfRes: Call rejected. Name present, but insufficient resources.",
+ * "RFCNBE_CallRejUnSpec: Call rejected. Unspecified error.",
+ * "RFCNBE_BadParam: Bad parameters passed to a routine.",
+ * "RFCNBE_Timeout: IO Operation timed out ..."
+ *
+ * };
+ */
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NEtBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB IO Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 <features.h> */
+#include "config.h"
+#include "std-includes.h"
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+#include <sys/uio.h>
+#include <sys/signal.h>
+#include <string.h>
+
+int RFCNB_Timeout = 0; /* Timeout in seconds ... */
+
+void
+rfcnb_alarm(int sig)
+{
+
+ fprintf(stderr, "IO Timed out ...\n");
+
+}
+
+/* Set timeout value and setup signal handling */
+
+int
+RFCNB_Set_Timeout(int seconds)
+{
+ /* If we are on a Bezerkeley system, use sigvec, else sigaction */
+#if HAVE_SIGACTION
+ struct sigaction inact, outact;
+#else
+ struct sigvec invec, outvec;
+#endif
+
+ RFCNB_Timeout = seconds;
+
+ if (RFCNB_Timeout > 0) { /* Set up handler to ignore but not restart */
+
+#if HAVE_SIGACTION
+ inact.sa_handler = (void (*)()) rfcnb_alarm;
+ sigemptyset(&inact.sa_mask);
+ inact.sa_flags = 0; /* Don't restart */
+
+ if (sigaction(SIGALRM, &inact, &outact) < 0)
+ return (-1);
+#else
+ invec.sv_handler = (void (*)()) rfcnb_alarm;
+ invec.sv_mask = 0;
+ invec.sv_flags = SV_INTERRUPT;
+
+ if (sigvec(SIGALRM, &invec, &outvec) < 0)
+ return (-1);
+#endif
+
+ }
+ return (0);
+
+}
+
+/* Discard the rest of an incoming packet as we do not have space for it
+ * in the buffer we allocated or were passed ... */
+
+int
+RFCNB_Discard_Rest(struct RFCNB_Con *con, int len)
+{
+ char temp[100]; /* Read into here */
+ int rest, this_read, bytes_read;
+
+ /* len is the amount we should read */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Discard_Rest called to discard: %i\n", len);
+#endif
+
+ rest = len;
+
+ while (rest > 0) {
+
+ this_read = (rest > sizeof(temp) ? sizeof(temp) : rest);
+
+ bytes_read = read(con->fd, temp, this_read);
+
+ if (bytes_read <= 0) { /* Error so return */
+
+ if (bytes_read < 0)
+ RFCNB_errno = RFCNBE_BadRead;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ rest = rest - bytes_read;
+
+ }
+
+ return (0);
+
+}
+
+
+/* Send an RFCNB packet to the connection.
+ *
+ * We just send each of the blocks linked together ...
+ *
+ * If we can, try to send it as one iovec ...
+ *
+ */
+
+int
+RFCNB_Put_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len)
+{
+ int len_sent, tot_sent, this_len;
+ struct RFCNB_Pkt *pkt_ptr;
+ char *this_data;
+ int i;
+ struct iovec io_list[10]; /* We should never have more */
+ /* If we do, this will blow up ... */
+
+ /* Try to send the data ... We only send as many bytes as len claims */
+ /* We should try to stuff it into an IOVEC and send as one write */
+
+
+ pkt_ptr = pkt;
+ len_sent = tot_sent = 0; /* Nothing sent so far */
+ i = 0;
+
+ while ((pkt_ptr != NULL) & (i < 10)) { /* Watch that magic number! */
+
+ this_len = pkt_ptr->len;
+ this_data = pkt_ptr->data;
+ if ((tot_sent + this_len) > len)
+ this_len = len - tot_sent; /* Adjust so we don't send too much */
+
+ /* Now plug into the iovec ... */
+
+ io_list[i].iov_len = this_len;
+ io_list[i].iov_base = this_data;
+ i++;
+
+ tot_sent += this_len;
+
+ if (tot_sent == len)
+ break; /* Let's not send too much */
+
+ pkt_ptr = pkt_ptr->next;
+
+ }
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Frags = %i, tot_sent = %i\n", i, tot_sent);
+#endif
+
+ /* Set up an alarm if timeouts are set ... */
+
+ if (RFCNB_Timeout > 0)
+ alarm(RFCNB_Timeout);
+
+ if ((len_sent = writev(con->fd, io_list, i)) < 0) { /* An error */
+
+ con->rfc_errno = errno;
+ if (errno == EINTR) /* We were interrupted ... */
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadWrite;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ if (len_sent < tot_sent) { /* Less than we wanted */
+ if (errno == EINTR) /* We were interrupted */
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadWrite;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ if (RFCNB_Timeout > 0)
+ alarm(0); /* Reset that sucker */
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Len sent = %i ...\n", len_sent);
+ RFCNB_Print_Pkt(stderr, "sent", pkt, len_sent); /* Print what send ... */
+
+#endif
+
+ return (len_sent);
+
+}
+
+/* Read an RFCNB packet off the connection.
+ *
+ * We read the first 4 bytes, that tells us the length, then read the
+ * rest. We should implement a timeout, but we don't just yet
+ *
+ */
+
+
+int
+RFCNB_Get_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len)
+{
+ int read_len, pkt_len;
+ char hdr[RFCNB_Pkt_Hdr_Len]; /* Local space for the header */
+ struct RFCNB_Pkt *pkt_frag;
+ int more, this_time, offset, frag_len, this_len;
+ BOOL seen_keep_alive = TRUE;
+
+ /* Read that header straight into the buffer */
+
+ if (len < RFCNB_Pkt_Hdr_Len) { /* What a bozo */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Trying to read less than a packet:");
+ perror("");
+#endif
+ RFCNB_errno = RFCNBE_BadParam;
+ return (RFCNBE_Bad);
+
+ }
+ /* We discard keep alives here ... */
+
+ if (RFCNB_Timeout > 0)
+ alarm(RFCNB_Timeout);
+
+ while (seen_keep_alive) {
+
+ if ((read_len = read(con->fd, hdr, sizeof(hdr))) < 0) { /* Problems */
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Reading the packet, we got:");
+ perror("");
+#endif
+ if (errno == EINTR)
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_BadRead;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ /* Now we check out what we got */
+
+ if (read_len == 0) { /* Connection closed, send back eof? */
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Connection closed reading\n");
+#endif
+
+ if (errno == EINTR)
+ RFCNB_errno = RFCNBE_Timeout;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ if (RFCNB_Pkt_Type(hdr) == RFCNB_SESSION_KEEP_ALIVE) {
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "RFCNB KEEP ALIVE received\n");
+#endif
+
+ } else {
+ seen_keep_alive = FALSE;
+ }
+
+ }
+
+ /* What if we got less than or equal to a hdr size in bytes? */
+
+ if (read_len < sizeof(hdr)) { /* We got a small packet */
+
+ /* Now we need to copy the hdr portion we got into the supplied packet */
+
+ memcpy(pkt->data, hdr, read_len); /*Copy data */
+
+#ifdef RFCNB_DEBUG
+ RFCNB_Print_Pkt(stderr, "rcvd", pkt, read_len);
+#endif
+
+ return (read_len);
+
+ }
+ /* Now, if we got at least a hdr size, alloc space for rest, if we need it */
+
+ pkt_len = RFCNB_Pkt_Len(hdr);
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Reading Pkt: Length = %i\n", pkt_len);
+#endif
+
+ /* Now copy in the hdr */
+
+ memcpy(pkt->data, hdr, sizeof(hdr));
+
+ /* Get the rest of the packet ... first figure out how big our buf is? */
+ /* And make sure that we handle the fragments properly ... Sure should */
+ /* use an iovec ... */
+
+ if (len < pkt_len) /* Only get as much as we have space for */
+ more = len - RFCNB_Pkt_Hdr_Len;
+ else
+ more = pkt_len;
+
+ this_time = 0;
+
+ /* We read for each fragment ... */
+
+ if (pkt->len == read_len) { /* If this frag was exact size */
+ pkt_frag = pkt->next; /* Stick next lot in next frag */
+ offset = 0; /* then we start at 0 in next */
+ } else {
+ pkt_frag = pkt; /* Otherwise use rest of this frag */
+ offset = RFCNB_Pkt_Hdr_Len; /* Otherwise skip the header */
+ }
+
+ frag_len = pkt_frag->len;
+
+ if (more <= frag_len) /* If len left to get less than frag space */
+ this_len = more; /* Get the rest ... */
+ else
+ this_len = frag_len - offset;
+
+ while (more > 0) {
+
+ if ((this_time = read(con->fd, (pkt_frag->data) + offset, this_len)) <= 0) { /* Problems */
+
+ if (errno == EINTR) {
+
+ RFCNB_errno = RFCNB_Timeout;
+
+ } else {
+ if (this_time < 0)
+ RFCNB_errno = RFCNBE_BadRead;
+ else
+ RFCNB_errno = RFCNBE_ConGone;
+ }
+
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Frag_Len = %i, this_time = %i, this_len = %i, more = %i\n", frag_len,
+ this_time, this_len, more);
+#endif
+
+ read_len = read_len + this_time; /* How much have we read ... */
+
+ /* Now set up the next part */
+
+ if (pkt_frag->next == NULL)
+ break; /* That's it here */
+
+ pkt_frag = pkt_frag->next;
+ this_len = pkt_frag->len;
+ offset = 0;
+
+ more = more - this_time;
+
+ }
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Pkt Len = %i, read_len = %i\n", pkt_len, read_len);
+ RFCNB_Print_Pkt(stderr, "rcvd", pkt, read_len + sizeof(hdr));
+#endif
+
+ if (read_len < (pkt_len + sizeof(hdr))) { /* Discard the rest */
+
+ return (RFCNB_Discard_Rest(con, (pkt_len + sizeof(hdr)) - read_len));
+
+ }
+ if (RFCNB_Timeout > 0)
+ alarm(0); /* Reset that sucker */
+
+ return (read_len + sizeof(RFCNB_Hdr));
+}
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB IO Routines Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+int RFCNB_Put_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len);
+
+int RFCNB_Get_Pkt(struct RFCNB_Con *con, struct RFCNB_Pkt *pkt, int len);
+
+void RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt);
--- /dev/null
+#ifndef __RFCNB_H__
+#define __RFCNB_H__
+
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* Defines we need */
+
+typedef unsigned short uint16;
+
+#define GLOBAL extern
+
+#include "rfcnb-error.h"
+#include "rfcnb-common.h"
+#include "byteorder.h"
+
+#ifdef RFCNB_PORT
+#define RFCNB_Default_Port RFCNB_PORT
+#else
+#define RFCNB_Default_Port 139
+#endif
+
+#define RFCNB_MAX_STATS 1
+
+/* Protocol defines we need */
+
+#define RFCNB_SESSION_MESSAGE 0
+#define RFCNB_SESSION_REQUEST 0x81
+#define RFCNB_SESSION_ACK 0x82
+#define RFCNB_SESSION_REJ 0x83
+#define RFCNB_SESSION_RETARGET 0x84
+#define RFCNB_SESSION_KEEP_ALIVE 0x85
+
+/* Structures */
+
+typedef struct redirect_addr *redirect_ptr;
+
+struct redirect_addr {
+
+ struct in_addr ip_addr;
+ int port;
+ redirect_ptr next;
+
+};
+
+typedef struct RFCNB_Con {
+
+ int fd; /* File descripter for TCP/IP connection */
+ int rfc_errno; /* last error */
+ int timeout; /* How many milli-secs before IO times out */
+ int redirects; /* How many times we were redirected */
+ struct redirect_addr *redirect_list; /* First is first address */
+ struct redirect_addr *last_addr;
+
+} RFCNB_Con;
+
+typedef char RFCNB_Hdr[4]; /* The header is 4 bytes long with */
+ /* char[0] as the type, char[1] the */
+ /* flags, and char[2..3] the length */
+
+/* Macros to extract things from the header. These are for portability
+ * between architecture types where we are worried about byte order */
+
+#define RFCNB_Pkt_Hdr_Len 4
+#define RFCNB_Pkt_Sess_Len 72
+#define RFCNB_Pkt_Retarg_Len 10
+#define RFCNB_Pkt_Nack_Len 5
+#define RFCNB_Pkt_Type_Offset 0
+#define RFCNB_Pkt_Flags_Offset 1
+#define RFCNB_Pkt_Len_Offset 2 /* Length is 2 bytes plus a flag bit */
+#define RFCNB_Pkt_N1Len_Offset 4
+#define RFCNB_Pkt_Called_Offset 5
+#define RFCNB_Pkt_N2Len_Offset 38
+#define RFCNB_Pkt_Calling_Offset 39
+#define RFCNB_Pkt_Error_Offset 4
+#define RFCNB_Pkt_IP_Offset 4
+#define RFCNB_Pkt_Port_Offset 8
+
+/* The next macro isolates the length of a packet, including the bit in the
+ * flags */
+
+#define RFCNB_Pkt_Len(p) (PVAL(p, 3) | (PVAL(p, 2) << 8) | \
+ ((PVAL(p, RFCNB_Pkt_Flags_Offset) & 0x01) << 16))
+
+#define RFCNB_Put_Pkt_Len(p, v) (p[1] = (((v) >> 16) & 1)); \
+ (p[2] = (((v) >> 8) & 0xFF)); \
+ (p[3] = ((v) & 0xFF));
+
+#define RFCNB_Pkt_Type(p) (CVAL(p, RFCNB_Pkt_Type_Offset))
+
+/*typedef struct RFCNB_Hdr {
+ *
+ * unsigned char type;
+ * unsigned char flags;
+ * int16 len;
+ *
+ * } RFCNB_Hdr;
+ *
+ * typedef struct RFCNB_Sess_Pkt {
+ * unsigned char type;
+ * unsigned char flags;
+ * int16 length;
+ * unsigned char n1_len;
+ * char called_name[33];
+ * unsigned char n2_len;
+ * char calling_name[33];
+ * } RFCNB_Sess_Pkt;
+ *
+ *
+ * typedef struct RFCNB_Nack_Pkt {
+ *
+ * struct RFCNB_Hdr hdr;
+ * unsigned char error;
+ *
+ * } RFCNB_Nack_Pkt;
+ *
+ * typedef struct RFCNB_Retarget_Pkt {
+ *
+ * struct RFCNB_Hdr hdr;
+ * int dest_ip;
+ * unsigned char port;
+ *
+ * } RFCNB_Redir_Pkt; */
+
+/* Static variables */
+
+/* Only declare this if not defined */
+
+#ifndef RFCNB_ERRNO
+extern int RFCNB_errno;
+extern int RFCNB_saved_errno; /* Save this from point of error */
+#endif
+
+#endif /* __RFCNB_H__ */
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Utility Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 <string.h>
+#include <malloc.h>
+
+#include "std-includes.h"
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+#include <arpa/inet.h>
+
+
+extern void (*Prot_Print_Routine) (); /* Pointer to protocol print routine */
+
+/* Convert name and pad to 16 chars as needed */
+/* Name 1 is a C string with null termination, name 2 may not be */
+/* If SysName is true, then put a <00> on end, else space> */
+
+void
+RFCNB_CvtPad_Name(char *name1, char *name2)
+{
+ char c, c1, c2;
+ int i, len;
+
+ len = strlen(name1);
+
+ for (i = 0; i < 16; i++) {
+
+ if (i >= len) {
+
+ c1 = 'C';
+ c2 = 'A'; /* CA is a space */
+
+ } else {
+
+ c = name1[i];
+ c1 = (char) ((int) c / 16 + (int) 'A');
+ c2 = (char) ((int) c % 16 + (int) 'A');
+ }
+
+ name2[i * 2] = c1;
+ name2[i * 2 + 1] = c2;
+
+ }
+
+ name2[32] = 0; /* Put in the nll ... */
+
+}
+
+/* Converts an Ascii NB Name (16 chars) to an RFCNB Name (32 chars)
+ * Uses the encoding in RFC1001. Each nibble of byte is added to 'A'
+ * to produce the next byte in the name.
+ *
+ * This routine assumes that AName is 16 bytes long and that NBName has
+ * space for 32 chars, so be careful ...
+ *
+ */
+
+void
+RFCNB_AName_To_NBName(char *AName, char *NBName)
+{
+ char c, c1, c2;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+
+ c = AName[i];
+
+ c1 = (char) ((c >> 4) + 'A');
+ c2 = (char) ((c & 0xF) + 'A');
+
+ NBName[i * 2] = c1;
+ NBName[i * 2 + 1] = c2;
+ }
+
+ NBName[32] = 0; /* Put in a null */
+
+}
+
+/* Do the reverse of the above ... */
+
+void
+RFCNB_NBName_To_AName(char *NBName, char *AName)
+{
+ char c, c1, c2;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+
+ c1 = NBName[i * 2];
+ c2 = NBName[i * 2 + 1];
+
+ c = (char) (((int) c1 - (int) 'A') * 16 + ((int) c2 - (int) 'A'));
+
+ AName[i] = c;
+
+ }
+
+ AName[i] = 0; /* Put a null on the end ... */
+
+}
+
+/* Print a string of bytes in HEX etc */
+
+void
+RFCNB_Print_Hex(FILE * fd, struct RFCNB_Pkt *pkt, int Offset, int Len)
+{
+ char c1, c2, outbuf1[33];
+ unsigned char c;
+ int i, j;
+ struct RFCNB_Pkt *pkt_ptr = pkt;
+ static char Hex_List[17] = "0123456789ABCDEF";
+
+ j = 0;
+
+ /* We only want to print as much as sepcified in Len */
+
+ while (pkt_ptr != NULL) {
+
+ for (i = 0;
+ i < ((Len > (pkt_ptr->len) ? pkt_ptr->len : Len) - Offset);
+ i++) {
+
+ c = pkt_ptr->data[i + Offset];
+ c1 = Hex_List[c >> 4];
+ c2 = Hex_List[c & 0xF];
+
+ outbuf1[j++] = c1;
+ outbuf1[j++] = c2;
+
+ if (j == 32) { /* Print and reset */
+ outbuf1[j] = 0;
+ fprintf(fd, " %s\n", outbuf1);
+ j = 0;
+ }
+ }
+
+ Offset = 0;
+ Len = Len - pkt_ptr->len; /* Reduce amount by this much */
+ pkt_ptr = pkt_ptr->next;
+
+ }
+
+ /* Print last lot in the buffer ... */
+
+ if (j > 0) {
+
+ outbuf1[j] = 0;
+ fprintf(fd, " %s\n", outbuf1);
+
+ }
+ fprintf(fd, "\n");
+
+}
+
+/* Get a packet of size n */
+
+struct RFCNB_Pkt *
+RFCNB_Alloc_Pkt(int n)
+{
+ RFCNB_Pkt *pkt;
+
+ if ((pkt = (struct RFCNB_Pkt *) malloc(sizeof(struct RFCNB_Pkt))) == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ pkt->next = NULL;
+ pkt->len = n;
+
+ if (n == 0)
+ return (pkt);
+
+ if ((pkt->data = (char *) malloc(n)) == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ free(pkt);
+ return (NULL);
+
+ }
+ return (pkt);
+
+}
+
+/* Free up a packet */
+
+void
+RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt)
+{
+ struct RFCNB_Pkt *pkt_next;
+ char *data_ptr;
+
+ while (pkt != NULL) {
+
+ pkt_next = pkt->next;
+
+ data_ptr = pkt->data;
+
+ if (data_ptr != NULL)
+ free(data_ptr);
+
+ free(pkt);
+
+ pkt = pkt_next;
+
+ }
+
+}
+
+/* Print an RFCNB packet */
+
+void
+RFCNB_Print_Pkt(FILE * fd, char *dirn, struct RFCNB_Pkt *pkt, int len)
+{
+ char lname[17];
+
+ /* We assume that the first fragment is the RFCNB Header */
+ /* We should loop through the fragments printing them out */
+
+ fprintf(fd, "RFCNB Pkt %s:", dirn);
+
+ switch (RFCNB_Pkt_Type(pkt->data)) {
+
+ case RFCNB_SESSION_MESSAGE:
+
+ fprintf(fd, "SESSION MESSAGE: Length = %i\n", RFCNB_Pkt_Len(pkt->data));
+ RFCNB_Print_Hex(fd, pkt, RFCNB_Pkt_Hdr_Len,
+#ifdef RFCNB_PRINT_DATA
+ RFCNB_Pkt_Len(pkt->data) - RFCNB_Pkt_Hdr_Len);
+#else
+ 40);
+#endif
+
+ if (Prot_Print_Routine != 0) { /* Print the rest of the packet */
+
+ Prot_Print_Routine(fd, strcmp(dirn, "sent"), pkt, RFCNB_Pkt_Hdr_Len,
+ RFCNB_Pkt_Len(pkt->data) - RFCNB_Pkt_Hdr_Len);
+
+ }
+ break;
+
+ case RFCNB_SESSION_REQUEST:
+
+ fprintf(fd, "SESSION REQUEST: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+ RFCNB_NBName_To_AName((char *) (pkt->data + RFCNB_Pkt_Called_Offset), lname);
+ fprintf(fd, " Called Name: %s\n", lname);
+ RFCNB_NBName_To_AName((char *) (pkt->data + RFCNB_Pkt_Calling_Offset), lname);
+ fprintf(fd, " Calling Name: %s\n", lname);
+
+ break;
+
+ case RFCNB_SESSION_ACK:
+
+ fprintf(fd, "RFCNB SESSION ACK: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ break;
+
+ case RFCNB_SESSION_REJ:
+ fprintf(fd, "RFCNB SESSION REJECT: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ if (RFCNB_Pkt_Len(pkt->data) < 1) {
+ fprintf(fd, " Protocol Error, short Reject packet!\n");
+ } else {
+ fprintf(fd, " Error = %x\n", CVAL(pkt->data, RFCNB_Pkt_Error_Offset));
+ }
+
+ break;
+
+ case RFCNB_SESSION_RETARGET:
+
+ fprintf(fd, "RFCNB SESSION RETARGET: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+
+ /* Print out the IP address etc and the port? */
+
+ break;
+
+ case RFCNB_SESSION_KEEP_ALIVE:
+
+ fprintf(fd, "RFCNB SESSION KEEP ALIVE: Length = %i\n",
+ RFCNB_Pkt_Len(pkt->data));
+ break;
+
+ default:
+
+ break;
+ }
+
+}
+
+/* Resolve a name into an address */
+
+int
+RFCNB_Name_To_IP(char *host, struct in_addr *Dest_IP)
+{
+ int addr; /* Assumes IP4, 32 bit network addresses */
+ struct hostent *hp;
+
+ /* Use inet_addr to try to convert the address */
+
+ if ((addr = inet_addr(host)) == INADDR_NONE) { /* Oh well, a good try :-) */
+
+ /* Now try a name look up with gethostbyname */
+
+ if ((hp = gethostbyname(host)) == NULL) { /* Not in DNS */
+
+ /* Try NetBIOS name lookup, how the hell do we do that? */
+
+ RFCNB_errno = RFCNBE_BadName; /* Is this right? */
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ } else { /* We got a name */
+
+ memcpy((void *) Dest_IP, (void *) hp->h_addr_list[0], sizeof(struct in_addr));
+
+ }
+ } else { /* It was an IP address */
+
+ memcpy((void *) Dest_IP, (void *) &addr, sizeof(struct in_addr));
+
+ }
+
+ return 0;
+
+}
+
+/* Disconnect the TCP connection to the server */
+
+int
+RFCNB_Close(int socket)
+{
+
+ close(socket);
+
+ /* If we want to do error recovery, here is where we put it */
+
+ return 0;
+
+}
+
+/* Connect to the server specified in the IP address.
+ * Not sure how to handle socket options etc. */
+
+int
+RFCNB_IP_Connect(struct in_addr Dest_IP, int port)
+{
+ struct sockaddr_in Socket;
+ int fd;
+
+ /* Create a socket */
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { /* Handle the error */
+
+ RFCNB_errno = RFCNBE_BadSocket;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ bzero((char *) &Socket, sizeof(Socket));
+ memcpy((char *) &Socket.sin_addr, (char *) &Dest_IP, sizeof(Dest_IP));
+
+ Socket.sin_port = htons(port);
+ Socket.sin_family = PF_INET;
+
+ /* Now connect to the destination */
+
+ if (connect(fd, (struct sockaddr *) &Socket, sizeof(Socket)) < 0) { /* Error */
+
+ close(fd);
+ RFCNB_errno = RFCNBE_ConnectFailed;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+ }
+ return (fd);
+
+}
+
+/* handle the details of establishing the RFCNB session with remote
+ * end
+ *
+ */
+
+int
+RFCNB_Session_Req(struct RFCNB_Con *con,
+ char *Called_Name,
+ char *Calling_Name,
+ BOOL * redirect,
+ struct in_addr *Dest_IP,
+ int *port)
+{
+ char *sess_pkt;
+
+ /* Response packet should be no more than 9 bytes, make 16 jic */
+
+ char resp[16];
+ int len;
+ struct RFCNB_Pkt *pkt, res_pkt;
+
+ /* We build and send the session request, then read the response */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Sess_Len);
+
+ if (pkt == NULL) {
+
+ return (RFCNBE_Bad); /* Leave the error that RFCNB_Alloc_Pkt gives) */
+
+ }
+ sess_pkt = pkt->data; /* Get pointer to packet proper */
+
+ sess_pkt[RFCNB_Pkt_Type_Offset] = RFCNB_SESSION_REQUEST;
+ RFCNB_Put_Pkt_Len(sess_pkt, RFCNB_Pkt_Sess_Len - RFCNB_Pkt_Hdr_Len);
+ sess_pkt[RFCNB_Pkt_N1Len_Offset] = 32;
+ sess_pkt[RFCNB_Pkt_N2Len_Offset] = 32;
+
+ RFCNB_CvtPad_Name(Called_Name, (sess_pkt + RFCNB_Pkt_Called_Offset));
+ RFCNB_CvtPad_Name(Calling_Name, (sess_pkt + RFCNB_Pkt_Calling_Offset));
+
+ /* Now send the packet */
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Sending packet: ");
+
+#endif
+
+ if ((len = RFCNB_Put_Pkt(con, pkt, RFCNB_Pkt_Sess_Len)) < 0) {
+
+ return (RFCNBE_Bad); /* Should be able to write that lot ... */
+
+ }
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Getting packet.\n");
+
+#endif
+
+ res_pkt.data = resp;
+ res_pkt.len = sizeof(resp);
+ res_pkt.next = NULL;
+
+ if ((len = RFCNB_Get_Pkt(con, &res_pkt, sizeof(resp))) < 0) {
+
+ return (RFCNBE_Bad);
+
+ }
+ /* Now analyze the packet ... */
+
+ switch (RFCNB_Pkt_Type(resp)) {
+
+ case RFCNB_SESSION_REJ: /* Didnt like us ... too bad */
+
+ /* Why did we get rejected ? */
+
+ switch (CVAL(resp, RFCNB_Pkt_Error_Offset)) {
+
+ case 0x80:
+ RFCNB_errno = RFCNBE_CallRejNLOCN;
+ break;
+ case 0x81:
+ RFCNB_errno = RFCNBE_CallRejNLFCN;
+ break;
+ case 0x82:
+ RFCNB_errno = RFCNBE_CallRejCNNP;
+ break;
+ case 0x83:
+ RFCNB_errno = RFCNBE_CallRejInfRes;
+ break;
+ case 0x8F:
+ RFCNB_errno = RFCNBE_CallRejUnSpec;
+ break;
+ default:
+ RFCNB_errno = RFCNBE_ProtErr;
+ break;
+ }
+
+ return (RFCNBE_Bad);
+ break;
+
+ case RFCNB_SESSION_ACK: /* Got what we wanted ... */
+
+ return (0);
+ break;
+
+ case RFCNB_SESSION_RETARGET: /* Go elsewhere */
+
+ *redirect = TRUE; /* Copy port and ip addr */
+
+ memcpy(Dest_IP, (resp + RFCNB_Pkt_IP_Offset), sizeof(struct in_addr));
+ *port = SVAL(resp, RFCNB_Pkt_Port_Offset);
+
+ return (0);
+ break;
+
+ default: /* A protocol error */
+
+ RFCNB_errno = RFCNBE_ProtErr;
+ return (RFCNBE_Bad);
+ break;
+ }
+}
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Utility Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+
+void RFCNB_CvtPad_Name(char *name1, char *name2);
+
+void RFCNB_AName_To_NBName(char *AName, char *NBName);
+
+void RFCNB_NBName_To_AName(char *NBName, char *AName);
+
+void RFCNB_Print_Hex(FILE * fd, struct RFCNB_Pkt *pkt, int Offset, int Len);
+
+struct RFCNB_Pkt *RFCNB_Alloc_Pkt(int n);
+
+void RFCNB_Print_Pkt(FILE * fd, char *dirn, struct RFCNB_Pkt *pkt, int len);
+
+int RFCNB_Name_To_IP(char *host, struct in_addr *Dest_IP);
+
+int RFCNB_Close(int socket);
+
+int RFCNB_IP_Connect(struct in_addr Dest_IP, int port);
+
+int RFCNB_Session_Req(struct RFCNB_Con *con,
+ char *Called_Name,
+ char *Calling_Name,
+ BOOL * redirect,
+ struct in_addr *Dest_IP,
+ int *port);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * RFCNB Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* Error responses */
+
+#include "rfcnb-error.h"
+#include "rfcnb-common.h"
+#include "smblib-priv.h"
+
+/* Defines we need */
+
+#define RFCNB_Default_Port 139
+
+/* Definition of routines we define */
+
+void *RFCNB_Call(char *Called_Name, char *Calling_Name, char *Called_Address,
+ int port);
+
+int RFCNB_Send(void *Con_Handle, struct RFCNB_Pkt *Data, int Length);
+
+int RFCNB_Recv(void *Con_Handle, struct RFCNB_Pkt *Data, int Length);
+
+int RFCNB_Hangup(void *con_Handle);
+
+void *RFCNB_Listen();
+
+void RFCNB_Get_Error(char *buffer, int buf_len);
+
+struct RFCNB_Pkt *RFCNB_Alloc_Pkt(int n);
+
+void RFCNB_Free_Pkt(struct RFCNB_Pkt *pkt);
+
+int RFCNB_Set_Sock_NoDelay(void *con_Handle, BOOL yn);
--- /dev/null
+/* UNIX RFCNB (RFC1001/RFC1002) NetBIOS implementation
+ *
+ * Version 1.0
+ * Session Routines ...
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 <malloc.h>
+#include <string.h>
+
+int RFCNB_errno = 0;
+int RFCNB_saved_errno = 0;
+#define RFCNB_ERRNO
+
+#include "std-includes.h"
+#include <netinet/tcp.h>
+#include "rfcnb-priv.h"
+#include "rfcnb-util.h"
+#include "rfcnb-io.h"
+
+/* global data structures */
+
+static char *RFCNB_Error_Strings[] =
+{
+
+ "RFCNBE_OK: Routine completed successfully.",
+ "RFCNBE_NoSpace: No space available for a malloc call.",
+ "RFCNBE_BadName: NetBIOS name could not be translated to IP address.",
+ "RFCNBE_BadRead: Read system call returned an error. Check errno.",
+ "RFCNBE_BadWrite: Write system call returned an error. Check errno.",
+ "RFCNBE_ProtErr: A protocol error has occurred.",
+ "RFCNBE_ConGone: Connection dropped during a read or write system call.",
+ "RFCNBE_BadHandle: Bad connection handle passed.",
+ "RFCNBE_BadSocket: Problems creating socket.",
+ "RFCNBE_ConnectFailed: Connection failed. See errno.",
+ "RFCNBE_CallRejNLOCN: Call rejected. Not listening on called name.",
+ "RFCNBE_CallRejNLFCN: Call rejected. Not listening for called name.",
+ "RFCNBE_CallRejCNNP: Call rejected. Called name not present.",
+ "RFCNBE_CallRejInfRes: Call rejected. Name present, but insufficient resources.",
+ "RFCNBE_CallRejUnSpec: Call rejected. Unspecified error.",
+ "RFCNBE_BadParam: Bad parameters passed to a routine.",
+ "RFCNBE_Timeout: IO Operation timed out ..."
+
+};
+
+int RFCNB_Stats[RFCNB_MAX_STATS];
+
+void (*Prot_Print_Routine) () = NULL; /* Pointer to print routine */
+
+/* Set up a session with a remote name. We are passed Called_Name as a
+ * string which we convert to a NetBIOS name, ie space terminated, up to
+ * 16 characters only if we need to. If Called_Address is not empty, then
+ * we use it to connect to the remote end, but put in Called_Name ... Called
+ * Address can be a DNS based name, or a TCP/IP address ...
+ */
+
+void *
+RFCNB_Call(char *Called_Name, char *Calling_Name, char *Called_Address,
+ int port)
+{
+ struct RFCNB_Con *con;
+ struct in_addr Dest_IP;
+ int Client;
+ BOOL redirect;
+ struct redirect_addr *redir_addr;
+ char *Service_Address;
+
+ /* Now, we really should look up the port in /etc/services ... */
+
+ if (port == 0)
+ port = RFCNB_Default_Port;
+
+ /* Create a connection structure first */
+
+ if ((con = (struct RFCNB_Con *) malloc(sizeof(struct RFCNB_Con))) == NULL) { /* Error in size */
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ con->fd = -0; /* no descriptor yet */
+ con->rfc_errno = 0; /* no error yet */
+ con->timeout = 0; /* no timeout */
+ con->redirects = 0;
+ con->redirect_list = NULL; /* Fix bug still in version 0.50 */
+
+ /* Resolve that name into an IP address */
+
+ Service_Address = Called_Name;
+ if (strcmp(Called_Address, "") != 0) { /* If the Called Address = "" */
+ Service_Address = Called_Address;
+ }
+ if ((errno = RFCNB_Name_To_IP(Service_Address, &Dest_IP)) < 0) { /* Error */
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_Name_To_IP */
+
+ return (NULL);
+
+ }
+ /* Now connect to the remote end */
+
+ redirect = TRUE; /* Fudge this one so we go once through */
+
+ while (redirect) { /* Connect and get session info etc */
+
+ redirect = FALSE; /* Assume all OK */
+
+ /* Build the redirect info. First one is first addr called */
+ /* And tack it onto the list of addresses we called */
+
+ if ((redir_addr = (struct redirect_addr *) malloc(sizeof(struct redirect_addr))) == NULL) { /* Could not get space */
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (NULL);
+
+ }
+ memcpy((char *) &(redir_addr->ip_addr), (char *) &Dest_IP, sizeof(Dest_IP));
+ redir_addr->port = port;
+ redir_addr->next = NULL;
+
+ if (con->redirect_list == NULL) { /* Stick on head */
+
+ con->redirect_list = con->last_addr = redir_addr;
+
+ } else {
+
+ con->last_addr->next = redir_addr;
+ con->last_addr = redir_addr;
+
+ }
+
+ /* Now, make that connection */
+
+ if ((Client = RFCNB_IP_Connect(Dest_IP, port)) < 0) { /* Error */
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_IP_Connect */
+
+ return (NULL);
+
+ }
+ con->fd = Client;
+
+ /* Now send and handle the RFCNB session request */
+ /* If we get a redirect, we will comeback with redirect true
+ * and a new IP address in DEST_IP */
+
+ if ((errno = RFCNB_Session_Req(con,
+ Called_Name,
+ Calling_Name,
+ &redirect, &Dest_IP, &port)) < 0) {
+
+ /* No need to modify RFCNB_errno as it was done by RFCNB_Session.. */
+
+ return (NULL);
+
+ }
+ if (redirect) {
+
+ /* We have to close the connection, and then try again */
+
+ (con->redirects)++;
+
+ RFCNB_Close(con->fd); /* Close it */
+
+ }
+ }
+
+ return (con);
+
+}
+
+/* We send a packet to the other end ... for the moment, we treat the
+ * data as a series of pointers to blocks of data ... we should check the
+ * length ... */
+
+int
+RFCNB_Send(struct RFCNB_Con *Con_Handle, struct RFCNB_Pkt *udata, int Length)
+{
+ struct RFCNB_Pkt *pkt;
+ char *hdr;
+ int len;
+
+ /* Plug in the header and send the data */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
+
+ if (pkt == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ pkt->next = udata; /* The user data we want to send */
+
+ hdr = pkt->data;
+
+ /* Following crap is for portability across multiple UNIX machines */
+
+ *(hdr + RFCNB_Pkt_Type_Offset) = RFCNB_SESSION_MESSAGE;
+ RFCNB_Put_Pkt_Len(hdr, Length);
+
+#ifdef RFCNB_DEBUG
+
+ fprintf(stderr, "Sending packet: ");
+
+#endif
+
+ if ((len = RFCNB_Put_Pkt(Con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
+
+ /* No need to change RFCNB_errno as it was done by put_pkt ... */
+
+ return (RFCNBE_Bad); /* Should be able to write that lot ... */
+
+ }
+ /* Now we have sent that lot, let's get rid of the RFCNB Header and return */
+
+ pkt->next = NULL;
+
+ RFCNB_Free_Pkt(pkt);
+
+ return (len);
+
+}
+
+/* We pick up a message from the internet ... We have to worry about
+ * non-message packets ... */
+
+int
+RFCNB_Recv(void *con_Handle, struct RFCNB_Pkt *Data, int Length)
+{
+ struct RFCNB_Pkt *pkt;
+ int ret_len;
+
+ if (con_Handle == NULL) {
+
+ RFCNB_errno = RFCNBE_BadHandle;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ /* Now get a packet from below. We allocate a header first */
+
+ /* Plug in the header and send the data */
+
+ pkt = RFCNB_Alloc_Pkt(RFCNB_Pkt_Hdr_Len);
+
+ if (pkt == NULL) {
+
+ RFCNB_errno = RFCNBE_NoSpace;
+ RFCNB_saved_errno = errno;
+ return (RFCNBE_Bad);
+
+ }
+ pkt->next = Data; /* Plug in the data portion */
+
+ if ((ret_len = RFCNB_Get_Pkt(con_Handle, pkt, Length + RFCNB_Pkt_Hdr_Len)) < 0) {
+
+#ifdef RFCNB_DEBUG
+ fprintf(stderr, "Bad packet return in RFCNB_Recv... \n");
+#endif
+
+ return (RFCNBE_Bad);
+
+ }
+ /* We should check that we go a message and not a keep alive */
+
+ pkt->next = NULL;
+
+ RFCNB_Free_Pkt(pkt);
+
+ return (ret_len);
+
+}
+
+/* We just disconnect from the other end, as there is nothing in the RFCNB */
+/* protocol that specifies any exchange as far as I can see */
+
+int
+RFCNB_Hangup(struct RFCNB_Con *con_Handle)
+{
+
+ if (con_Handle != NULL) {
+ RFCNB_Close(con_Handle->fd); /* Could this fail? */
+ free(con_Handle);
+ }
+ return 0;
+
+
+}
+
+/* Set TCP_NODELAY on the socket */
+
+int
+RFCNB_Set_Sock_NoDelay(struct RFCNB_Con *con_Handle, BOOL yn)
+{
+
+ return (setsockopt(con_Handle->fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *) &yn, sizeof(yn)));
+
+}
+
+
+/* Listen for a connection on a port???, when */
+/* the connection comes in, we return with the connection */
+
+void *
+RFCNB_Listen()
+{
+ fprintf(stderr, "RFCNB_Listen NOT IMPLEMENTED as yet!\n");
+ return NULL;
+}
+
+/* Pick up the last error response as a string, hmmm, this routine should */
+/* have been different ... */
+
+void
+RFCNB_Get_Error(char *buffer, int buf_len)
+{
+
+ if (RFCNB_saved_errno <= 0) {
+ sprintf(buffer, "%s", RFCNB_Error_Strings[RFCNB_errno]);
+ } else {
+ sprintf(buffer, "%s\n\terrno:%s", RFCNB_Error_Strings[RFCNB_errno],
+ strerror(RFCNB_saved_errno));
+ }
+
+}
+
+/* Pick up the last error response and returns as a code */
+
+int
+RFCNB_Get_Last_Error()
+{
+
+ return (RFCNB_errno);
+
+}
+
+/* Pick up saved errno as well */
+
+int
+RFCNB_Get_Last_Errno()
+{
+
+ return (RFCNB_saved_errno);
+
+}
+
+/* Pick up the last error response and return in string ... */
+
+void
+RFCNB_Get_Error_Msg(int code, char *msg_buf, int len)
+{
+
+ strncpy(msg_buf, RFCNB_Error_Strings[abs(code)], len);
+
+}
+
+/* Register a higher level protocol print routine */
+
+void
+RFCNB_Register_Print_Routine(void (*fn) ())
+{
+
+ Prot_Print_Routine = fn;
+
+}
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ *
+ * a partial implementation of DES designed for use in the
+ * SMB authentication protocol
+ *
+ * Copyright (C) Andrew Tridgell 1997
+ *
+ * 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.
+ */
+
+
+/* NOTES:
+ *
+ * This code makes no attempt to be fast! In fact, it is a very
+ * slow implementation
+ *
+ * This code is NOT a complete DES implementation. It implements only
+ * the minimum necessary for SMB authentication, as used by all SMB
+ * products (including every copy of Microsoft Windows95 ever sold)
+ *
+ * In particular, it can only do a unchained forward DES pass. This
+ * means it is not possible to use this code for encryption/decryption
+ * of data, instead it is only useful as a "hash" algorithm.
+ *
+ * There is no entry point into this code that allows normal DES operation.
+ *
+ * I believe this means that this code does not come under ITAR
+ * regulations but this is NOT a legal opinion. If you are concerned
+ * about the applicability of ITAR regulations to this code then you
+ * should confirm it for yourself (and maybe let me know if you come
+ * up with a different answer to the one above)
+ */
+
+
+
+static int perm1[56] =
+{57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static int perm2[48] =
+{14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static int perm3[64] =
+{58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static int perm4[48] =
+{32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static int perm5[32] =
+{16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static int perm6[64] =
+{40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static int sc[16] =
+{1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static int sbox[8][4][16] =
+{
+ {
+ {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {
+ {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {
+ {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {
+ {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {
+ {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {
+ {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {
+ {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {
+ {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void
+permute(char *out, char *in, int *p, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = in[p[i] - 1];
+}
+
+static void
+lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = d[(i + count) % n];
+ for (i = 0; i < n; i++)
+ d[i] = out[i];
+}
+
+static void
+concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void
+xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void
+dohash(char *out, char *in, char *key)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i = 0; i < 28; i++)
+ c[i] = pk1[i];
+ for (i = 0; i < 28; i++)
+ d[i] = pk1[i + 28];
+
+ for (i = 0; i < 16; i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j = 0; j < 32; j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j + 32];
+ }
+
+ for (i = 0; i < 16; i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[i], 48);
+
+ for (j = 0; j < 8; j++)
+ for (k = 0; k < 6; k++)
+ b[j][k] = erk[j * 6 + k];
+
+ for (j = 0; j < 8; j++) {
+ int m, n;
+ m = (b[j][0] << 1) | b[j][5];
+
+ n = (b[j][1] << 3) | (b[j][2] << 2) | (b[j][3] << 1) | b[j][4];
+
+ for (k = 0; k < 4; k++)
+ b[j][k] = (sbox[j][m][n] & (1 << (3 - k))) ? 1 : 0;
+ }
+
+ for (j = 0; j < 8; j++)
+ for (k = 0; k < 4; k++)
+ cb[j * 4 + k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j = 0; j < 32; j++)
+ l[j] = r[j];
+
+ for (j = 0; j < 32; j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void
+str_to_key(unsigned char *str, unsigned char *key)
+{
+ int i;
+
+ key[0] = str[0] >> 1;
+ key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2);
+ key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3);
+ key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4);
+ key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5);
+ key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6);
+ key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7);
+ key[7] = str[6] & 0x7F;
+ for (i = 0; i < 8; i++) {
+ key[i] = (key[i] << 1);
+ }
+}
+
+
+static void
+smbhash(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ unsigned char key2[8];
+
+ str_to_key(key, key2);
+
+ for (i = 0; i < 64; i++) {
+ inb[i] = (in[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
+ keyb[i] = (key2[i / 8] & (1 << (7 - (i % 8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb);
+
+ for (i = 0; i < 8; i++) {
+ out[i] = 0;
+ }
+
+ for (i = 0; i < 64; i++) {
+ if (outb[i])
+ out[i / 8] |= (1 << (7 - (i % 8)));
+ }
+}
+
+void
+E_P16(unsigned char *p14, unsigned char *p16)
+{
+ unsigned char sp8[8] =
+ {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ smbhash(p16, sp8, p14);
+ smbhash(p16 + 8, sp8, p14 + 7);
+}
+
+void
+E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24)
+{
+ smbhash(p24, c8, p21);
+ smbhash(p24 + 8, c8, p21 + 7);
+ smbhash(p24 + 16, c8, p21 + 14);
+}
+
+void
+cred_hash1(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+
+ smbhash(buf, in, key);
+ smbhash(out, buf, key + 9);
+}
+
+void
+cred_hash2(unsigned char *out, unsigned char *in, unsigned char *key)
+{
+ unsigned char buf[8];
+ static unsigned char key2[8];
+
+ smbhash(buf, in, key);
+ key2[0] = key[7];
+ smbhash(out, buf, key2);
+}
--- /dev/null
+void E_P16(unsigned char *p14, unsigned char *p16);
+void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24);
--- /dev/null
+/*
+ * Unix SMB/Netbios implementation.
+ * Version 1.9.
+ * SMB parameters and setup
+ * Copyright (C) Andrew Tridgell 1992-1997
+ * Modified by Jeremy Allison 1995.
+ *
+ * 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 <string.h>
+#include <ctype.h>
+//#include <arpa/inet.h>
+#include <dirent.h>
+#include <string.h>
+//#include <sys/vfs.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "smblib-priv.h"
+#include "md4.h"
+#include "smbdes.h"
+#define uchar unsigned char
+extern int DEBUGLEVEL;
+
+#include "byteorder.h"
+
+char *StrnCpy(char *dest, char *src, int n);
+void strupper(char *s);
+
+/*
+ * This implements the X/Open SMB password encryption
+ * It takes a password, a 8 byte "crypt key" and puts 24 bytes of
+ * encrypted password into p24 */
+void
+SMBencrypt(uchar * passwd, uchar * c8, uchar * p24)
+{
+ uchar p14[15], p21[21];
+
+ memset(p21, '\0', 21);
+ memset(p14, '\0', 14);
+ StrnCpy((char *) p14, (char *) passwd, 14);
+
+ strupper((char *) p14);
+ E_P16(p14, p21);
+ E_P24(p21, c8, p24);
+}
+
+/* Routines for Windows NT MD4 Hash functions. */
+static int
+_my_wcslen(int16 * str)
+{
+ int len = 0;
+ while (*str++ != 0)
+ len++;
+ return len;
+}
+
+/*
+ * Convert a string into an NT UNICODE string.
+ * Note that regardless of processor type
+ * this must be in intel (little-endian)
+ * format.
+ */
+
+static int
+_my_mbstowcs(int16 * dst, uchar * src, int len)
+{
+ int i;
+ int16 val;
+
+ for (i = 0; i < len; i++) {
+ val = *src;
+ SSVAL(dst, 0, val);
+ dst++;
+ src++;
+ if (val == 0)
+ break;
+ }
+ return i;
+}
+
+/*
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ */
+
+void
+E_md4hash(uchar * passwd, uchar * p16)
+{
+ int len;
+ int16 wpwd[129];
+
+ /* Password cannot be longer than 128 characters */
+ len = strlen((char *) passwd);
+ if (len > 128)
+ len = 128;
+ /* Password must be converted to NT unicode */
+ _my_mbstowcs(wpwd, passwd, len);
+ wpwd[len] = 0; /* Ensure string is null terminated */
+ /* Calculate length in bytes */
+ len = _my_wcslen(wpwd) * sizeof(int16);
+
+ mdfour(p16, (unsigned char *) wpwd, len);
+}
+
+/* Does the NT MD4 hash then des encryption. */
+
+void
+SMBNTencrypt(uchar * passwd, uchar * c8, uchar * p24)
+{
+ uchar p21[21];
+
+ memset(p21, '\0', 21);
+
+ E_md4hash(passwd, p21);
+ E_P24(p21, c8, p24);
+}
+
+/* Does both the NT and LM owfs of a user's password */
+
+void
+nt_lm_owf_gen(char *pwd, char *nt_p16, char *p16)
+{
+ char passwd[130];
+ StrnCpy(passwd, pwd, sizeof(passwd) - 1);
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash((uchar *) passwd, (uchar *) nt_p16);
+
+ /* Mangle the passwords into Lanman format */
+ passwd[14] = '\0';
+ strupper(passwd);
+
+ /* Calculate the SMB (lanman) hash functions of the password */
+
+ memset(p16, '\0', 16);
+ E_P16((uchar *) passwd, (uchar *) p16);
+
+ /* clear out local copy of user's password (just being paranoid). */
+ bzero(passwd, sizeof(passwd));
+}
+
+/****************************************************************************
+line strncpy but always null terminates. Make sure there is room!
+****************************************************************************/
+char *
+StrnCpy(char *dest, char *src, int n)
+{
+ char *d = dest;
+ if (!dest)
+ return (NULL);
+ if (!src) {
+ *dest = 0;
+ return (dest);
+ }
+ while (n-- && (*d++ = *src++));
+ *d = 0;
+ return (dest);
+}
+
+void
+strupper(char *s)
+{
+ while (*s) {
+ /*
+ * #if !defined(KANJI_WIN95_COMPATIBILITY)
+ * if(lp_client_code_page() == KANJI_CODEPAGE)
+ * {
+ *
+ * if (is_shift_jis (*s))
+ * {
+ * if (is_sj_lower (s[0], s[1]))
+ * s[1] = sj_toupper2 (s[1]);
+ * s += 2;
+ * }
+ * else if (is_kana (*s))
+ * {
+ * s++;
+ * }
+ * else
+ * {
+ * if (islower(*s))
+ * *s = toupper(*s);
+ * s++;
+ * }
+ * }
+ * else
+ * #endif *//* KANJI_WIN95_COMPATIBILITY */
+ {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+ }
+}
--- /dev/null
+void SMBencrypt(uchar * passwd, uchar * c8, uchar * p24);
--- /dev/null
+#ifndef __SMBLIB_COMMON_H__
+#define __SMBLIB_COMMON_H__
+
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Common Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/* To get the error class we want the first 8 bits */
+/* Because we just grab 4bytes from the SMB header, we have to re-order */
+/* here, but it makes the NtStatus part easier in future */
+
+#define SMBlib_Error_Class(p) (p & 0x000000FF)
+
+/* To get the error code, we want the bottom 16 bits */
+
+#define SMBlib_Error_Code(p) (((unsigned int)p & 0xFFFF0000) >>16)
+
+/* Error CLASS codes and etc ... */
+
+#define SMBC_SUCCESS 0
+#define SMBC_ERRDOS 0x01
+#define SMBC_ERRSRV 0x02
+#define SMBC_ERRHRD 0x03
+#define SMBC_ERRCMD 0xFF
+
+/* Success error codes */
+
+#define SMBS_BUFFERED 0x54
+#define SMBS_LOGGED 0x55
+#define SMBS_DISPLAYED 0x56
+
+/* ERRDOS Error codes */
+
+#define SMBD_badfunc 0x01
+#define SMBD_badfile 0x02
+#define SMBD_badpath 0x03
+#define SMBD_nofids 0x04
+#define SMBD_noaccess 0x05
+#define SMBD_badfid 0x06
+#define SMBD_badmcb 0x07
+#define SMBD_nomem 0x08
+#define SMBD_badmem 0x09
+#define SMBD_badenv 0x0A
+#define SMBD_badformat 0x0B
+#define SMBD_badaccess 0x0C
+#define SMBD_baddata 0x0D
+#define SMBD_reserved 0x0E
+#define SMBD_baddrive 0x0F
+#define SMBD_remcd 0x10
+#define SMBD_diffdevice 0x11
+#define SMBD_nofiles 0x12
+#define SMBD_badshare 0x20
+#define SMBD_errlock 0x21
+#define SMBD_filexists 0x50
+
+/* Server errors ... */
+
+#define SMBV_error 0x01 /* Generic error */
+#define SMBV_badpw 0x02
+#define SMBV_badtype 0x03
+#define SMBV_access 0x04
+#define SMBV_invnid 0x05
+#define SMBV_invnetname 0x06
+#define SMBV_invdevice 0x07
+#define SMBV_qfull 0x31
+#define SMBV_qtoobig 0x32
+#define SMBV_qeof 0x33
+#define SMBV_invpfid 0x34
+#define SMBV_paused 0x51
+#define SMBV_msgoff 0x52
+#define SMBV_noroom 0x53
+#define SMBV_rmuns 0x57
+#define SMBV_nosupport 0xFFFF
+
+/* Hardware error codes ... */
+
+#define SMBH_nowrite 0x13
+#define SMBH_badunit 0x14
+#define SMBH_notready 0x15
+#define SMBH_badcmd 0x16
+#define SMBH_data 0x17
+#define SMBH_badreq 0x18
+#define SMBH_seek 0x19
+#define SMBH_badmedia 0x1A
+#define SMBH_badsector 0x1B
+#define SMBH_nopaper 0x1C
+#define SMBH_write 0x1D
+#define SMBH_read 0x1E
+#define SMBH_general 0x1F
+#define SMBH_badshare 0x20
+
+/* Access mode defines ... */
+
+#define SMB_AMODE_WTRU 0x4000
+#define SMB_AMODE_NOCACHE 0x1000
+#define SMB_AMODE_COMPAT 0x0000
+#define SMB_AMODE_DENYRWX 0x0010
+#define SMB_AMODE_DENYW 0x0020
+#define SMB_AMODE_DENYRX 0x0030
+#define SMB_AMODE_DENYNONE 0x0040
+#define SMB_AMODE_OPENR 0x0000
+#define SMB_AMODE_OPENW 0x0001
+#define SMB_AMODE_OPENRW 0x0002
+#define SMB_AMODE_OPENX 0x0003
+#define SMB_AMODE_FCBOPEN 0x00FF
+#define SMB_AMODE_LOCUNKN 0x0000
+#define SMB_AMODE_LOCMSEQ 0x0100
+#define SMB_AMODE_LOCMRAN 0x0200
+#define SMB_AMODE_LOCRAL 0x0300
+
+/* File attribute encoding ... */
+
+#define SMB_FA_ORD 0x00
+#define SMB_FA_ROF 0x01
+#define SMB_FA_HID 0x02
+#define SMB_FA_SYS 0x04
+#define SMB_FA_VOL 0x08
+#define SMB_FA_DIR 0x10
+#define SMB_FA_ARC 0x20
+
+/* Define the protocol types ... */
+
+#define SMB_P_Unknown -1 /* Hmmm, is this smart? */
+#define SMB_P_Core 0
+#define SMB_P_CorePlus 1
+#define SMB_P_DOSLanMan1 2
+#define SMB_P_LanMan1 3
+#define SMB_P_DOSLanMan2 4
+#define SMB_P_LanMan2 5
+#define SMB_P_DOSLanMan2_1 6
+#define SMB_P_LanMan2_1 7
+#define SMB_P_NT1 8
+
+/* SMBlib return codes */
+/* We want something that indicates whether or not the return code was a */
+/* remote error, a local error in SMBlib or returned from lower layer ... */
+/* Wonder if this will work ... */
+/* SMBlibE_Remote = 1 indicates remote error */
+/* SMBlibE_ values < 0 indicate local error with more info available */
+/* SMBlibE_ values >1 indicate local from SMBlib code errors? */
+
+#define SMBlibE_Success 0
+#define SMBlibE_Remote 1 /* Remote error, get more info from con */
+#define SMBlibE_BAD -1
+#define SMBlibE_LowerLayer 2 /* Lower layer error */
+#define SMBlibE_NotImpl 3 /* Function not yet implemented */
+#define SMBlibE_ProtLow 4 /* Protocol negotiated does not support req */
+#define SMBlibE_NoSpace 5 /* No space to allocate a structure */
+#define SMBlibE_BadParam 6 /* Bad parameters */
+#define SMBlibE_NegNoProt 7 /* None of our protocols was liked */
+#define SMBlibE_SendFailed 8 /* Sending an SMB failed */
+#define SMBlibE_RecvFailed 9 /* Receiving an SMB failed */
+#define SMBlibE_GuestOnly 10 /* Logged in as guest */
+#define SMBlibE_CallFailed 11 /* Call remote end failed */
+#define SMBlibE_ProtUnknown 12 /* Protocol unknown */
+#define SMBlibE_NoSuchMsg 13 /* Keep this up to date */
+
+typedef struct { /* A structure for a Dirent */
+
+ unsigned char resume_key[21]; /* Don't touch this */
+ unsigned char file_attributes; /* Attributes of file */
+ unsigned int date_time; /* date and time of last mod */
+ unsigned int size;
+ char filename[13]; /* The name of the file */
+
+} SMB_CP_dirent;
+
+#endif /* __SMBLIB_COMMON_H__ */
--- /dev/null
+#ifndef __SMBLIB_PRIV_H__
+#define __SMBLIB_PRIV_H__
+
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib private Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "std-defines.h"
+#include "smblib-common.h"
+#include <sys/types.h>
+#include <unistd.h>
+
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+
+#include "byteorder.h" /* Hmmm ... hot good */
+
+#define max(a,b) (a < b ? b : a)
+
+#define SMB_DEF_IDF 0x424D53FF /* "\377SMB" */
+
+/* Core protocol commands */
+
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* CorePlus protocol */
+
+#define SMBlockread 0x13 /* Lock a range and read it */
+#define SMBwriteunlock 0x14 /* Unlock a range and then write */
+#define SMBreadbraw 0x1a /* read a block of data without smb header ohead */
+#define SMBwritebraw 0x1d /* write a block of data without smb header ohead */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file and then close it */
+
+/* DOS Extended Protocol */
+
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBtconX 0x75 /* tree connect and X */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Any more ? */
+
+#define SMBdatablockID 0x01 /* A data block identifier */
+#define SMBdialectID 0x02 /* A dialect id */
+#define SMBpathnameID 0x03 /* A pathname ID */
+#define SMBasciiID 0x04 /* An ascii string ID */
+#define SMBvariableblockID 0x05 /* A variable block ID */
+
+/* some other defines we need */
+
+/* Flags defines ... */
+
+#define SMB_FLG2_NON_DOS 0x01 /* We know non dos names */
+#define SMB_FLG2_EXT_ATR 0x02 /* We know about Extended Attributes */
+#define SMB_FLG2_LNG_NAM 0x04 /* Long names ? */
+
+typedef unsigned short WORD;
+typedef unsigned short UWORD;
+typedef unsigned int ULONG;
+typedef unsigned char BYTE;
+typedef unsigned char UCHAR;
+
+/* Some macros to allow access to actual packet data so that we */
+/* can change the underlying representation of packets. */
+/* */
+/* The current formats vying for attention are a fragment */
+/* approach where the SMB header is a fragment linked to the */
+/* data portion with the transport protocol (rfcnb or whatever) */
+/* being linked on the front. */
+/* */
+/* The other approach is where the whole packet is one array */
+/* of bytes with space allowed on the front for the packet */
+/* headers. */
+
+#define SMB_Hdr(p) (char *)(p -> data)
+
+/* SMB Hdr def for File Sharing Protocol? From MS and Intel, */
+/* Intel PN 138446 Doc Version 2.0, Nov 7, 1988. This def also */
+/* applies to LANMAN1.0 as well as the Core Protocol */
+/* The spec states that wct and bcc must be present, even if 0 */
+
+/* We define these as offsets into a char SMB[] array for the */
+/* sake of portability */
+
+/* NOTE!. Some of the lenght defines, SMB_<protreq>_len do not include */
+/* the data that follows in the SMB packet, so the code will have to */
+/* take that into account. */
+
+#define SMB_hdr_idf_offset 0 /* 0xFF,'SMB' 0-3 */
+#define SMB_hdr_com_offset 4 /* BYTE 4 */
+#define SMB_hdr_rcls_offset 5 /* BYTE 5 */
+#define SMB_hdr_reh_offset 6 /* BYTE 6 */
+#define SMB_hdr_err_offset 7 /* WORD 7 */
+#define SMB_hdr_reb_offset 9 /* BYTE 9 */
+#define SMB_hdr_flg_offset 9 /* same as reb ... */
+#define SMB_hdr_res_offset 10 /* 7 WORDs 10 */
+#define SMB_hdr_res0_offset 10 /* WORD 10 */
+#define SMB_hdr_flg2_offset 10 /* WORD */
+#define SMB_hdr_res1_offset 12 /* WORD 12 */
+#define SMB_hdr_res2_offset 14
+#define SMB_hdr_res3_offset 16
+#define SMB_hdr_res4_offset 18
+#define SMB_hdr_res5_offset 20
+#define SMB_hdr_res6_offset 22
+#define SMB_hdr_tid_offset 24
+#define SMB_hdr_pid_offset 26
+#define SMB_hdr_uid_offset 28
+#define SMB_hdr_mid_offset 30
+#define SMB_hdr_wct_offset 32
+
+#define SMB_hdr_len 33 /* 33 byte header? */
+
+#define SMB_hdr_axc_offset 33 /* AndX Command */
+#define SMB_hdr_axr_offset 34 /* AndX Reserved */
+#define SMB_hdr_axo_offset 35 /* Offset from start to WCT of AndX cmd */
+
+/* Format of the Negotiate Protocol SMB */
+
+#define SMB_negp_bcc_offset 33
+#define SMB_negp_buf_offset 35 /* Where the buffer starts */
+#define SMB_negp_len 35 /* plus the data */
+
+/* Format of the Negotiate Response SMB, for CoreProtocol, LM1.2 and */
+/* NT LM 0.12. wct will be 1 for CoreProtocol, 13 for LM 1.2, and 17 */
+/* for NT LM 0.12 */
+
+#define SMB_negrCP_idx_offset 33 /* Response to the neg req */
+#define SMB_negrCP_bcc_offset 35
+#define SMB_negrLM_idx_offset 33 /* dialect index */
+#define SMB_negrLM_sec_offset 35 /* Security mode */
+#define SMB_sec_user_mask 0x01 /* 0 = share, 1 = user */
+#define SMB_sec_encrypt_mask 0x02 /* pick out encrypt */
+#define SMB_negrLM_mbs_offset 37 /* max buffer size */
+#define SMB_negrLM_mmc_offset 39 /* max mpx count */
+#define SMB_negrLM_mnv_offset 41 /* max number of VCs */
+#define SMB_negrLM_rm_offset 43 /* raw mode support bit vec */
+#define SMB_read_raw_mask 0x01
+#define SMB_write_raw_mask 0x02
+#define SMB_negrLM_sk_offset 45 /* session key, 32 bits */
+#define SMB_negrLM_st_offset 49 /* Current server time */
+#define SMB_negrLM_sd_offset 51 /* Current server date */
+#define SMB_negrLM_stz_offset 53 /* Server Time Zone */
+#define SMB_negrLM_ekl_offset 55 /* encryption key length */
+#define SMB_negrLM_res_offset 57 /* reserved */
+#define SMB_negrLM_bcc_offset 59 /* bcc */
+#define SMB_negrLM_len 61 /* 61 bytes ? */
+#define SMB_negrLM_buf_offset 61 /* Where the fun begins */
+
+#define SMB_negrNTLM_idx_offset 33 /* Selected protocol */
+#define SMB_negrNTLM_sec_offset 35 /* Security more */
+#define SMB_negrNTLM_mmc_offset 36 /* Different format above */
+#define SMB_negrNTLM_mnv_offset 38 /* Max VCs */
+#define SMB_negrNTLM_mbs_offset 40 /* MBS now a long */
+#define SMB_negrNTLM_mrs_offset 44 /* Max raw size */
+#define SMB_negrNTLM_sk_offset 48 /* Session Key */
+#define SMB_negrNTLM_cap_offset 52 /* Capabilities */
+#define SMB_negrNTLM_stl_offset 56 /* Server time low */
+#define SMB_negrNTLM_sth_offset 60 /* Server time high */
+#define SMB_negrNTLM_stz_offset 64 /* Server time zone */
+#define SMB_negrNTLM_ekl_offset 66 /* Encrypt key len */
+#define SMB_negrNTLM_bcc_offset 67 /* Bcc */
+#define SMB_negrNTLM_len 69
+#define SMB_negrNTLM_buf_offset 69
+
+/* Offsets related to Tree Connect */
+
+#define SMB_tcon_bcc_offset 33
+#define SMB_tcon_buf_offset 35 /* where the data is for tcon */
+#define SMB_tcon_len 35 /* plus the data */
+
+#define SMB_tconr_mbs_offset 33 /* max buffer size */
+#define SMB_tconr_tid_offset 35 /* returned tree id */
+#define SMB_tconr_bcc_offset 37
+#define SMB_tconr_len 39
+
+#define SMB_tconx_axc_offset 33 /* And X Command */
+#define SMB_tconx_axr_offset 34 /* reserved */
+#define SMB_tconx_axo_offset 35 /* Next command offset */
+#define SMB_tconx_flg_offset 37 /* Flags, bit0=1 means disc TID */
+#define SMB_tconx_pwl_offset 39 /* Password length */
+#define SMB_tconx_bcc_offset 41 /* bcc */
+#define SMB_tconx_buf_offset 43 /* buffer */
+#define SMB_tconx_len 43 /* up to data ... */
+
+#define SMB_tconxr_axc_offset 33 /* Where the AndX Command is */
+#define SMB_tconxr_axr_offset 34 /* Reserved */
+#define SMB_tconxr_axo_offset 35 /* AndX offset location */
+
+/* Offsets related to tree_disconnect */
+
+#define SMB_tdis_bcc_offset 33 /* bcc */
+#define SMB_tdis_len 35 /* total len */
+
+#define SMB_tdisr_bcc_offset 33 /* bcc */
+#define SMB_tdisr_len 35
+
+/* Offsets related to Open Request */
+
+#define SMB_open_mod_offset 33 /* Mode to open with */
+#define SMB_open_atr_offset 35 /* Attributes of file */
+#define SMB_open_bcc_offset 37 /* bcc */
+#define SMB_open_buf_offset 39 /* File name */
+#define SMB_open_len 39 /* Plus the file name */
+
+#define SMB_openx_axc_offset 33 /* Next command */
+#define SMB_openx_axr_offset 34 /* Reserved */
+#define SMB_openx_axo_offset 35 /* offset of next wct */
+#define SMB_openx_flg_offset 37 /* Flags, bit0 = need more info */
+ /* bit1 = exclusive oplock */
+ /* bit2 = batch oplock */
+#define SMB_openx_mod_offset 39 /* mode to open with */
+#define SMB_openx_atr_offset 41 /* search attributes */
+#define SMB_openx_fat_offset 43 /* File attributes */
+#define SMB_openx_tim_offset 45 /* time and date of creat */
+#define SMB_openx_ofn_offset 49 /* Open function */
+#define SMB_openx_als_offset 51 /* Space to allocate on */
+#define SMB_openx_res_offset 55 /* reserved */
+#define SMB_openx_bcc_offset 63 /* bcc */
+#define SMB_openx_buf_offset 65 /* Where file name goes */
+#define SMB_openx_len 65
+
+#define SMB_openr_fid_offset 33 /* FID returned */
+#define SMB_openr_atr_offset 35 /* Attributes opened with */
+#define SMB_openr_tim_offset 37 /* Last mod time of file */
+#define SMB_openr_fsz_offset 41 /* File size 4 bytes */
+#define SMB_openr_acc_offset 45 /* Access allowed */
+#define SMB_openr_bcc_offset 47
+#define SMB_openr_len 49
+
+#define SMB_openxr_axc_offset 33 /* And X command */
+#define SMB_openxr_axr_offset 34 /* reserved */
+#define SMB_openxr_axo_offset 35 /* offset to next command */
+#define SMB_openxr_fid_offset 37 /* FID returned */
+#define SMB_openxr_fat_offset 39 /* File attributes returned */
+#define SMB_openxr_tim_offset 41 /* File creation date etc */
+#define SMB_openxr_fsz_offset 45 /* Size of file */
+#define SMB_openxr_acc_offset 49 /* Access granted */
+
+#define SMB_clos_fid_offset 33 /* FID to close */
+#define SMB_clos_tim_offset 35 /* Last mod time */
+#define SMB_clos_bcc_offset 39 /* bcc */
+#define SMB_clos_len 41
+
+/* Offsets related to Write requests */
+
+#define SMB_write_fid_offset 33 /* FID to write */
+#define SMB_write_cnt_offset 35 /* bytes to write */
+#define SMB_write_ofs_offset 37 /* location to write to */
+#define SMB_write_clf_offset 41 /* advisory count left */
+#define SMB_write_bcc_offset 43 /* bcc = data bytes + 3 */
+#define SMB_write_buf_offset 45 /* Data=0x01, len, data */
+#define SMB_write_len 45 /* plus the data ... */
+
+#define SMB_writr_cnt_offset 33 /* Count of bytes written */
+#define SMB_writr_bcc_offset 35 /* bcc */
+#define SMB_writr_len 37
+
+/* Offsets related to read requests */
+
+#define SMB_read_fid_offset 33 /* FID of file to read */
+#define SMB_read_cnt_offset 35 /* count of words to read */
+#define SMB_read_ofs_offset 37 /* Where to read from */
+#define SMB_read_clf_offset 41 /* Advisory count to go */
+#define SMB_read_bcc_offset 43
+#define SMB_read_len 45
+
+#define SMB_readr_cnt_offset 33 /* Count of bytes returned */
+#define SMB_readr_res_offset 35 /* 4 shorts reserved, 8 bytes */
+#define SMB_readr_bcc_offset 43 /* bcc */
+#define SMB_readr_bff_offset 45 /* buffer format char = 0x01 */
+#define SMB_readr_len_offset 46 /* buffer len */
+#define SMB_readr_len 45 /* length of the readr before data */
+
+/* Offsets for Create file */
+
+#define SMB_creat_atr_offset 33 /* Attributes of new file ... */
+#define SMB_creat_tim_offset 35 /* Time of creation */
+#define SMB_creat_dat_offset 37 /* 4004BCE :-) */
+#define SMB_creat_bcc_offset 39 /* bcc */
+#define SMB_creat_buf_offset 41
+#define SMB_creat_len 41 /* Before the data */
+
+#define SMB_creatr_fid_offset 33 /* FID of created file */
+
+/* Offsets for Delete file */
+
+#define SMB_delet_sat_offset 33 /* search attribites */
+#define SMB_delet_bcc_offset 35 /* bcc */
+#define SMB_delet_buf_offset 37
+#define SMB_delet_len 37
+
+/* Offsets for SESSION_SETUP_ANDX for both LM and NT LM protocols */
+
+#define SMB_ssetpLM_mbs_offset 37 /* Max buffer Size, allow for AndX */
+#define SMB_ssetpLM_mmc_offset 39 /* max multiplex count */
+#define SMB_ssetpLM_vcn_offset 41 /* VC number if new VC */
+#define SMB_ssetpLM_snk_offset 43 /* Session Key */
+#define SMB_ssetpLM_pwl_offset 47 /* password length */
+#define SMB_ssetpLM_res_offset 49 /* reserved */
+#define SMB_ssetpLM_bcc_offset 53 /* bcc */
+#define SMB_ssetpLM_len 55 /* before data ... */
+#define SMB_ssetpLM_buf_offset 55
+
+#define SMB_ssetpNTLM_mbs_offset 37 /* Max Buffer Size for NT LM 0.12 */
+ /* and above */
+#define SMB_ssetpNTLM_mmc_offset 39 /* Max Multiplex count */
+#define SMB_ssetpNTLM_vcn_offset 41 /* VC Number */
+#define SMB_ssetpNTLM_snk_offset 43 /* Session key */
+#define SMB_ssetpNTLM_cipl_offset 47 /* Case Insensitive PW Len */
+#define SMB_ssetpNTLM_cspl_offset 49 /* Unicode pw len */
+#define SMB_ssetpNTLM_res_offset 51 /* reserved */
+#define SMB_ssetpNTLM_cap_offset 55 /* server capabilities */
+#define SMB_ssetpNTLM_bcc_offset 59 /* bcc */
+#define SMB_ssetpNTLM_len 61 /* before data */
+#define SMB_ssetpNTLM_buf_offset 61
+
+#define SMB_ssetpr_axo_offset 35 /* Offset of next response ... */
+#define SMB_ssetpr_act_offset 37 /* action, bit 0 = 1 => guest */
+#define SMB_ssetpr_bcc_offset 39 /* bcc */
+#define SMB_ssetpr_buf_offset 41 /* Native OS etc */
+
+/* Offsets for SMB create directory */
+
+#define SMB_creatdir_bcc_offset 33 /* only a bcc here */
+#define SMB_creatdir_buf_offset 35 /* Where things start */
+#define SMB_creatdir_len 35
+
+/* Offsets for SMB delete directory */
+
+#define SMB_deletdir_bcc_offset 33 /* only a bcc here */
+#define SMB_deletdir_buf_offset 35 /* where things start */
+#define SMB_deletdir_len 35
+
+/* Offsets for SMB check directory */
+
+#define SMB_checkdir_bcc_offset 33 /* Only a bcc here */
+#define SMB_checkdir_buf_offset 35 /* where things start */
+#define SMB_checkdir_len 35
+
+/* Offsets for SMB search */
+
+#define SMB_search_mdc_offset 33 /* Max Dir ents to return */
+#define SMB_search_atr_offset 35 /* Search attributes */
+#define SMB_search_bcc_offset 37 /* bcc */
+#define SMB_search_buf_offset 39 /* where the action is */
+#define SMB_search_len 39
+
+#define SMB_searchr_dec_offset 33 /* Dir ents returned */
+#define SMB_searchr_bcc_offset 35 /* bcc */
+#define SMB_searchr_buf_offset 37 /* Where the action starts */
+#define SMB_searchr_len 37 /* before the dir ents */
+
+#define SMB_searchr_dirent_len 43 /* 53 bytes */
+
+/* Defines for SMB transact and transact2 calls */
+
+#define SMB_trans_tpc_offset 33 /* Total param count */
+#define SMB_trans_tdc_offset 35 /* total Data count */
+#define SMB_trans_mpc_offset 37 /* Max params bytes to return */
+#define SMB_trans_mdc_offset 39 /* Max data bytes to return */
+#define SMB_trans_msc_offset 41 /* Max setup words to return */
+#define SMB_trans_rs1_offset 42 /* Reserved byte */
+#define SMB_trans_flg_offset 43 /* flags */
+#define SMB_trans_tmo_offset 45 /* Timeout, long */
+#define SMB_trans_rs2_offset 49 /* Next reserved */
+#define SMB_trans_pbc_offset 51 /* Param Byte count in buf */
+#define SMB_trans_pbo_offset 53 /* Offset to param bytes */
+#define SMB_trans_dbc_offset 55 /* Data byte count in buf */
+#define SMB_trans_dbo_offset 57 /* Data byte offset */
+#define SMB_trans_suc_offset 59 /* Setup count - byte */
+#define SMB_trans_rs3_offset 60 /* Reserved to pad ... */
+#define SMB_trans_len 61 /* Up to setup, still need bcc */
+
+#define SMB_transr_tpc_offset 33 /* Total param bytes returned */
+#define SMB_transr_tdc_offset 35
+#define SMB_transr_rs1_offset 37
+#define SMB_transr_pbc_offset 39
+#define SMB_transr_pbo_offset 41
+#define SMB_transr_pdi_offset 43 /* parameter displacement */
+#define SMB_transr_dbc_offset 45
+#define SMB_transr_dbo_offset 47
+#define SMB_transr_ddi_offset 49
+#define SMB_transr_suc_offset 51
+#define SMB_transr_rs2_offset 52
+#define SMB_transr_len 53
+
+/* Bit masks for SMB Capabilities ... */
+
+#define SMB_cap_raw_mode 0x0001
+#define SMB_cap_mpx_mode 0x0002
+#define SMB_cap_unicode 0x0004
+#define SMB_cap_large_files 0x0008
+#define SMB_cap_nt_smbs 0x0010
+#define SMB_rpc_remote_apis 0x0020
+#define SMB_cap_nt_status 0x0040
+#define SMB_cap_level_II_oplocks 0x0080
+#define SMB_cap_lock_and_read 0x0100
+#define SMB_cap_nt_find 0x0200
+
+/* SMB LANMAN api call defines */
+
+#define SMB_LMapi_SetUserInfo 0x0072
+#define SMB_LMapi_UserPasswordSet 0x0073
+
+/* Structures and defines we use in the client interface */
+
+/* The protocols we might support. Perhaps a bit ambitious, as only RFCNB */
+/* has any support so far 0(sometimes called NBT) */
+
+typedef enum {
+ SMB_RFCNB, SMB_IPXNB, SMB_NETBEUI, SMB_X25
+} SMB_Transport_Types;
+
+typedef enum {
+ SMB_Con_FShare, SMB_Con_PShare, SMB_Con_IPC
+} SMB_Con_Types;
+
+typedef enum {
+ SMB_State_NoState, SMB_State_Stopped, SMB_State_Started
+} SMB_State_Types;
+
+/* The following two arrays need to be in step! */
+/* We must make it possible for callers to specify these ... */
+
+
+extern char *SMB_Prots[];
+
+/*
+ * static char *SMB_Prots[] = {"PC NETWORK PROGRAM 1.0",
+ * "MICROSOFT NETWORKS 1.03",
+ * "MICROSOFT NETWORKS 3.0",
+ * "DOS LANMAN1.0",
+ * "LANMAN1.0",
+ * "DOS LM1.2X002",
+ * "LM1.2X002",
+ * "DOS LANMAN2.1",
+ * "LANMAN2.1",
+ * "Samba",
+ * "NT LM 0.12",
+ * "NT LANMAN 1.0",
+ * NULL};
+ */
+extern int SMB_Types[];
+
+/*
+ * static int SMB_Types[] = {SMB_P_Core,
+ * SMB_P_CorePlus,
+ * SMB_P_DOSLanMan1,
+ * SMB_P_DOSLanMan1,
+ * SMB_P_LanMan1,
+ * SMB_P_DOSLanMan2,
+ * SMB_P_LanMan2,
+ * SMB_P_LanMan2_1,
+ * SMB_P_LanMan2_1,
+ * SMB_P_NT1,
+ * SMB_P_NT1,
+ * SMB_P_NT1,
+ * -1};
+ */
+typedef struct SMB_Status {
+
+ union {
+ struct {
+ unsigned char ErrorClass;
+ unsigned char Reserved;
+ unsigned short Error;
+ } DosError;
+ unsigned int NtStatus;
+ } status;
+} SMB_Status;
+
+typedef struct SMB_Tree_Structure *SMB_Tree_Handle;
+
+typedef struct SMB_Connect_Def *SMB_Handle_Type;
+
+struct SMB_Connect_Def {
+
+ SMB_Handle_Type Next_Con, Prev_Con; /* Next and previous conn */
+ int protocol; /* What is the protocol */
+ int prot_IDX; /* And what is the index */
+ void *Trans_Connect; /* The connection */
+
+ /* All these strings should be malloc'd */
+
+ char service[80], username[80], password[80], desthost[80], sock_options[80];
+ char address[80], myname[80];
+
+ SMB_Tree_Handle first_tree, last_tree; /* List of trees on this server */
+
+ int gid; /* Group ID, do we need it? */
+ int mid; /* Multiplex ID? We might need one per con */
+ int pid; /* Process ID */
+
+ int uid; /* Authenticated user id. */
+
+ /* It is pretty clear that we need to bust some of */
+ /* these out into a per TCon record, as there may */
+ /* be multiple TCon's per server, etc ... later */
+
+ int port; /* port to use in case not default, this is a TCPism! */
+
+ int max_xmit; /* Max xmit permitted by server */
+ int Security; /* 0 = share, 1 = user */
+ int Raw_Support; /* bit 0 = 1 = Read Raw supported, 1 = 1 Write raw */
+ BOOL encrypt_passwords; /* FALSE = don't */
+ int MaxMPX, MaxVC, MaxRaw;
+ unsigned int SessionKey, Capabilities;
+ int SvrTZ; /* Server Time Zone */
+ int Encrypt_Key_Len;
+ char Encrypt_Key[80], Domain[80], PDomain[80], OSName[80], LMType[40];
+ char Svr_OS[80], Svr_LMType[80], Svr_PDom[80];
+
+};
+
+#ifndef SMBLIB_DEFAULT_DOMAIN
+#define SMBLIB_DEFAULT_DOMAIN "STAFF"
+#endif
+#define SMBLIB_DEFAULT_OSNAME "UNIX of some type"
+#define SMBLIB_DEFAULT_LMTYPE "SMBlib LM2.1 minus a bit"
+#define SMBLIB_MAX_XMIT 65535
+
+#define SMB_Sec_Mode_Share 0
+#define SMB_Sec_Mode_User 1
+
+/* A Tree_Structure */
+
+struct SMB_Tree_Structure {
+
+ SMB_Tree_Handle next, prev;
+ SMB_Handle_Type con;
+ char path[129];
+ char device_type[20];
+ int mbs; /* Local MBS */
+ int tid;
+
+};
+
+typedef struct SMB_File_Def SMB_File;
+
+struct SMB_File_Def {
+
+ SMB_Tree_Handle tree;
+ char filename[256]; /* We should malloc this ... */
+ UWORD fid;
+ unsigned int lastmod;
+ unsigned int size; /* Could blow up if 64bit files supported */
+ UWORD access;
+ off_t fileloc;
+
+};
+
+/* global Variables for the library */
+
+extern SMB_State_Types SMBlib_State;
+
+#ifndef SMBLIB_ERRNO
+extern int SMBlib_errno;
+extern int SMBlib_SMB_Error; /* last Error */
+#endif
+
+SMB_Tree_Handle SMB_TreeConnect(SMB_Handle_Type con, SMB_Tree_Handle tree,
+ char *path, char *password, char *dev);
+
+int SMB_Init();
+void SMB_Get_My_Name(char *name, int len);
+int SMB_Negotiate(SMB_Handle_Type Con_Handle, char *Prots[]);
+int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle);
+
+int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
+ char *PassWord, char *UserDomain, int precrypted);
+
+int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
+
+int SMB_Get_Last_Error();
+
+#endif /* __SMBLIB_PRIV_H__ */
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Utility Routines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "smblib-priv.h"
+#include <malloc.h>
+#include <string.h>
+
+#include "rfcnb.h"
+
+/* global data structures */
+
+static int SMB_Types[] =
+{SMB_P_Core,
+ SMB_P_CorePlus,
+ SMB_P_DOSLanMan1,
+ SMB_P_DOSLanMan1,
+ SMB_P_LanMan1,
+ SMB_P_DOSLanMan2,
+ SMB_P_LanMan2,
+ SMB_P_LanMan2_1,
+ SMB_P_LanMan2_1,
+ SMB_P_NT1,
+ SMB_P_NT1,
+ SMB_P_NT1,
+ -1};
+
+static char *SMB_Prots[] =
+{"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "DOS LANMAN1.0",
+ "LANMAN1.0",
+ "DOS LM1.2X002",
+ "LM1.2X002",
+ "DOS LANMAN2.1",
+ "LANMAN2.1",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+
+/* Print out an SMB pkt in all its gory detail ... */
+
+void
+SMB_Print_Pkt(FILE fd, RFCNB_Pkt * pkt, BOOL command, int Offset, int Len)
+{
+
+ /* Well, just how do we do this ... print it I suppose */
+
+ /* Print out the SMB header ... */
+
+ /* Print the command */
+
+ /* Print the other bits in the header */
+
+
+ /* etc */
+
+}
+
+/* Convert a DOS Date_Time to a local host type date time for printing */
+
+char *
+SMB_DOSTimToStr(int DOS_time)
+{
+ static char SMB_Time_Temp[48];
+ int DOS_sec, DOS_min, DOS_hour, DOS_day, DOS_month, DOS_year;
+
+ SMB_Time_Temp[0] = 0;
+
+ DOS_sec = (DOS_time & 0x001F) * 2;
+ DOS_min = (DOS_time & 0x07E0) >> 5;
+ DOS_hour = ((DOS_time & 0xF800) >> 11);
+
+ DOS_day = (DOS_time & 0x001F0000) >> 16;
+ DOS_month = (DOS_time & 0x01E00000) >> 21;
+ DOS_year = ((DOS_time & 0xFE000000) >> 25) + 80;
+
+ sprintf(SMB_Time_Temp, "%2d/%02d/%2d %2d:%02d:%02d", DOS_day, DOS_month,
+ DOS_year, DOS_hour, DOS_min, DOS_sec);
+
+ return (SMB_Time_Temp);
+
+}
+
+/* Convert an attribute byte/word etc to a string ... We return a pointer
+ * to a static string which we guarantee is long enough. If verbose is
+ * true, we print out long form of strings ... */
+
+char *
+SMB_AtrToStr(int attribs, BOOL verbose)
+{
+ static char SMB_Attrib_Temp[128];
+
+ SMB_Attrib_Temp[0] = 0;
+
+ if (attribs & SMB_FA_ROF)
+ strcat(SMB_Attrib_Temp, (verbose ? "Read Only " : "R"));
+
+ if (attribs & SMB_FA_HID)
+ strcat(SMB_Attrib_Temp, (verbose ? "Hidden " : "H"));
+
+ if (attribs & SMB_FA_SYS)
+ strcat(SMB_Attrib_Temp, (verbose ? "System " : "S"));
+
+ if (attribs & SMB_FA_VOL)
+ strcat(SMB_Attrib_Temp, (verbose ? "Volume " : "V"));
+
+ if (attribs & SMB_FA_DIR)
+ strcat(SMB_Attrib_Temp, (verbose ? "Directory " : "D"));
+
+ if (attribs & SMB_FA_ARC)
+ strcat(SMB_Attrib_Temp, (verbose ? "Archive " : "A"));
+
+ return (SMB_Attrib_Temp);
+
+}
+
+/* Pick up the Max Buffer Size from the Tree Structure ... */
+
+int
+SMB_Get_Tree_MBS(SMB_Tree_Handle tree)
+{
+ if (tree != NULL) {
+ return (tree->mbs);
+ } else {
+ return (SMBlibE_BAD);
+ }
+}
+
+/* Pick up the Max buffer size */
+
+int
+SMB_Get_Max_Buf_Siz(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->max_xmit);
+ } else {
+ return (SMBlibE_BAD);
+ }
+
+}
+/* Pickup the protocol index from the connection structure */
+
+int
+SMB_Get_Protocol_IDX(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->prot_IDX);
+ } else {
+ return (0xFFFF); /* Invalid protocol */
+ }
+
+}
+
+/* Pick up the protocol from the connection structure */
+
+int
+SMB_Get_Protocol(SMB_Handle_Type Con_Handle)
+{
+ if (Con_Handle != NULL) {
+ return (Con_Handle->protocol);
+ } else {
+ return (0xFFFF); /* Invalid protocol */
+ }
+
+}
+
+/* Figure out what protocol was accepted, given the list of dialect strings */
+/* We offered, and the index back from the server. We allow for a user */
+/* supplied list, and assume that it is a subset of our list */
+
+int
+SMB_Figure_Protocol(char *dialects[], int prot_index)
+{
+ int i;
+
+ if (dialects == SMB_Prots) { /* The jobs is easy, just index into table */
+
+ return (SMB_Types[prot_index]);
+ } else { /* Search through SMB_Prots looking for a match */
+
+ for (i = 0; SMB_Prots[i] != NULL; i++) {
+
+ if (strcmp(dialects[prot_index], SMB_Prots[i]) == 0) { /* A match */
+
+ return (SMB_Types[i]);
+
+ }
+ }
+
+ /* If we got here, then we are in trouble, because the protocol was not */
+ /* One we understand ... */
+
+ return (SMB_P_Unknown);
+
+ }
+
+}
+
+
+/* Negotiate the protocol we will use from the list passed in Prots */
+/* we return the index of the accepted protocol in NegProt, -1 indicates */
+/* none acceptible, and our return value is 0 if ok, <0 if problems */
+
+int
+SMB_Negotiate(SMB_Handle_Type Con_Handle, char *Prots[])
+{
+ struct RFCNB_Pkt *pkt;
+ int prots_len, i, pkt_len, prot, alloc_len;
+ char *p;
+
+ /* Figure out how long the prot list will be and allocate space for it */
+
+ prots_len = 0;
+
+ for (i = 0; Prots[i] != NULL; i++) {
+
+ prots_len = prots_len + strlen(Prots[i]) + 2; /* Account for null etc */
+
+ }
+
+ /* The -1 accounts for the one byte smb_buf we have because some systems */
+ /* don't like char msg_buf[] */
+
+ pkt_len = SMB_negp_len + prots_len;
+
+ /* Make sure that the pkt len is long enough for the max response ... */
+ /* Which is a problem, because the encryption key len eec may be long */
+
+ if (pkt_len < (SMB_hdr_wct_offset + (19 * 2) + 40)) {
+
+ alloc_len = SMB_hdr_wct_offset + (19 * 2) + 40;
+
+ } else {
+
+ alloc_len = pkt_len;
+
+ }
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(alloc_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now plug in the bits we need */
+
+ bzero(SMB_Hdr(pkt), SMB_negp_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBnegprot;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_negp_bcc_offset, prots_len);
+
+ /* Now copy the prot strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_negp_buf_offset);
+
+ for (i = 0; Prots[i] != NULL; i++) {
+
+ *p = SMBdialectID;
+ strcpy(p + 1, Prots[i]);
+ p = p + strlen(Prots[i]) + 2; /* Adjust len of p for null plus dialectID */
+
+ }
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending negotiate protocol\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed; /* Failed, check lower layer errno */
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, alloc_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to negotiate\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed; /* Failed, check lower layer errno */
+ return (SMBlibE_BAD);
+
+ }
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_Negotiate failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+ if (SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset) == 0xFFFF) {
+
+#ifdef DEBUG
+ fprintf(stderr, "None of our protocols was accepted ... ");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_NegNoProt;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now, unpack the info from the response, if any and evaluate the proto */
+ /* selected. We must make sure it is one we like ... */
+
+ Con_Handle->prot_IDX = prot = SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset);
+ Con_Handle->protocol = SMB_Figure_Protocol(Prots, prot);
+
+ if (Con_Handle->protocol == SMB_P_Unknown) { /* No good ... */
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_ProtUnknown;
+ return (SMBlibE_BAD);
+
+ }
+ switch (CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset)) {
+
+ case 0x01: /* No more info ... */
+
+ break;
+
+ case 13: /* Up to and including LanMan 2.1 */
+
+ Con_Handle->Security = SVAL(SMB_Hdr(pkt), SMB_negrLM_sec_offset);
+ Con_Handle->encrypt_passwords = ((Con_Handle->Security & SMB_sec_encrypt_mask) != 0x00);
+ Con_Handle->Security = Con_Handle->Security & SMB_sec_user_mask;
+
+ Con_Handle->max_xmit = SVAL(SMB_Hdr(pkt), SMB_negrLM_mbs_offset);
+ Con_Handle->MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrLM_mmc_offset);
+ Con_Handle->MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrLM_mnv_offset);
+ Con_Handle->Raw_Support = SVAL(SMB_Hdr(pkt), SMB_negrLM_rm_offset);
+ Con_Handle->SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrLM_sk_offset);
+ Con_Handle->SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrLM_stz_offset);
+ Con_Handle->Encrypt_Key_Len = SVAL(SMB_Hdr(pkt), SMB_negrLM_ekl_offset);
+
+ p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset);
+ fprintf(stderr, "%8s", (char *) (SMB_Hdr(pkt) + SMB_negrLM_buf_offset));
+ memcpy(Con_Handle->Encrypt_Key, p, 8);
+
+ p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset + Con_Handle->Encrypt_Key_Len);
+
+ strncpy(p, Con_Handle->Svr_PDom, sizeof(Con_Handle->Svr_PDom) - 1);
+
+ break;
+
+ case 17: /* NT LM 0.12 and LN LM 1.0 */
+
+ Con_Handle->Security = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_sec_offset);
+ Con_Handle->encrypt_passwords = ((Con_Handle->Security & SMB_sec_encrypt_mask) != 0x00);
+ Con_Handle->Security = Con_Handle->Security & SMB_sec_user_mask;
+
+ Con_Handle->max_xmit = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mbs_offset);
+ Con_Handle->MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mmc_offset);
+ Con_Handle->MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mnv_offset);
+ Con_Handle->MaxRaw = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mrs_offset);
+ Con_Handle->SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_sk_offset);
+ Con_Handle->SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_stz_offset);
+ Con_Handle->Encrypt_Key_Len = CVAL(SMB_Hdr(pkt), SMB_negrNTLM_ekl_offset);
+
+ p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset);
+ memcpy(Con_Handle->Encrypt_Key, p, 8);
+ p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset + Con_Handle->Encrypt_Key_Len);
+
+ strncpy(p, Con_Handle->Svr_PDom, sizeof(Con_Handle->Svr_PDom) - 1);
+
+ break;
+
+ default:
+
+#ifdef DEBUG
+ fprintf(stderr, "Unknown NegProt response format ... Ignored\n");
+ fprintf(stderr, " wct = %i\n", CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset));
+#endif
+
+ break;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Protocol selected is: %i:%s\n", prot, Prots[prot]);
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ return (0);
+
+}
+
+/* Get our hostname */
+
+void
+SMB_Get_My_Name(char *name, int len)
+{
+
+ if (gethostname(name, len) < 0) { /* Error getting name */
+
+ strncpy(name, "unknown", len);
+
+ /* Should check the error */
+
+#ifdef DEBUG
+ fprintf(stderr, "gethostname in SMB_Get_My_Name returned error:");
+ perror("");
+#endif
+
+ }
+ /* only keep the portion up to the first "." */
+
+
+}
+
+/* Send a TCON to the remote server ... */
+
+SMB_Tree_Handle
+SMB_TreeConnect(SMB_Handle_Type Con_Handle,
+ SMB_Tree_Handle Tree_Handle,
+ char *path,
+ char *password,
+ char *device)
+{
+ struct RFCNB_Pkt *pkt;
+ int param_len, pkt_len;
+ char *p;
+ SMB_Tree_Handle tree;
+
+ /* Figure out how much space is needed for path, password, dev ... */
+
+ if ((path == NULL) || (password == NULL) || (device == NULL)) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Bad parameter passed to SMB_TreeConnect\n");
+#endif
+
+ SMBlib_errno = SMBlibE_BadParam;
+ return (NULL);
+
+ }
+ /* The + 2 is because of the \0 and the marker ... */
+
+ param_len = strlen(path) + 2 + strlen(password) + 2 + strlen(device) + 2;
+
+ /* The -1 accounts for the one byte smb_buf we have because some systems */
+ /* don't like char msg_buf[] */
+
+ pkt_len = SMB_tcon_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (NULL); /* Should handle the error */
+
+ }
+ /* Now allocate a tree for this to go into ... */
+
+ if (Tree_Handle == NULL) {
+
+ tree = (SMB_Tree_Handle) malloc(sizeof(struct SMB_Tree_Structure));
+
+ if (tree == NULL) {
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (NULL);
+
+ }
+ } else {
+
+ tree = Tree_Handle;
+
+ }
+
+ tree->next = tree->prev = NULL;
+ tree->con = Con_Handle;
+ strncpy(tree->path, path, sizeof(tree->path));
+ strncpy(tree->device_type, device, sizeof(tree->device_type));
+
+ /* Now plug in the values ... */
+
+ bzero(SMB_Hdr(pkt), SMB_tcon_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtcon;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_tcon_buf_offset);
+ *p = SMBasciiID;
+ strcpy(p + 1, path);
+ p = p + strlen(path) + 2;
+ *p = SMBasciiID;
+ strcpy(p + 1, password);
+ p = p + strlen(password) + 2;
+ *p = SMBasciiID;
+ strcpy(p + 1, device);
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending TCon request\n");
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed;
+ return (NULL);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to TCon\n");
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed;
+ return (NULL);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_TCon failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ if (Tree_Handle == NULL)
+ free(tree);
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (NULL);
+
+ }
+ tree->tid = SVAL(SMB_Hdr(pkt), SMB_tconr_tid_offset);
+ tree->mbs = SVAL(SMB_Hdr(pkt), SMB_tconr_mbs_offset);
+
+#ifdef DEBUG
+ fprintf(stderr, "TConn succeeded, with TID=%i, Max Xmit=%i\n",
+ tree->tid, tree->mbs);
+#endif
+
+ /* Now link the Tree to the Server Structure ... */
+
+ if (Con_Handle->first_tree == NULL) {
+
+ Con_Handle->first_tree = tree;
+ Con_Handle->last_tree = tree;
+
+ } else {
+
+ Con_Handle->last_tree->next = tree;
+ tree->prev = Con_Handle->last_tree;
+ Con_Handle->last_tree = tree;
+
+ }
+
+ RFCNB_Free_Pkt(pkt);
+ return (tree);
+
+}
+
+int
+SMB_TreeDisconnect(SMB_Tree_Handle Tree_Handle, BOOL discard)
+{
+ struct RFCNB_Pkt *pkt;
+ int pkt_len;
+
+ pkt_len = SMB_tdis_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return (SMBlibE_BAD); /* Should handle the error */
+
+ }
+ /* Now plug in the values ... */
+
+ bzero(SMB_Hdr(pkt), SMB_tdis_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtdis;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Tree_Handle->con->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Tree_Handle->con->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Tree_Handle->con->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
+
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, Tree_Handle->tid);
+ SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, 0);
+
+ /* Now send the packet and sit back ... */
+
+ if (RFCNB_Send(Tree_Handle->con->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending TDis request\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_SendFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Tree_Handle->con->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to TCon\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = -SMBlibE_RecvFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_TDis failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+ Tree_Handle->tid = 0xFFFF; /* Invalid TID */
+ Tree_Handle->mbs = 0; /* Invalid */
+
+#ifdef DEBUG
+
+ fprintf(stderr, "Tree disconnect successful ...\n");
+
+#endif
+
+ /* What about the tree handle ? */
+
+ if (discard == TRUE) { /* Unlink it and free it ... */
+
+ if (Tree_Handle->next == NULL)
+ Tree_Handle->con->first_tree = Tree_Handle->prev;
+ else
+ Tree_Handle->next->prev = Tree_Handle->prev;
+
+ if (Tree_Handle->prev == NULL)
+ Tree_Handle->con->last_tree = Tree_Handle->next;
+ else
+ Tree_Handle->prev->next = Tree_Handle->next;
+
+ }
+ RFCNB_Free_Pkt(pkt);
+ return (0);
+
+}
+
+/* Pick up the last LMBlib error ... */
+
+int
+SMB_Get_Last_Error()
+{
+
+ return (SMBlib_errno);
+
+}
+
+/* Pick up the last error returned in an SMB packet */
+/* We will need macros to extract error class and error code */
+
+int
+SMB_Get_Last_SMB_Err()
+{
+
+ return (SMBlib_SMB_Error);
+
+}
+
+/* Pick up the error message associated with an error from SMBlib */
+
+/* Keep this table in sync with the message codes in smblib-common.h */
+
+static char *SMBlib_Error_Messages[] =
+{
+
+ "Request completed sucessfully.",
+ "Server returned a non-zero SMB Error Class and Code.",
+ "A lower layer protocol error occurred.",
+ "Function not yet implemented.",
+ "The protocol negotiated does not support the request.",
+ "No space available for operation.",
+ "One or more bad parameters passed.",
+ "None of the protocols we offered were accepted.",
+ "The attempt to send an SMB request failed. See protocol error info.",
+ "The attempt to get an SMB response failed. See protocol error info.",
+ "The logon request failed, but you were logged in as guest.",
+ "The attempt to call the remote server failed. See protocol error info.",
+ "The protocol dialect specified in a NegProt and accepted by the server is unknown.",
+ /* This next one simplifies error handling */
+ "No such error code.",
+ NULL};
+
+int
+SMB_Get_Error_Msg(int msg, char *msgbuf, int len)
+{
+
+ if (msg >= 0) {
+
+ strncpy(msgbuf,
+ SMBlib_Error_Messages[msg > SMBlibE_NoSuchMsg ? SMBlibE_NoSuchMsg : msg],
+ len - 1);
+ msgbuf[len - 1] = 0; /* Make sure it is a string */
+ } else { /* Add the lower layer message ... */
+
+ char prot_msg[1024];
+
+ msg = -msg; /* Make it positive */
+
+ strncpy(msgbuf,
+ SMBlib_Error_Messages[msg > SMBlibE_NoSuchMsg ? SMBlibE_NoSuchMsg : msg],
+ len - 1);
+
+ msgbuf[len - 1] = 0; /* make sure it is a string */
+
+ if (strlen(msgbuf) < len) { /* If there is space, put rest in */
+
+ strncat(msgbuf, "\n\t", len - strlen(msgbuf));
+
+ RFCNB_Get_Error(prot_msg, sizeof(prot_msg) - 1);
+
+ strncat(msgbuf, prot_msg, len - strlen(msgbuf));
+
+ }
+ }
+ return 0;
+}
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Routines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "config.h"
+#include <malloc.h>
+#include <ctype.h>
+#include <string.h>
+
+int SMBlib_errno;
+int SMBlib_SMB_Error;
+#define SMBLIB_ERRNO
+#define uchar unsigned char
+#include "smblib-priv.h"
+
+#include "rfcnb.h"
+#include "smbencrypt.h"
+
+#include <signal.h>
+
+#define DEBUG
+
+SMB_State_Types SMBlib_State;
+
+/* Initialize the SMBlib package */
+
+int
+SMB_Init()
+{
+
+ SMBlib_State = SMB_State_Started;
+
+ signal(SIGPIPE, SIG_IGN); /* Ignore these ... */
+
+/* If SMBLIB_Instrument is defines, turn on the instrumentation stuff */
+#ifdef SMBLIB_INSTRUMENT
+
+ SMBlib_Instrument_Init();
+
+#endif
+
+ return 0;
+
+}
+
+int
+SMB_Term()
+{
+
+#ifdef SMBLIB_INSTRUMENT
+
+ SMBlib_Instrument_Term(); /* Clean up and print results */
+
+#endif
+
+ return 0;
+
+}
+
+/* SMB_Create: Create a connection structure and return for later use */
+/* We have other helper routines to set variables */
+
+SMB_Handle_Type
+SMB_Create_Con_Handle()
+{
+
+ SMBlib_errno = SMBlibE_NotImpl;
+ return (NULL);
+
+}
+
+int
+SMBlib_Set_Sock_NoDelay(SMB_Handle_Type Con_Handle, BOOL yn)
+{
+
+
+ if (RFCNB_Set_Sock_NoDelay(Con_Handle->Trans_Connect, yn) < 0) {
+
+#ifdef DEBUG
+#endif
+
+ fprintf(stderr, "Setting no-delay on TCP socket failed ...\n");
+
+ }
+ return (0);
+
+}
+
+/* SMB_Connect_Server: Connect to a server, but don't negotiate protocol */
+/* or anything else ... */
+
+SMB_Handle_Type
+SMB_Connect_Server(SMB_Handle_Type Con_Handle,
+ char *server, char *NTdomain)
+{
+ SMB_Handle_Type con;
+ char called[80], calling[80], *address;
+ int i;
+
+ /* Get a connection structure if one does not exist */
+
+ con = Con_Handle;
+
+ if (Con_Handle == NULL) {
+
+ if ((con = (struct SMB_Connect_Def *) malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
+
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return NULL;
+ }
+ }
+ /* Init some things ... */
+
+ strcpy(con->service, "");
+ strcpy(con->username, "");
+ strcpy(con->password, "");
+ strcpy(con->sock_options, "");
+ strcpy(con->address, "");
+ strcpy(con->desthost, server);
+ strcpy(con->PDomain, NTdomain);
+ strcpy(con->OSName, SMBLIB_DEFAULT_OSNAME);
+ strcpy(con->LMType, SMBLIB_DEFAULT_LMTYPE);
+ con->first_tree = con->last_tree = NULL;
+
+ /* ugh. This is horribly broken. */
+/* SMB_Get_My_Name(con -> myname, sizeof(con -> myname)); */
+ /* hacked by Kinkie */
+ i = gethostname(con->myname, sizeof(con->myname));
+ if (i == -1) {
+ strcpy(con->myname, "unknown");
+ } else {
+ if (NULL != (address = strchr(con->myname, '.'))) {
+ *address = '\0'; /* truncate at first '.' */
+ }
+ }
+
+
+ con->port = 0; /* No port selected */
+
+ /* Get some things we need for the SMB Header */
+
+ con->pid = getpid();
+ con->mid = con->pid; /* This will do for now ... */
+ con->uid = 0; /* Until we have done a logon, no uid ... */
+ con->gid = getgid();
+
+ /* Now connect to the remote end, but first upper case the name of the
+ * service we are going to call, sine some servers want it in uppercase */
+
+ for (i = 0; i < strlen(server); i++)
+ called[i] = toupper(server[i]);
+
+ called[strlen(server)] = 0; /* Make it a string */
+
+ for (i = 0; i < strlen(con->myname); i++)
+ calling[i] = toupper(con->myname[i]);
+
+ calling[strlen(con->myname)] = 0; /* Make it a string */
+
+ if (strcmp(con->address, "") == 0)
+ address = con->desthost;
+ else
+ address = con->address;
+
+ con->Trans_Connect = RFCNB_Call(called,
+ calling,
+ address, /* Protocol specific */
+ con->port);
+
+ /* Did we get one? */
+
+ if (con->Trans_Connect == NULL) {
+
+ if (Con_Handle == NULL) {
+ Con_Handle = NULL;
+ free(con);
+ }
+ SMBlib_errno = -SMBlibE_CallFailed;
+ return NULL;
+
+ }
+ return (con);
+
+}
+
+/* SMB_Connect: Connect to the indicated server */
+/* If Con_Handle == NULL then create a handle and connect, otherwise */
+/* use the handle passed */
+
+char *SMB_Prots_Restrict[] =
+{"PC NETWORK PROGRAM 1.0",
+ NULL};
+
+
+SMB_Handle_Type
+SMB_Connect(SMB_Handle_Type Con_Handle,
+ SMB_Tree_Handle * tree,
+ char *service,
+ char *username,
+ char *password)
+{
+ SMB_Handle_Type con;
+ char *host, *address;
+ char temp[80], called[80], calling[80];
+ int i;
+
+ /* Get a connection structure if one does not exist */
+
+ con = Con_Handle;
+
+ if (Con_Handle == NULL) {
+
+ if ((con = (struct SMB_Connect_Def *) malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ return NULL;
+ }
+ }
+ /* Init some things ... */
+
+ strcpy(con->service, service);
+ strcpy(con->username, username);
+ strcpy(con->password, password);
+ strcpy(con->sock_options, "");
+ strcpy(con->address, "");
+ strcpy(con->PDomain, SMBLIB_DEFAULT_DOMAIN);
+ strcpy(con->OSName, SMBLIB_DEFAULT_OSNAME);
+ strcpy(con->LMType, SMBLIB_DEFAULT_LMTYPE);
+ con->first_tree = con->last_tree = NULL;
+
+ SMB_Get_My_Name(con->myname, sizeof(con->myname));
+
+ con->port = 0; /* No port selected */
+
+ /* Get some things we need for the SMB Header */
+
+ con->pid = getpid();
+ con->mid = con->pid; /* This will do for now ... */
+ con->uid = 0; /* Until we have done a logon, no uid */
+ con->gid = getgid();
+
+ /* Now figure out the host portion of the service */
+
+ strcpy(temp, service);
+ host = (char *) strtok(temp, "/\\"); /* Separate host name portion */
+ strcpy(con->desthost, host);
+
+ /* Now connect to the remote end, but first upper case the name of the
+ * service we are going to call, sine some servers want it in uppercase */
+
+ for (i = 0; i < strlen(host); i++)
+ called[i] = toupper(host[i]);
+
+ called[strlen(host)] = 0; /* Make it a string */
+
+ for (i = 0; i < strlen(con->myname); i++)
+ calling[i] = toupper(con->myname[i]);
+
+ calling[strlen(con->myname)] = 0; /* Make it a string */
+
+ if (strcmp(con->address, "") == 0)
+ address = con->desthost;
+ else
+ address = con->address;
+
+ con->Trans_Connect = RFCNB_Call(called,
+ calling,
+ address, /* Protocol specific */
+ con->port);
+
+ /* Did we get one? */
+
+ if (con->Trans_Connect == NULL) {
+
+ if (Con_Handle == NULL) {
+ free(con);
+ Con_Handle = NULL;
+ }
+ SMBlib_errno = -SMBlibE_CallFailed;
+ return NULL;
+
+ }
+ /* Now, negotiate the protocol */
+
+ if (SMB_Negotiate(con, SMB_Prots_Restrict) < 0) {
+
+ /* Hmmm what should we do here ... We have a connection, but could not
+ * negotiate ... */
+
+ return NULL;
+
+ }
+ /* Now connect to the service ... */
+
+ if ((*tree = SMB_TreeConnect(con, NULL, service, password, "A:")) == NULL) {
+
+ return NULL;
+
+ }
+ return (con);
+
+}
+
+/* Logon to the server. That is, do a session setup if we can. We do not do */
+/* Unicode yet! */
+
+int
+SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
+ char *PassWord, char *UserDomain, int precrypted)
+{
+ struct RFCNB_Pkt *pkt;
+ int param_len, pkt_len, pass_len;
+ char *p, pword[128];
+
+ /* First we need a packet etc ... but we need to know what protocol has */
+ /* been negotiated to figure out if we can do it and what SMB format to */
+ /* use ... */
+
+ if (Con_Handle->protocol < SMB_P_LanMan1) {
+
+ SMBlib_errno = SMBlibE_ProtLow;
+ return (SMBlibE_BAD);
+
+ }
+ if (precrypted) {
+ pass_len = 24;
+ memcpy(pword, PassWord, 24);
+ } else {
+ strcpy(pword, PassWord);
+ if (Con_Handle->encrypt_passwords) {
+ pass_len = 24;
+ SMBencrypt((uchar *) PassWord, (uchar *) Con_Handle->Encrypt_Key, (uchar *) pword);
+ } else
+ pass_len = strlen(pword);
+ }
+
+ /* Now build the correct structure */
+
+ if (Con_Handle->protocol < SMB_P_NT1) {
+
+ param_len = strlen(UserName) + 1 + pass_len + 1 +
+ strlen(UserDomain) + 1 +
+ strlen(Con_Handle->OSName) + 1;
+
+ pkt_len = SMB_ssetpLM_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ fprintf(stderr, "SMB_Logon_server: Couldn't allocate packet\n");
+ return (SMBlibE_BAD); /* Should handle the error */
+ }
+ bzero(SMB_Hdr(pkt), SMB_ssetpLM_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10;
+ *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
+
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mbs_offset, SMBLIB_MAX_XMIT);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mmc_offset, 2);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_vcn_offset, Con_Handle->pid);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_snk_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_pwl_offset, pass_len + 1);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_res_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_ssetpLM_buf_offset);
+
+ /* Copy in password, then the rest. Password has a null at end */
+
+ memcpy(p, pword, pass_len);
+
+ p = p + pass_len + 1;
+
+ strcpy(p, UserName);
+ p = p + strlen(UserName);
+ *p = 0;
+
+ p = p + 1;
+
+ strcpy(p, UserDomain);
+ p = p + strlen(UserDomain);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->OSName);
+ p = p + strlen(Con_Handle->OSName);
+ *p = 0;
+
+ } else {
+
+ /* We don't admit to UNICODE support ... */
+
+ param_len = strlen(UserName) + 1 + pass_len +
+ strlen(UserDomain) + 1 +
+ strlen(Con_Handle->OSName) + 1 +
+ strlen(Con_Handle->LMType) + 1;
+
+ pkt_len = SMB_ssetpNTLM_len + param_len;
+
+ pkt = (struct RFCNB_Pkt *) RFCNB_Alloc_Pkt(pkt_len);
+
+ if (pkt == NULL) {
+
+ SMBlib_errno = SMBlibE_NoSpace;
+ fprintf(stderr, "SMB_Logon_server: Couldn't allocate packet\n");
+ return (-1); /* Should handle the error */
+ }
+ bzero(SMB_Hdr(pkt), SMB_ssetpNTLM_len);
+ SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
+ *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle->pid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle->mid);
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle->uid);
+ *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13;
+ *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF; /* No extra command */
+ SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
+
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_vcn_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_snk_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cipl_offset, pass_len);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cspl_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_res_offset, 0);
+ SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cap_offset, 0);
+ SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_bcc_offset, param_len);
+
+ /* Now copy the param strings in with the right stuff */
+
+ p = (char *) (SMB_Hdr(pkt) + SMB_ssetpNTLM_buf_offset);
+
+ /* Copy in password, then the rest. Password has no null at end */
+
+ memcpy(p, pword, pass_len);
+
+ p = p + pass_len;
+
+ strcpy(p, UserName);
+ p = p + strlen(UserName);
+ *p = 0;
+
+ p = p + 1;
+
+ strcpy(p, UserDomain);
+ p = p + strlen(UserDomain);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->OSName);
+ p = p + strlen(Con_Handle->OSName);
+ *p = 0;
+ p = p + 1;
+
+ strcpy(p, Con_Handle->LMType);
+ p = p + strlen(Con_Handle->LMType);
+ *p = 0;
+
+ }
+
+ /* Now send it and get a response */
+
+ if (RFCNB_Send(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error sending SessSetupX request\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_SendFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Now get the response ... */
+
+ if (RFCNB_Recv(Con_Handle->Trans_Connect, pkt, pkt_len) < 0) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Error receiving response to SessSetupAndX\n");
+#endif
+
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_RecvFailed;
+ return (SMBlibE_BAD);
+
+ }
+ /* Check out the response type ... */
+
+ if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
+
+#ifdef DEBUG
+ fprintf(stderr, "SMB_SessSetupAndX failed with errorclass = %i, Error Code = %i\n",
+ CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
+ SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
+#endif
+
+ SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
+ RFCNB_Free_Pkt(pkt);
+ SMBlib_errno = SMBlibE_Remote;
+ return (SMBlibE_BAD);
+
+ }
+/** @@@ mdz: check for guest login { **/
+ if (SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset) & 0x1) {
+ /* do we allow guest login? NO! */
+ return (SMBlibE_BAD);
+
+ }
+/** @@@ mdz: } **/
+
+
+#ifdef DEBUG
+ fprintf(stderr, "SessSetupAndX response. Action = %i\n",
+ SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset));
+#endif
+
+ /* Now pick up the UID for future reference ... */
+
+ Con_Handle->uid = SVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset);
+ RFCNB_Free_Pkt(pkt);
+
+ return (0);
+
+}
+
+
+/* Disconnect from the server, and disconnect all tree connects */
+
+int
+SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle)
+{
+
+ /* We just disconnect the connection for now ... */
+ if (Con_Handle != NULL)
+ RFCNB_Hangup(Con_Handle->Trans_Connect);
+
+ if (!KeepHandle)
+ free(Con_Handle);
+
+ return (0);
+
+}
--- /dev/null
+/* UNIX SMBlib NetBIOS implementation
+ *
+ * Version 1.0
+ * SMBlib Defines
+ *
+ * Copyright (C) Richard Sharpe 1996
+ *
+ */
+
+/*
+ * 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 "std-defines.h"
+#include "smblib-common.h"
+
+/* Just define all the entry points */
+
+/* Create a handle to allow us to set/override some parameters ... */
+
+void *SMB_Create_Con_Handle();
+
+/* Connect to a server, but do not do a tree con etc ... */
+
+void *SMB_Connect_Server(void *Con, char *server, char *NTdomain);
+
+/* Connect to a server and give us back a handle. If Con == NULL, create */
+/* The handle and populate it with defaults */
+
+void *SMB_Connect(void *Con, void **tree,
+ char *name, char *User, char *Password);
+
+/* Negotiate a protocol */
+
+int SMB_Negotiate(void *Con_Handle, char *Prots[]);
+
+/* Connect to a tree ... */
+
+void *SMB_TreeConnect(void *con_handle, void *tree_handle,
+ char *path, char *password, char *dev);
+
+/* Disconnect a tree ... */
+
+int SMB_TreeDisconect(void *tree_handle);
+
+/* Open a file */
+
+void *SMB_Open(void *tree_handle,
+ void *file_handle,
+ char *file_name,
+ unsigned short mode,
+ unsigned short search);
+
+/* Close a file */
+
+int SMB_Close(void *file_handle);
+
+/* Disconnect from server. Has flag to specify whether or not we keep the */
+/* handle. */
+
+int SMB_Discon(void *Con, BOOL KeepHandle);
+
+void *SMB_Create(void *Tree_Handle,
+ void *File_Handle,
+ char *file_name,
+ short search);
+
+int SMB_Delete(void *tree, char *file_name, short search);
+
+int SMB_Create_Dir(void *tree, char *dir_name);
+
+int SMB_Delete_Dir(void *tree, char *dir_name);
+
+int SMB_Check_Dir(void *tree, char *dir_name);
+
+int SMB_Get_Last_Error();
+
+int SMB_Get_Last_SMB_Err();
+
+int SMB_Get_Error_Msg(int msg, char *msgbuf, int len);
+
+void *SMB_Logon_And_TCon(void *con, void *tree, char *user, char *pass,
+ char *service, char *st);
+
+
+#define SMBLIB_DEFAULT_DOMAIN "anydom"
--- /dev/null
+#ifndef __STD_DEFINES__
+#define __STD_DEFINES__
+
+/* RFCNB Standard includes ... */
+/*
+ *
+ * SMBlib Standard Includes
+ *
+ * Copyright (C) 1996, Richard Sharpe
+ *
+ * One day we will conditionalize these on OS types ... */
+
+/*
+ * 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.
+ */
+
+#define BOOL int
+typedef short int16;
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <strings.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#endif /* __STD_DEFINES__ */
--- /dev/null
+/* RFCNB Standard includes ... */
+/*
+ *
+ * RFCNB Standard Includes
+ *
+ * Copyright (C) 1996, Richard Sharpe
+ *
+ * One day we will conditionalize these on OS types ... */
+
+/*
+ * 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.
+ */
+
+#define BOOL int
+typedef short int16;
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/* Pick up define for INADDR_NONE */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
--- /dev/null
+#include <sys/types.h>
+#include <unistd.h>
+#include <syslog.h>
+#include "smblib-priv.h"
+#include "valid.h"
+
+SMB_Handle_Type SMB_Connect_Server(void *, char *, char *);
+
+int
+Valid_User(char *USERNAME, char *PASSWORD, char *SERVER, char *BACKUP, char *DOMAIN)
+{
+ int pass_is_precrypted_p = 0;
+ char *SMB_Prots[] =
+ {
+/* "PC NETWORK PROGRAM 1.0", */
+/* "MICROSOFT NETWORKS 1.03", */
+/* "MICROSOFT NETWORKS 3.0", */
+ "LANMAN1.0",
+ "LM1.2X002",
+ "Samba",
+/* "NT LM 0.12", */
+/* "NT LANMAN 1.0", */
+ NULL};
+ SMB_Handle_Type con;
+
+ SMB_Init();
+ con = SMB_Connect_Server(NULL, SERVER, DOMAIN);
+ if (con == NULL) { /* Error ... */
+ con = SMB_Connect_Server(NULL, BACKUP, DOMAIN);
+ if (con == NULL) {
+ return (NTV_SERVER_ERROR);
+ }
+ }
+ if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */
+ SMB_Discon(con, 0);
+ return (NTV_PROTOCOL_ERROR);
+ }
+ /* Test for a server in share level mode do not authenticate against it */
+ if (con->Security == 0) {
+ SMB_Discon(con, 0);
+ return (NTV_PROTOCOL_ERROR);
+ }
+ if (SMB_Logon_Server(con, USERNAME, PASSWORD, DOMAIN, pass_is_precrypted_p) < 0) {
+ SMB_Discon(con, 0);
+ return (NTV_LOGON_ERROR);
+ }
+ SMB_Discon(con, 0);
+ return (NTV_NO_ERROR);
+}
+
+void *
+NTLM_Connect(char *SERVER, char *BACKUP, char *DOMAIN, char *nonce)
+{
+ char *SMB_Prots[] =
+ {
+/* "PC NETWORK PROGRAM 1.0", */
+/* "MICROSOFT NETWORKS 1.03", */
+/* "MICROSOFT NETWORKS 3.0", */
+ "LANMAN1.0",
+ "LM1.2X002",
+ "Samba",
+/* "NT LM 0.12", */
+/* "NT LANMAN 1.0", */
+ NULL};
+ SMB_Handle_Type con;
+
+ SMB_Init();
+ con = SMB_Connect_Server(NULL, SERVER, DOMAIN);
+ if (con == NULL) { /* Error ... */
+ con = SMB_Connect_Server(NULL, BACKUP, DOMAIN);
+ if (con == NULL) {
+ return (NULL);
+ }
+ }
+ if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */
+ SMB_Discon(con, 0);
+ return (NULL);
+ }
+ /* Test for a server in share level mode do not authenticate against it */
+ if (con->Security == 0) {
+ SMB_Discon(con, 0);
+ return (NULL);
+ }
+ memcpy(nonce, con->Encrypt_Key, 8);
+
+ return (con);
+}
+
+int
+NTLM_Auth(void *handle, char *USERNAME, char *PASSWORD, int flag)
+{
+ SMB_Handle_Type con = handle;
+
+ if (SMB_Logon_Server(con, USERNAME, PASSWORD, NULL, flag) < 0) {
+ return (NTV_LOGON_ERROR);
+ }
+ return (NTV_NO_ERROR);
+}
+
+void
+NTLM_Disconnect(void *handle)
+{
+ SMB_Handle_Type con = handle;
+ SMB_Discon(con, 0);
+}
--- /dev/null
+#ifndef _VALID_H_
+#define _VALID_H_
+/* SMB User verification function */
+
+#define NTV_NO_ERROR 0
+#define NTV_SERVER_ERROR 1
+#define NTV_PROTOCOL_ERROR 2
+#define NTV_LOGON_ERROR 3
+
+int Valid_User(char *USERNAME, char *PASSWORD, char *SERVER, char *BACKUP, char *DOMAIN);
+void *NTLM_Connect(char *SERVER, char *BACKUP, char *DOMAIN, char *nonce);
+int NTLM_Auth(void *handle, char *USERNAME, char *PASSWORD, int flag);
+void NTLM_Disconnect(void *handle);
+
+#endif
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:50 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+FAKEAUTH_AUTH_EXE = fakeauth_auth$(exec_suffix)
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+CRYPTLIB = @CRYPTLIB@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(FAKEAUTH_AUTH_EXE)
+OBJS = fakeauth_auth.o
+
+all: $(FAKEAUTH_AUTH_EXE)
+
+$(OBJS): $(top_srcdir)/include/version.h
+
+$(FAKEAUTH_AUTH_EXE): $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $@ $(AUTH_LIBS)
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+/*
+ *
+ * AUTHOR: Robert Collins <rbtcollins@hotmail.com>
+ *
+ * Example ntlm authentication program for Squid, based on the
+ * original proxy_auth code from client_side.c, written by
+ * Jon Thackray <jrmt@uk.gdscorp.com>. and the inital ntlm code
+ * Andy Doran.
+ *
+ * This code gets the username and returns it. No validation is done.
+ * and by the way: it is a complete patch-up. Use the "real thing" NTLMSSP
+ * if you can.
+ */
+
+#include "config.h"
+
+#include "ntlm.h"
+#include "util.h"
+#include <ctype.h>
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+
+#define ERR "ERR\n"
+#define OK "OK\n"
+
+#if 0
+#define NTLM_STATIC_CHALLENGE "deadbeef"
+#endif
+static char *authenticate_ntlm_domain = "LIFELESSWKS";
+
+/* NTLM authentication by ad@netbsd.org - 07/1999 */
+/* XXX this is not done cleanly... */
+
+/* makes a null-terminated string lower-case. Changes CONTENTS! */
+static void
+lc(char *string)
+{
+ char *p = string, c;
+ while ((c = *p)) {
+ *p = tolower(c);
+ p++;
+ }
+}
+
+
+/*
+ * Generates a challenge request. The randomness of the 8 byte
+ * challenge strings can be guarenteed to be poor at best.
+ */
+void
+ntlmMakeChallenge(struct ntlm_challenge *chal)
+{
+#ifndef NTLM_STATIC_CHALLENGE
+ static unsigned hash;
+ int r;
+#endif
+ char *d;
+ int i;
+
+ memset(chal, 0, sizeof(*chal));
+ memcpy(chal->hdr.signature, "NTLMSSP", 8);
+ chal->flags = WSWAP(0x00018206);
+ chal->hdr.type = WSWAP(NTLM_CHALLENGE);
+ chal->unknown[6] = SSWAP(0x003a);
+
+ d = (char *) chal + 48;
+ i = 0;
+
+ if (authenticate_ntlm_domain != NULL)
+ while (authenticate_ntlm_domain[i++]);
+
+
+ chal->target.offset = WSWAP(48);
+ chal->target.maxlen = SSWAP(i);
+ chal->target.len = chal->target.maxlen;
+
+#ifdef NTLM_STATIC_CHALLENGE
+ memcpy(chal->challenge, NTLM_STATIC_CHALLENGE, 8);
+#else
+ r = (int) rand();
+ r = (hash ^ r) + r;
+
+ for (i = 0; i < 8; i++) {
+ chal->challenge[i] = r;
+ r = (r >> 2) ^ r;
+ }
+
+ hash = r;
+#endif
+}
+
+/*
+ * Check the vailidity of a request header. Return -1 on error.
+ */
+int
+ntlmCheckHeader(struct ntlmhdr *hdr, int type)
+{
+ /*
+ * Must be the correct security package and request type. The
+ * 8 bytes compared includes the ASCII 'NUL'.
+ */
+ if (memcmp(hdr->signature, "NTLMSSP", 8) != 0) {
+ fprintf(stderr, "ntlmCheckHeader: bad header signature\n");
+ return (-1);
+ }
+ if (type == NTLM_ANY)
+ return 0;
+
+ if (WSWAP(hdr->type) != type) {
+/* don't report this error - it's ok as we do a if() around this function */
+// fprintf(stderr, "ntlmCheckHeader: type is %d, wanted %d\n",
+ // WSWAP(hdr->type), type);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Extract a string from an NTLM request and return as ASCII.
+ */
+char *
+ntlmGetString(ntlmhdr * hdr, strhdr * str, int flags)
+{
+ static char buf[512];
+ u_short *s, c;
+ char *d, *sc;
+ int l, o;
+
+ l = SSWAP(str->len);
+ o = WSWAP(str->offset);
+
+ /* Sanity checks. XXX values arbitrarialy chosen */
+ if (l <= 0 || l >= 32 || o >= 256) {
+ fprintf(stderr, "ntlmGetString: insane: l:%d o:%d\n", l, o);
+ return (NULL);
+ }
+ if ((flags & 2) == 0) {
+ /* UNICODE string */
+ s = (u_short *) ((char *) hdr + o);
+ d = buf;
+
+ for (l >>= 1; l; s++, l--) {
+ c = SSWAP(*s);
+ if (c > 254 || c == '\0' || !isprint(c)) {
+ fprintf(stderr, "ntlmGetString: bad uni: %04x\n", c);
+ return (NULL);
+ }
+ *d++ = c;
+ fprintf(stderr, "ntlmGetString: conv: '%c'\n", c);
+ }
+
+ *d = 0;
+ } else {
+ /* ASCII string */
+ sc = (char *) hdr + o;
+ d = buf;
+
+ for (; l; l--) {
+ if (*sc == '\0' || !isprint(*sc)) {
+ fprintf(stderr, "ntlmGetString: bad ascii: %04x\n", *sc);
+ return (NULL);
+ }
+ *d++ = *sc++;
+ }
+
+ *d = 0;
+ }
+
+ return (buf);
+}
+
+/*
+ * Decode the strings in an NTLM authentication request
+ */
+int
+ntlmDecodeAuth(struct ntlm_authenticate *auth, char *buf, size_t size)
+{
+ char *p, *origbuf;
+ int s;
+
+ if (!buf) {
+ return 1;
+ }
+ origbuf = buf;
+ if (ntlmCheckHeader(&auth->hdr, NTLM_AUTHENTICATE)) {
+
+ fprintf(stderr, "ntlmDecodeAuth: header check fails\n");
+ return -1;
+ }
+/* only on when you need to debug
+ * fprintf(stderr,"ntlmDecodeAuth: size of %d\n", size);
+ * fprintf(stderr,"ntlmDecodeAuth: flg %08x\n", auth->flags);
+ * fprintf(stderr,"ntlmDecodeAuth: usr o(%d) l(%d)\n", auth->user.offset, auth->user.len);
+ */
+ if ((p = ntlmGetString(&auth->hdr, &auth->domain, 2)) == NULL)
+ p = authenticate_ntlm_domain;
+// fprintf(stderr,"ntlmDecodeAuth: Domain '%s'.\n",p);
+ if ((s = strlen(p) + 1) >= size)
+ return 1;
+ strcpy(buf, p);
+// fprintf(stdout,"ntlmDecodeAuth: Domain '%s'.\n",buf);
+
+ size -= s;
+ buf += (s - 1);
+ *buf++ = '\\'; /* Using \ is more consistent with MS-proxy */
+
+ p = ntlmGetString(&auth->hdr, &auth->user, 2);
+ if ((s = strlen(p) + 1) >= size)
+ return 1;
+ while (*p)
+ *buf++ = (*p++); //tolower
+
+ *buf++ = '\0';
+ size -= s;
+// fprintf(stderr, "ntlmDecodeAuth: user: %s%s\n",origbuf, p);
+
+
+ return 0;
+}
+
+
+int
+main()
+{
+ char buf[256];
+ char user[256], *p, *cleartext;
+ struct ntlm_challenge chal;
+ int len;
+ char *data = NULL;
+
+ setbuf(stdout, NULL);
+ while (fgets(buf, 256, stdin) != NULL) {
+ user[0] = '\0'; /*no usercode */
+
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+#if defined(NTLMHELPPROTOCOLV3) || !defined(NTLMHELPPROTOCOLV2)
+ if (strncasecmp(buf, "YR", 2) == 0) {
+ ntlmMakeChallenge(&chal);
+ len =
+ sizeof(chal) - sizeof(chal.pad) +
+ SSWAP(chal.target.maxlen);
+ data = (char *) base64_encode_bin((char *) &chal, len);
+ printf("TT %s\n", data);
+ } else if (strncasecmp(buf, "KK ", 3) == 0) {
+ cleartext = (char *) uudecode(buf + 3);
+ if (!ntlmCheckHeader((struct ntlmhdr *) cleartext, NTLM_AUTHENTICATE)) {
+ if (!ntlmDecodeAuth((struct ntlm_authenticate *) cleartext, user, 256)) {
+ lc(user);
+ printf("AF %s\n", user);
+ } else {
+ lc(user);
+ printf("NA invalid credentials%s\n", user);
+ }
+ } else {
+ lc(user);
+ printf("BH wrong packet type!%s\n", user);
+ }
+ }
+#endif
+#ifdef NTLMHELPPROTOCOLV2
+/* V2 of the protocol */
+ if (strncasecmp(buf, "RESET", 5) == 0) {
+ printf("RESET OK\n");
+ } else {
+ cleartext = (char *) uudecode(buf);
+ if (!ntlmCheckHeader((struct ntlmhdr *) cleartext, NTLM_NEGOTIATE)) {
+ ntlmMakeChallenge(&chal);
+ len =
+ sizeof(chal) - sizeof(chal.pad) +
+ SSWAP(chal.target.maxlen);
+ data = (char *) base64_encode_bin((char *) &chal, len);
+ printf("CH %s\n", data);
+ } else if (!ntlmCheckHeader
+ ((struct ntlmhdr *) cleartext, NTLM_AUTHENTICATE)) {
+ if (!ntlmDecodeAuth
+ ((struct ntlm_authenticate *) cleartext, user, 256)) {
+ lc(user);
+ printf("OK %s\n", user);
+ } else {
+ lc(user);
+ printf("ERR %s\n", user);
+ }
+ } else {
+ lc(user);
+ printf("ERR %s\n", user);
+ }
+ }
+#endif /*v2 */
+ }
+ exit(0);
+}
--- /dev/null
+/*
+ * $Id: ntlm.h,v 1.1 2001/01/07 23:36:50 hno Exp $
+ *
+ * AUTHOR: Andy Doran <ad@netbsd.org>
+ *
+ * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
+ * --------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from the
+ * Internet community. Development is led by Duane Wessels of the
+ * National Laboratory for Applied Network Research and funded by
+ * the National Science Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef _NTLM_H_
+#define _NTLM_H_
+
+/* undefine this to have strict protocol adherence. You don't really need
+ * that though */
+#define IGNORANCE_IS_BLISS
+
+#include <sys/types.h>
+
+/* All of this cruft is little endian */
+#ifdef WORDS_BIGENDIAN
+#define SSWAP(x) (bswap16((x)))
+#define WSWAP(x) (bswap32((x)))
+#else
+#define SSWAP(x) (x)
+#define WSWAP(x) (x)
+#endif
+
+/* NTLM request types that we know about */
+#define NTLM_NEGOTIATE 1
+#define NTLM_CHALLENGE 2
+#define NTLM_AUTHENTICATE 3
+#define NTLM_ANY 0
+
+/* Header proceeding each request */
+typedef struct ntlmhdr {
+ char signature[8]; /* NTLMSSP */
+ int32_t type; /* One of NTLM_* from above */
+} ntlmhdr;
+
+/* String header. String data resides at the end of the request */
+typedef struct strhdr {
+ int16_t len; /* Length in bytes */
+ int16_t maxlen; /* Allocated space in bytes */
+ int32_t offset; /* Offset from start of request */
+} strhdr;
+
+/* Negotiation request sent by client */
+struct ntlm_negotiate {
+ ntlmhdr hdr; /* NTLM header */
+ int32_t flags; /* Request flags */
+ strhdr domain; /* Domain we wish to authenticate in */
+ strhdr workstation; /* Client workstation name */
+ char pad[256]; /* String data */
+};
+
+/* Challenge request sent by server. */
+struct ntlm_challenge {
+ ntlmhdr hdr; /* NTLM header */
+ strhdr target; /* Authentication target (domain/server ...) */
+ int32_t flags; /* Request flags */
+ u_char challenge[8]; /* Challenge string */
+ int16_t unknown[8]; /* Some sort of context data */
+ char pad[256]; /* String data */
+};
+
+/* Authentication request sent by client in response to challenge */
+struct ntlm_authenticate {
+ ntlmhdr hdr; /* NTLM header */
+ strhdr lmresponse; /* LANMAN challenge response */
+ strhdr ntresponse; /* NT challenge response */
+ strhdr domain; /* Domain to authenticate against */
+ strhdr user; /* Username */
+ strhdr workstation; /* Workstation name */
+ strhdr sessionkey; /* Session key for server's use */
+ int32_t flags; /* Request flags */
+ char pad[256 * 6]; /* String data */
+};
+
+char *ntlmGetString(ntlmhdr * hdr, strhdr * str, int flags);
+void ntlmMakeChallenge(struct ntlm_challenge *chal);
+int ntlmCheckHeader(struct ntlmhdr *hdr, int type);
+int ntlmCheckNegotiation(struct ntlm_negotiate *neg);
+int ntlmAuthenticate(struct ntlm_authenticate *neg);
+
+#endif /* _NTLM_H_ */
--- /dev/null
+#
+# Makefile for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:50 hno Exp $
+#
+# Uncomment and customize the following to suit your needs:
+#
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+exec_suffix = @exec_suffix@
+top_srcdir = @top_srcdir@
+bindir = @bindir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# Gotta love the DOS legacy
+#
+NO_CHECK = no_check
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+INSTALL = @INSTALL@
+INSTALL_BIN = @INSTALL_PROGRAM@
+CRYPTLIB = @CRYPTLIB@
+AC_CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+XTRA_LIBS = @XTRA_LIBS@
+XTRA_OBJS = @XTRA_OBJS@
+MV = @MV@
+RM = @RM@
+SHELL = /bin/sh
+
+
+INCLUDE = -I. -I../../../../../include -I$(top_srcdir)/include
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+AUTH_LIBS = -L../../../../../lib -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+PROGS = $(NO_CHECK).pl
+OBJS = $(NO_CHECK)
+
+all: $(PROGS)
+
+#$(OBJS):
+
+$(NO_CHECK).pl: $(OBJS)
+ cp $(srcdir)/$(NO_CHECK) ./$(NO_CHECK).pl
+
+install-mkdirs:
+ -@if test ! -d $(prefix); then \
+ echo "mkdir $(prefix)"; \
+ mkdir $(prefix); \
+ fi
+ -@if test ! -d $(bindir); then \
+ echo "mkdir $(bindir)"; \
+ mkdir $(bindir); \
+ fi
+
+install: all install-mkdirs
+ @for f in $(PROGS); do \
+ if test -f $(bindir)/$$f; then \
+ echo $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ $(MV) $(bindir)/$$f $(bindir)/-$$f; \
+ fi; \
+ echo $(INSTALL_BIN) $$f $(bindir); \
+ $(INSTALL_BIN) $$f $(bindir); \
+ if test -f $(bindir)/-$$f; then \
+ echo $(RM) -f $(bindir)/-$$f; \
+ $(RM) -f $(bindir)/-$$f; \
+ fi; \
+ done
+
+clean:
+ -rm -rf *.o *pure_* core $(PROGS)
+
+distclean: clean
+ -rm -f Makefile
+
+depend:
+ $(MAKEDEPEND) -I../include -I. -fMakefile *.c
--- /dev/null
+This is a dummy NTLM authentication module for Squid.
+It performs the NTLM challenge, but then it doesn't verify the
+user's credentials, it just takes the client's domain and username
+at face value.
+It's included mostly for demonstration purposes.
+
+(C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>
+Distributed freely under the terms of the GNU General Public License,
+version 2. For the licensing terms, see the file COPYING that
+came with Squid.
--- /dev/null
+/*
+ * $Id: ntlmauth.h,v 1.1 2001/01/07 23:36:35 hno Exp $
+ *
+ * * * * * * * * Legal stuff * * * * * * *
+ *
+ * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>,
+ * inspired by previous work by Andy Doran.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ * * * * * * * * Declaration of intents * * * * * * *
+ *
+ * This header contains definitions and defines allowing to decode and
+ * understand NTLM packets, as sent by Internet Explorer.
+ * It's put here as it is a common utility to all HTLM-enabled modules.
+ */
+
+#ifndef _NTLMAUTH_H_
+#define _NTLMAUTH_H_
+
+#include <sys/types.h> /* for *int32_t */
+
+/* All of this cruft is little endian */
+#ifdef WORDS_BIGENDIAN
+#define SSWAP(x) (bswap16((x)))
+#define WSWAP(x) (bswap32((x)))
+#else
+#define SSWAP(x) (x)
+#define WSWAP(x) (x)
+#endif
+
+/* Used internally. Microsoft seems to think this is right, I believe them.
+ * Right. */
+#define MAX_FIELD_LENGTH 300 /* max length of an NTLMSSP field */
+
+
+/* Here start the NTLMSSP definitions */
+/* NTLM request types that we know about */
+#define NTLM_NEGOTIATE 1
+#define NTLM_CHALLENGE 2
+#define NTLM_CHALLENGE_HEADER_OFFSET 40
+#define NTLM_AUTHENTICATE 3
+
+#define NONCE_LEN 8
+
+/* negotiate request flags */
+#define NEGOTIATE_UNICODE 0x0001
+#define NEGOTIATE_ASCII 0x0002
+#define NEGOTIATE_REQUEST_TARGET 0x0004
+#define NEGOTIATE_REQUEST_SIGN 0x0010
+#define NEGOTIATE_REQUEST_SEAL 0x0020
+#define NEGOTIATE_DATAGRAM_STYLE 0x0040
+#define NEGOTIATE_USE_LM 0x0080
+#define NEGOTIATE_USE_NETWARE 0x0100
+#define NEGOTIATE_USE_NTLM 0x0200
+#define NEGOTIATE_DOMAIN_SUPPLIED 0x1000
+#define NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
+#define NEGOTIATE_THIS_IS_LOCAL_CALL 0x4000
+#define NEGOTIATE_ALWAYS_SIGN 0x8000
+
+/* challenge request flags */
+#define CHALLENGE_TARGET_IS_DOMAIN 0x10000
+#define CHALLENGE_TARGET_IS_SERVER 0x20000
+#define CHALLENGE_TARGET_IS_SHARE 0x40000
+
+/* these are marked as "extra" fields */
+#define REQUEST_INIT_RESPONSE 0x100000
+#define REQUEST_ACCEPT_RESPONSE 0x200000
+#define REQUEST_NON_NT_SESSION_KEY 0x400000
+
+
+/* String header. String data resides at the end of the request */
+typedef struct _strhdr {
+ int16_t len; /* Length in bytes */
+ int16_t maxlen; /* Allocated space in bytes */
+ int32_t offset; /* Offset from start of request */
+} strhdr;
+
+/* We use this to keep data/lenght couples. Only used internally. */
+typedef struct _lstring {
+ int32_t l; /* length, -1 if empty */
+ char *str; /* the string. NULL if not initialized */
+} lstring;
+
+/* This is an header common to all signatures, it's used to discriminate
+ * among the different signature types. */
+typedef struct _ntlmhdr {
+ char signature[8]; /* "NTLMSSP" */
+ int32_t type; /* LSWAP(0x1) */
+} ntlmhdr;
+
+/* Negotiation request sent by client */
+typedef struct _ntlm_negotiate {
+ char signature[8]; /* "NTLMSSP" */
+ int32_t type; /* LSWAP(0x1) */
+ ntlmhdr hdr; /* NTLM header */
+ u_int32_t flags; /* Request flags */
+ strhdr domain; /* Domain we wish to authenticate in */
+ strhdr workstation; /* Client workstation name */
+ char payload[256]; /* String data */
+} ntlm_negotiate;
+
+/* Challenge request sent by server. */
+typedef struct _ntlm_challenge {
+ char signature[8]; /* "NTLMSSP" */
+ int32_t type; /* LSWAP(0x2) */
+ strhdr target; /* Authentication target (domain/server ...) */
+ u_int32_t flags; /* Request flags */
+ u_char challenge[NONCE_LEN]; /* Challenge string */
+ u_int32_t context_low; /* LS part of the server context handle */
+ u_int32_t context_high; /* MS part of the server context handle */
+ char payload[256]; /* String data */
+} ntlm_challenge;
+
+/* Authentication request sent by client in response to challenge */
+typedef struct _ntlm_authenticate {
+ char signature[8]; /* "NTLMSSP" */
+ int32_t type; /* LSWAP(0x3) */
+ strhdr lmresponse; /* LANMAN challenge response */
+ strhdr ntresponse; /* NT challenge response */
+ strhdr domain; /* Domain to authenticate against */
+ strhdr user; /* Username */
+ strhdr workstation; /* Workstation name */
+ strhdr sessionkey; /* Session key for server's use */
+ int32_t flags; /* Request flags */
+ char payload[256 * 6]; /* String data */
+} ntlm_authenticate;
+
+const char *ntlm_make_challenge(char *domain, char *domain_controller,
+ char *challenge_nonce, int challenge_nonce_len);
+lstring ntlm_fetch_string(char *packet, int32_t length, strhdr * str);
+void ntlm_add_to_payload(char *payload, int *payload_length,
+ strhdr * hdr, char *toadd,
+ int toadd_length, int base_offset);
+
+#endif /* _NTLMAUTH_H_ */
/*
- * $Id: util.h,v 1.56 2000/11/04 23:04:09 hno Exp $
+ * $Id: util.h,v 1.57 2001/01/07 23:36:35 hno Exp $
*
* AUTHOR: Harvest Derived
*
extern time_t parse_iso3307_time(const char *buf);
extern char *base64_decode(const char *coded);
extern const char *base64_encode(const char *decoded);
+extern const char *base64_encode_bin(const char *data, int len);
extern double xpercent(double part, double whole);
extern int xpercentInt(double part, double whole);
#
-# $Id: Makefile.in,v 1.47 2000/11/21 21:15:10 wessels Exp $
+# $Id: Makefile.in,v 1.48 2001/01/07 23:36:36 hno Exp $
#
prefix = @prefix@
top_srcdir = @top_srcdir@
$(LIBOBJS)
REGEXOBJS = GNUregex.o
DLMALLOCOBJS = dlmalloc.o
-LIBS = libmiscutil.a @LIBREGEX@ @LIBDLMALLOC@
+NTLMAUTHOBJS = ntlmauth.o
+LIBS = libmiscutil.a @LIBREGEX@ @LIBDLMALLOC@ libntlmauth.a
CFLAGS = $(AC_CFLAGS) $(INCLUDE)
$(AR_R) $@ $(DLMALLOCOBJS)
$(RANLIB) $@
+libntlmauth.a: $(NTLMAUTHOBJS)
+ $(RM) -f $@
+ $(AR_R) $@ $(NTLMAUTHOBJS)
+ $(RANLIB) $@
+
clean:
-rm -f *.o $(LIBS) core
/*
- * $Id: base64.c,v 1.16 1998/10/14 02:40:25 wessels Exp $
+ * $Id: base64.c,v 1.17 2001/01/07 23:36:36 hno Exp $
*/
#include "config.h"
result[out_cnt] = '\0'; /* terminate */
return result;
}
+
+/* adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments */
+const char *
+base64_encode_bin(const char *data, int len)
+{
+ static char result[BASE64_RESULT_SZ];
+ int bits = 0;
+ int char_count = 0;
+ int out_cnt = 0;
+ int c;
+
+ if (!data)
+ return data;
+
+ if (!base64_initialized)
+ base64_init();
+
+ while (len-- && out_cnt < sizeof(result) - 1) {
+ c = (unsigned char) *data++;
+ bits += c;
+ char_count++;
+ if (char_count == 3) {
+ result[out_cnt++] = base64_code[bits >> 18];
+ result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
+ result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
+ result[out_cnt++] = base64_code[bits & 0x3f];
+ bits = 0;
+ char_count = 0;
+ } else {
+ bits <<= 8;
+ }
+ }
+ if (char_count != 0) {
+ bits <<= 16 - (8 * char_count);
+ result[out_cnt++] = base64_code[bits >> 18];
+ result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
+ if (char_count == 1) {
+ result[out_cnt++] = '=';
+ result[out_cnt++] = '=';
+ } else {
+ result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
+ result[out_cnt++] = '=';
+ }
+ }
+ result[out_cnt] = '\0'; /* terminate */
+ return result;
+}
--- /dev/null
+/*
+ * $Id: ntlmauth.c,v 1.1 2001/01/07 23:36:36 hno Exp $
+ *
+ * * * * * * * * Legal stuff * * * * * * *
+ *
+ * (C) 2000 Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it>,
+ * inspired by previous work by Andy Doran.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "ntlmauth.h"
+#include "util.h" /* for base64-related stuff */
+
+/* Dumps NTLM flags to standard error for debugging purposes */
+void
+ntlm_dump_ntlmssp_flags(u_int32_t flags)
+{
+ fprintf(stderr, "flags: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ (flags & NEGOTIATE_UNICODE ? "Unicode " : ""),
+ (flags & NEGOTIATE_ASCII ? "ASCII " : ""),
+ (flags & NEGOTIATE_REQUEST_TARGET ? "ReqTgt " : ""),
+ (flags & NEGOTIATE_REQUEST_SIGN ? "ReqSign " : ""),
+ (flags & NEGOTIATE_REQUEST_SEAL ? "ReqSeal " : ""),
+ (flags & NEGOTIATE_DATAGRAM_STYLE ? "Dgram " : ""),
+ (flags & NEGOTIATE_USE_LM ? "UseLM " : ""),
+ (flags & NEGOTIATE_USE_NETWARE ? "UseNW " : ""),
+ (flags & NEGOTIATE_USE_NTLM ? "UseNTLM " : ""),
+ (flags & NEGOTIATE_DOMAIN_SUPPLIED ? "HaveDomain " : ""),
+ (flags & NEGOTIATE_WORKSTATION_SUPPLIED ? "HaveWKS " : ""),
+ (flags & NEGOTIATE_THIS_IS_LOCAL_CALL ? "LocalCall " : ""),
+ (flags & NEGOTIATE_ALWAYS_SIGN ? "AlwaysSign " : ""),
+ (flags & CHALLENGE_TARGET_IS_DOMAIN ? "Tgt_is_domain" : ""),
+ (flags & CHALLENGE_TARGET_IS_SERVER ? "Tgt_is_server " : ""),
+ (flags & CHALLENGE_TARGET_IS_SHARE ? "Tgt_is_share " : ""),
+ (flags & REQUEST_INIT_RESPONSE ? "Req_init_response " : ""),
+ (flags & REQUEST_ACCEPT_RESPONSE ? "Req_accept_response " : ""),
+ (flags & REQUEST_NON_NT_SESSION_KEY ? "Req_nonnt_sesskey " : "")
+ );
+}
+
+#define lstring_zero(s) s.str=NULL; s.l=-1;
+
+/* fetches a string from the authentication packet.
+ * The lstring data-part points to inside the packet itself.
+ * It's up to the user to memcpy() that if the value needs to
+ * be used in any way that requires a tailing \0. (he can check whether the
+ * value is there though, in that case lstring.length==-1).
+ */
+lstring
+ntlm_fetch_string(char *packet, int32_t length, strhdr * str)
+{
+ int16_t l; /* length */
+ int32_t o; /* offset */
+ lstring rv;
+
+ lstring_zero(rv);
+
+ l = SSWAP(str->len);
+ o = WSWAP(str->offset);
+ /* debug("fetch_string(plength=%d,l=%d,o=%d)\n",length,l,o); */
+
+ if (l < 0 || l > MAX_FIELD_LENGTH || o + l > length || o == 0) {
+ /* debug("ntlmssp: insane data (l: %d, o: %d)\n", l,o); */
+ return rv;
+ }
+ rv.str = packet + o;
+ rv.l = l;
+
+ return rv;
+}
+
+/* Adds something to the payload. The caller must guarrantee that
+ * there is enough space in the payload string to accommodate the
+ * added value.
+ * payload_length and hdr will be modified as a side-effect.
+ * base_offset is the payload offset from the packet's beginning, and is
+ */
+void
+ntlm_add_to_payload(char *payload, int *payload_length,
+ strhdr * hdr, char *toadd,
+ int toadd_length, int base_offset)
+{
+
+ int l = (*payload_length);
+ memcpy(payload + l, toadd, toadd_length);
+
+ hdr->len = toadd_length;
+ hdr->maxlen = toadd_length;
+ hdr->offset = l + base_offset; /* 48 is the base offset of the payload */
+ (*payload_length) += toadd_length;
+}
+
+
+/* prepares a base64-encode challenge packet to be sent to the client
+ * note: domain should be upper_case
+ * note: the storage type for the returned value depends on
+ * base64_encode_bin. Currently this means static storage.
+ */
+const char *
+ntlm_make_challenge(char *domain, char *domain_controller,
+ char *challenge_nonce, int challenge_nonce_len)
+{
+ ntlm_challenge ch;
+ int pl = 0;
+ const char *encoded;
+ memset(&ch, 0, sizeof(ntlm_challenge)); /* reset */
+ memcpy(ch.signature, "NTLMSSP", 8); /* set the signature */
+ ch.type = WSWAP(NTLM_CHALLENGE); /* this is a challenge */
+ ntlm_add_to_payload(ch.payload, &pl, &ch.target, domain, strlen(domain),
+ NTLM_CHALLENGE_HEADER_OFFSET);
+ ch.flags = WSWAP(
+ REQUEST_NON_NT_SESSION_KEY |
+ CHALLENGE_TARGET_IS_DOMAIN |
+ NEGOTIATE_ALWAYS_SIGN |
+ NEGOTIATE_USE_NTLM |
+ NEGOTIATE_USE_LM |
+ NEGOTIATE_ASCII |
+ 0
+ );
+ ch.context_low = 0; /* check this out */
+ ch.context_high = 0;
+ memcpy(ch.challenge, challenge_nonce, challenge_nonce_len);
+ encoded = base64_encode_bin((char *) &ch, NTLM_CHALLENGE_HEADER_OFFSET + pl);
+ return encoded;
+}
/*
- * $Id: util.c,v 1.75 2001/01/07 09:55:22 hno Exp $
+ * $Id: util.c,v 1.76 2001/01/07 23:36:36 hno Exp $
*
* DEBUG:
* AUTHOR: Harvest Derived
}
/* A default failure notifier when the main program hasn't installed any */
-void
+void
default_failure_notify(const char *msg)
{
write(2, msg, strlen(msg));
/*
- * $Id: HttpRequest.cc,v 1.27 2001/01/04 21:09:00 wessels Exp $
+ * $Id: HttpRequest.cc,v 1.28 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 73 HTTP Request
* AUTHOR: Duane Wessels
requestDestroy(request_t * req)
{
assert(req);
- safe_free(req->body);
+ if (req->body_connection)
+ clientAbortBody(req);
+ if (req->auth_user_request)
+ authenticateAuthUserRequestUnlock(req->auth_user_request);
safe_free(req->canonical);
stringClean(&req->urlpath);
httpHeaderClean(&req->header);
#
# Makefile for the Squid Object Cache server
#
-# $Id: Makefile.in,v 1.195 2000/10/20 23:50:59 hno Exp $
+# $Id: Makefile.in,v 1.196 2001/01/07 23:36:37 hno Exp $
#
# Uncomment and customize the following to suit your needs:
#
srcdir = @srcdir@
VPATH = @srcdir@
-SUBDIRS = fs repl
+SUBDIRS = fs repl auth
# Gotta love the DOS legacy
#
DEFAULT_ERROR_DIR = $(sysconfdir)/errors
DEFAULT_MIB_PATH = $(sysconfdir)/mib.txt
+AUTH_OBJS = @AUTH_OBJS@
+AUTH_MODULES = @AUTH_MODULES@
CC = @CC@
MAKEDEPEND = @MAKEDEPEND@
INSTALL = @INSTALL@
access_log.o \
acl.o \
asn.o \
+ auth_modules.o \
authenticate.o \
cache_cf.o \
CacheDigest.o \
pconn.o \
peer_digest.o \
peer_select.o \
- pump.o \
redirect.o \
referer.o \
refresh.o \
$(SNMP_OBJS): ../snmplib/libsnmp.a $(top_srcdir)/include/cache_snmp.h
-$(SQUID_EXE): $(OBJS) $(STORE_OBJS) $(REPL_OBJS)
- $(CC) -o $@ $(LDFLAGS) $(OBJS) $(STORE_OBJS) $(REPL_OBJS) $(SQUID_LIBS)
+$(SQUID_EXE): $(OBJS) $(STORE_OBJS) $(REPL_OBJS) $(AUTH_OBJS)
+ $(CC) -o $@ $(LDFLAGS) $(OBJS) $(STORE_OBJS) $(REPL_OBJS) $(AUTH_OBJS) $(SQUID_LIBS)
globals.o: globals.c Makefile
$(CC) -c globals.c $(CFLAGS) -I$(srcdir) $(DEFAULTS)
repl_modules repl/stamp:
@sh -c "cd repl && $(MAKE) all"
+auth_modules.c: auth_modules.sh Makefile
+ sh $(srcdir)/auth_modules.sh $(AUTH_MODULES) >auth_modules.c
+
+auth_modules.o: auth_modules.c
+ $(CC) -c auth_modules.c $(CFLAGS) -I$(srcdir)
+
+$(AUTH_OBJS):
+ @sh -c "cd `dirname $@` && $(MAKE) $(MFLAGS) `basename $@`"
+
install-mkdirs:
-@if test ! -d $(prefix); then \
echo "mkdir $(prefix)"; \
clean:
-rm -rf *.o *pure_* core $(PROGS) $(UTILS) $(CGIPROGS) $(SUID_UTILS)
-rm -f cf_gen cf_gen_defines.h cf_parser.c cf.data globals.c string_arrays.c
- -rm -f store_modules.c repl_modules.c squid.conf
+ -rm -f store_modules.c repl_modules.c auth_modules.c squid.conf
@for dir in $(SUBDIRS); do \
echo "Making $@ in $$dir..."; \
(cd $$dir ; $(MAKE) $(MFLAGS) prefix="$(prefix)" $@) || exit 1; \
/*
- * $Id: access_log.cc,v 1.64 2001/01/02 00:09:55 wessels Exp $
+ * $Id: access_log.cc,v 1.65 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 46 Access Log
* AUTHOR: Duane Wessels
return buf;
}
+char *
+username_quote(const char *header)
+/* copy of log_quote. Bugs there will be found here */
+{
+ int c;
+ int i;
+ char *buf;
+ char *buf_cursor;
+ if (header == NULL) {
+ buf = xcalloc(1, 1);
+ *buf = '\0';
+ return buf;
+ }
+ buf = xcalloc((strlen(header) * 3) + 1, 1);
+ buf_cursor = buf;
+ /*
+ * We escape: space \x00-\x1F and space (0x40) and \x7F-\xFF
+ * to prevent garbage in the logs. CR and LF are also there just in case.
+ */
+ while ((c = *(const unsigned char *) header++) != '\0') {
+ if (c == '\r') {
+ *buf_cursor++ = '\\';
+ *buf_cursor++ = 'r';
+ } else if (c == '\n') {
+ *buf_cursor++ = '\\';
+ *buf_cursor++ = 'n';
+ } else if (c <= 0x1F
+ || c >= 0x7F
+ || c == ' ') {
+ *buf_cursor++ = '%';
+ i = c * 2;
+ *buf_cursor++ = c2x[i];
+ *buf_cursor++ = c2x[i + 1];
+ } else {
+ *buf_cursor++ = (char) c;
+ }
+ }
+ *buf_cursor = '\0';
+ return buf;
+}
+
+char *
+accessLogFormatName(const char *name)
+{
+ if (NULL == name)
+ return xcalloc(strlen(dash_str) + 1, 1);
+ return username_quote(name);
+}
+
static void
accessLogSquid(AccessLogEntry * al)
{
const char *client = NULL;
+ char *user = NULL;
if (Config.onoff.log_fqdn)
client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
if (client == NULL)
al->cache.size,
al->private.method_str,
al->url,
- al->cache.ident,
+ (user = accessLogFormatName(al->cache.authuser ?
+ al->cache.authuser : al->cache.rfc931)),
al->hier.ping.timedout ? "TIMEOUT_" : "",
hier_strings[al->hier.code],
al->hier.host,
al->http.content_type);
+ safe_free(user);
}
static void
accessLogCommon(AccessLogEntry * al)
{
const char *client = NULL;
+ char *user = NULL;
if (Config.onoff.log_fqdn)
client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
if (client == NULL)
client = inet_ntoa(al->cache.caddr);
- logfilePrintf(logfile, "%s %s - [%s] \"%s %s HTTP/%d.%d\" %d %d %s:%s",
+ logfilePrintf(logfile, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %d %s:%s",
client,
- al->cache.ident,
+ accessLogFormatName(al->cache.rfc931),
+ (user = accessLogFormatName(al->cache.authuser)),
mkhttpdlogtime(&squid_curtime),
al->private.method_str,
al->url,
al->cache.size,
log_tags[al->cache.code],
hier_strings[al->hier.code]);
+ safe_free(user);
}
void
accessLogLog(AccessLogEntry * al)
{
- LOCAL_ARRAY(char, ident_buf, USER_IDENT_SZ);
-
if (LogfileStatus != LOG_ENABLE)
return;
if (al->url == NULL)
al->url = dash_str;
if (!al->http.content_type || *al->http.content_type == '\0')
al->http.content_type = dash_str;
- if (!al->cache.ident || *al->cache.ident == '\0') {
- al->cache.ident = dash_str;
- } else {
- xstrncpy(ident_buf, rfc1738_escape(al->cache.ident), USER_IDENT_SZ);
- al->cache.ident = ident_buf;
- }
if (al->icp.opcode)
al->private.method_str = icp_opcode_str[al->icp.opcode];
else
/*
- * $Id: acl.cc,v 1.235 2001/01/07 20:11:16 hno Exp $
+ * $Id: acl.cc,v 1.236 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 28 Access Control
* AUTHOR: Duane Wessels
static int aclFromFile = 0;
static FILE *aclFile;
-static hash_table *proxy_auth_cache = NULL;
static void aclParseDomainList(void *curlist);
static void aclParseUserList(void **current);
static void aclDestroyAclList(acl_list * list);
static void aclDestroyTimeList(acl_time_data * data);
static void aclDestroyIntRange(intrange *);
-static FREE aclFreeProxyAuthUser;
+static void aclLookupProxyAuthStart(aclCheck_t * checklist);
+static void aclLookupProxyAuthDone(void *data, char *result);
static struct _acl *aclFindByName(const char *name);
static int aclMatchAcl(struct _acl *, aclCheck_t *);
static int aclMatchIntegerRange(intrange * data, int i);
static IPH aclLookupDstIPforASNDone;
static FQDNH aclLookupSrcFQDNDone;
static FQDNH aclLookupDstFQDNDone;
-static void aclLookupProxyAuthStart(aclCheck_t * checklist);
-static void aclLookupProxyAuthDone(void *data, char *result);
static wordlist *aclDumpIpList(void *);
static wordlist *aclDumpDomainList(void *data);
static wordlist *aclDumpTimeSpecList(acl_time_data *);
static SPLAYCMP aclArpCompare;
static SPLAYWALKEE aclDumpArpListWalkee;
#endif
+static int aclCacheMatchAcl(dlink_list * cache, squid_acl acltype, void *data, char *MatchParam);
static char *
strtokFile(void)
aclParseMethodList(&A->data);
break;
case ACL_PROXY_AUTH:
- aclParseUserList(&A->data);
- if (!proxy_auth_cache) {
- /* First time around, 7921 should be big enough */
- proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
- assert(proxy_auth_cache);
+ if (authenticateSchemeCount() == 0) {
+ debug(28, 0) ("aclParseAclLine: IGNORING: Proxy Auth ACL '%s' \
+because no authentication schemes were compiled.\n", A->cfgline);
+ } else if (authenticateActiveSchemeCount() == 0) {
+ debug(28, 0) ("aclParseAclLine: IGNORING: Proxy Auth ACL '%s' \
+because no authentication schemes are fully configured.\n", A->cfgline);
+ } else {
+ aclParseUserList(&A->data);
}
break;
case ACL_PROXY_AUTH_REGEX:
- aclParseRegexList(&A->data);
- if (!proxy_auth_cache) {
- /* First time around, 7921 should be big enough */
- proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
- assert(proxy_auth_cache);
+ if (authenticateSchemeCount() == 0) {
+ debug(28, 0) ("aclParseAclLine: IGNORING: Proxy Auth ACL '%s' \
+because no authentication schemes were compiled.\n", A->cfgline);
+ } else if (authenticateActiveSchemeCount() == 0) {
+ debug(28, 0) ("aclParseAclLine: IGNORING: Proxy Auth ACL '%s' \
+because no authentication schemes are fully configured.\n", A->cfgline);
+ } else {
+ aclParseRegexList(&A->data);
}
break;
#if SQUID_SNMP
return !splayLastResult;
}
+/* ACL result caching routines */
+
+/*
+ * we lookup an acl's cached results, and if we cannot find the acl being
+ * checked we check it and cache the result. This function is deliberatly
+ * generic to support caching of multiple acl types (but it needs to be more
+ * generic still....
+ * The Match Param and the cache MUST be tied together by the calling routine.
+ * You have been warned :-]
+ * Also only Matchxxx that are of the form (void *, void *) can be used.
+ * probably some ugly overloading _could_ be done but I'll leave that as an
+ * exercise for the reader. Note that caching of time based acl's is not
+ * wise due to no expiry occuring to the cache entries until the user expires
+ * or a reconfigure takes place.
+ * RBC
+ */
static int
-aclDecodeProxyAuth(const char *proxy_auth, char **user, char **password, char *buf, size_t bufsize)
-{
- char *sent_auth;
- char *cleartext;
- if (proxy_auth == NULL)
- return 0;
- debug(28, 6) ("aclDecodeProxyAuth: header = '%s'\n", proxy_auth);
- if (strncasecmp(proxy_auth, "Basic ", 6) != 0) {
- debug(28, 1) ("aclDecodeProxyAuth: Unsupported proxy-auth sheme, '%s'\n", proxy_auth);
- return 0;
+aclCacheMatchAcl(dlink_list * cache, squid_acl acltype, void *data,
+ char *MatchParam)
+{
+ int matchrv;
+ acl_proxy_auth_match_cache *auth_match;
+ dlink_node *link;
+ link = cache->head;
+ while (link) {
+ auth_match = link->data;
+ if (auth_match->acl_data == data) {
+ debug(28, 4) ("aclCacheMatchAcl: cache hit on acl '%d'\n",
+ data);
+ return auth_match->matchrv;
+ }
+ link = link->next;
}
- proxy_auth += 6; /* "Basic " */
- /* Trim leading whitespace before decoding */
- while (xisspace(*proxy_auth))
- proxy_auth++;
- sent_auth = xstrdup(proxy_auth); /* username and password */
- /* Trim trailing \n before decoding */
- strtok(sent_auth, "\n");
- cleartext = uudecode(sent_auth);
- xfree(sent_auth);
- /*
- * Don't allow NL or CR in the credentials.
- * Oezguer Kesim <oec@codeblau.de>
- */
- strtok(cleartext, "\r\n");
- debug(28, 6) ("aclDecodeProxyAuth: cleartext = '%s'\n", cleartext);
- xstrncpy(buf, cleartext, bufsize);
- xfree(cleartext);
- /* Trim leading whitespace after decoding */
- while (xisspace(*buf))
- buf++;
- *user = buf;
- if ((*password = strchr(*user, ':')) != NULL)
- *(*password)++ = '\0';
- if (*password == NULL) {
- debug(28, 1) ("aclDecodeProxyAuth: no password in proxy authorization header '%s'\n", proxy_auth);
- return 0;
+ auth_match = NULL;
+ /* match the user in the acl. They are not cached. */
+ switch (acltype) {
+ case ACL_PROXY_AUTH:
+ matchrv = aclMatchUser(data, MatchParam);
+ break;
+ case ACL_PROXY_AUTH_REGEX:
+ matchrv = aclMatchRegex(data, MatchParam);
+ default:
+ /* This is a fatal to ensure that aclCacheMatchAcl calls are _only_
+ * made for supported acl types */
+ fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
+ return 0; /* NOTREACHED */
}
- if (**password == '\0') {
- debug(28, 1) ("aclDecodeProxyAuth: Disallowing empty password,"
- "user is '%s'\n", *user);
- return 0;
+ auth_match = memAllocate(MEM_ACL_PROXY_AUTH_MATCH);
+ auth_match->matchrv = matchrv;
+ auth_match->acl_data = data;
+ dlinkAddTail(auth_match, &auth_match->link, cache);
+ return matchrv;
+}
+
+void
+aclCacheMatchFlush(dlink_list * cache)
+{
+ acl_proxy_auth_match_cache *auth_match;
+ dlink_node *link, *tmplink;
+ link = cache->head;
+ while (link) {
+ auth_match = link->data;
+ tmplink = link;
+ link = link->next;
+ dlinkDelete(tmplink, cache);
+ memFree(auth_match, MEM_ACL_PROXY_AUTH_MATCH);
}
- return 1;
}
-/* aclMatchProxyAuth can return three exit codes:
- * 0 : user denied access
- * 1 : user validated OK
- * -1 : check the password for this user via an external authenticator
- * -2 : invalid Proxy-authorization: header;
- * ask for Proxy-Authorization: header
+/* aclMatchProxyAuth can return four exit codes:
+ * 0 : Authenticated OK, Authorisation for this ACL failed.
+ * 1 : Authenticated OK, Authorisation OK.
+ * -1 : send data to an external authenticator
+ * -2 : send data to the client
*/
-
static int
-aclMatchProxyAuth(void *data, const char *proxy_auth, acl_proxy_auth_user * auth_user, aclCheck_t * checklist, squid_acl acltype)
+aclMatchProxyAuth(void *data, http_hdr_type headertype,
+ auth_user_request_t * auth_user_request, aclCheck_t * checklist, squid_acl acltype)
{
/* checklist is used to register user name when identified, nothing else */
- LOCAL_ARRAY(char, login_buf, USER_IDENT_SZ);
- char *user, *password;
-
- if (!aclDecodeProxyAuth(proxy_auth, &user, &password, login_buf, sizeof(login_buf)))
- /* No or invalid Proxy-Auth header */
- return -2;
+ const char *proxy_auth;
+ /* consistent parameters ? */
+ assert(auth_user_request == checklist->auth_user_request);
+
+ /* General program flow in proxy_auth acls
+ * 1. Consistency checks: are we getting sensible data
+ * 2. Call the authenticate* functions to establish a authenticated user
+ * 4. look up the username in acltype (and cache the result against the
+ * username
+ */
- debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", user);
+ assert(headertype != 0);
+ proxy_auth = httpHeaderGetStr(&checklist->request->header, headertype);
- if (auth_user) {
+ if (checklist->conn == NULL) {
+ debug(28, 1) ("aclMatchProxyAuth: no connection data, cannot process authentication\n");
/*
- * This should be optimized to a boolean argument indicating that the
- * password is invalid, instead of passing full acl_proxy_auth_user
- * structures, and all messing with checklist->proxy_auth should
- * be restricted the functions that deal with the authenticator.
+ * deny access: clientreadrequest requires conn data, and it is always
+ * compiled in so we should have it too.
*/
- assert(auth_user == checklist->auth_user);
- checklist->auth_user = NULL; /* get rid of that special reference */
- /* Check result from external validation */
- if (auth_user->passwd_ok != 1) {
- /* password was checked but did not match */
- assert(auth_user->passwd_ok == 0);
- debug(28, 4) ("aclMatchProxyAuth: authentication failed for user '%s'\n",
- user);
- aclFreeProxyAuthUser(auth_user);
- /*
- * copy username to request for logging on client-side
- * unless ident is known (do not override ident with
- * false proxy auth names)
- */
- if (!*checklist->request->user_ident)
- xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ);
- return -2;
- } else {
- /* password was checked and did match */
- debug(28, 4) ("aclMatchProxyAuth: user '%s' validated OK\n", user);
- /* store validated user in hash, after filling in expiretime */
- xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ);
- auth_user->expiretime = current_time.tv_sec + Config.authenticateTTL;
- auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL;
- auth_user->ipaddr = checklist->src_addr;
- hash_join(proxy_auth_cache, &auth_user->hash);
- /* Continue checking below, as normal */
+ return 0;
+ }
+ if (((proxy_auth == NULL) && (checklist->conn->auth_type == AUTH_UNKNOWN)) || (checklist->conn->auth_type == AUTH_BROKEN)) {
+ /* no header or authentication failed/got corrupted - restart */
+ checklist->conn->auth_type = AUTH_UNKNOWN;
+ debug(28, 4) ("aclMatchProxyAuth: broken auth or no proxy_auth header. Requesting auth header.\n");
+ /* something wrong with the AUTH credentials. Force a new attempt */
+ checklist->auth_user_request = NULL;
+ checklist->conn->auth_user_request = NULL;
+ if (auth_user_request) {
+ /* unlock the ACL lock */
+ authenticateAuthUserRequestUnlock(auth_user_request);
}
+ return -2;
}
- /* see if we already know this user */
- auth_user = hash_lookup(proxy_auth_cache, user);
-
- if (!auth_user) {
- /* user not yet known, ask external authenticator */
- debug(28, 4) ("aclMatchProxyAuth: user '%s' not yet known\n", user);
- return -1;
- } else if ((0 == strcmp(auth_user->passwd, password)) &&
- (auth_user->expiretime > current_time.tv_sec)) {
- if (checklist->src_addr.s_addr == auth_user->ipaddr.s_addr
- || auth_user->ip_expiretime <= squid_curtime) {
- /* user already known and valid */
- debug(28, 5) ("aclMatchProxyAuth: user '%s' previously validated\n",
- user);
- /* Update IP ttl */
- auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL;
- auth_user->ipaddr = checklist->src_addr;
- /* copy username to request for logging on client-side */
- xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ);
- switch (acltype) {
- case ACL_PROXY_AUTH:
- return aclMatchUser(data, user);
- case ACL_PROXY_AUTH_REGEX:
- return aclMatchRegex(data, user);
- default:
- fatal("aclMatchProxyAuth: unknown ACL type");
- return 0; /* NOTREACHED */
+ /* we have a proxy auth header and as far as we know this connection has
+ * not had bungled connection oriented authentication happen on it. */
+ debug(28, 9) ("aclMatchProxyAuth: header %s.\n", proxy_auth);
+ if (auth_user_request == NULL) {
+ debug(28, 9) ("aclMatchProxyAuth: This is a new request on FD:%d\n", checklist->conn->fd);
+ if ((!checklist->request->auth_user_request) && (checklist->conn->auth_type == AUTH_UNKNOWN)) {
+ /* beginning of a new request check */
+ debug(28, 4) ("aclMatchProxyAuth: no connection authentication type\n");
+ if (!authenticateValidateUser(auth_user_request = authenticateGetAuthUser(proxy_auth))) {
+ /* the decode might have left a username for logging, or a message to
+ * the user */
+ if (auth_user_request) {
+ /* lock the user for the request structure link */
+ authenticateAuthUserRequestLock(auth_user_request);
+ checklist->request->auth_user_request = auth_user_request;
+ /* unlock the ACL reference. */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ }
+ return -2;
}
+ /* the user_request comes prelocked for the caller to GetAuthUser (us) */
+ } else if (checklist->request->auth_user_request) {
+ auth_user_request = checklist->request->auth_user_request;
+ /* lock the user request for this ACL processing */
+ authenticateAuthUserRequestLock(auth_user_request);
} else {
- if (Config.onoff.authenticateIpTTLStrict) {
- /* Access from some other IP address than the one owning
- * this user ID. Deny access
- */
- debug(28, 1) ("aclMatchProxyAuth: user '%s' tries to use multple IP addresses!\n", user);
- return 0;
+ if (checklist->conn->auth_user_request != NULL) {
+ auth_user_request = checklist->conn->auth_user_request;
+ /* lock the user request for this ACL processing */
+ authenticateAuthUserRequestLock(auth_user_request);
} else {
- /* user has switched to another IP addr */
- debug(28, 1) ("aclMatchProxyAuth: user '%s' has changed IP address\n", user);
- /* remove this user from the hash, making him unknown */
- hash_remove_link(proxy_auth_cache, (hash_link *) auth_user);
- aclFreeProxyAuthUser(auth_user);
- /* require the user to reauthenticate */
+ /* failed connection based authentication */
+ debug(28, 4) ("aclMatchProxyAuth: Aauth user request %d conn-auth user request %d conn type %d authentication failed.\n", auth_user_request, checklist->conn->auth_user_request, checklist->conn->auth_type);
return -2;
}
}
- } else {
- /* password mismatch/timeout */
- debug(28, 4) ("aclMatchProxyAuth: user '%s' password mismatch/timeout\n",
- user);
- /* remove this user from the hash, making him unknown */
- hash_remove_link(proxy_auth_cache, (hash_link *) auth_user);
- aclFreeProxyAuthUser(auth_user);
- /* ask the external authenticator in case the password is changed */
- /* wrong password will be trapped above so this does not loop */
- return -1;
}
- /* NOTREACHED */
+ /* Clear the reference in the checklist */
+ checklist->auth_user_request = NULL;
+ if (!authenticateUserAuthenticated(auth_user_request)) {
+ /* User not logged in. Log them in */
+ authenticateAuthUserRequestSetIp(auth_user_request, checklist->src_addr);
+ authenticateAuthenticateUser(auth_user_request, checklist->request, checklist->conn, headertype);
+ switch (authenticateDirection(auth_user_request)) {
+ case 1:
+ /* this ACL check is finished. Unlock. */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ return -2;
+ case -1:
+ /* we are partway through authentication within squid
+ * store the auth_user for the callback to here */
+ checklist->auth_user_request = auth_user_request;
+ /* we will be called back here. Do not Unlock */
+ return -1;
+ case -2:
+ /* this ACL check is finished. Unlock. */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ return -2;
+ } /* on 0 the authentication is finished - fallthrough */
+ /* See of user authentication failed for some reason */
+ if (!authenticateUserAuthenticated(auth_user_request)) {
+ if ((!checklist->rfc931[0]) &&
+ (authenticateUserRequestUsername(auth_user_request))) {
+ if (!checklist->request->auth_user_request) {
+ /* lock the user for the request structure link */
+ authenticateAuthUserRequestLock(auth_user_request);
+ checklist->request->auth_user_request = auth_user_request;
+ }
+ }
+ /* this ACL check is finished. Unlock. */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ return -2;
+ }
+ }
+ /* User authenticated ok */
+ assert(authenticateUserAuthenticated(auth_user_request));
+
+ /* copy username to request for logging on client-side */
+ /* the credentials are correct at this point */
+ if (!checklist->request->auth_user_request) {
+ /* lock the user for the request structure link */
+ authenticateAuthUserRequestLock(auth_user_request);
+ checklist->request->auth_user_request = auth_user_request;
+ }
+ if (authenticateCheckAuthUserIP(checklist->src_addr, auth_user_request)) {
+ /* Once the match is completed we have finished with the
+ * auth_user structure */
+ /* this ACL check completed */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ /* check to see if we have matched the user-acl before */
+ return aclCacheMatchAcl(&auth_user_request->auth_user->proxy_match_cache,
+ acltype, data, authenticateUserRequestUsername(auth_user_request));
+ }
+ /* this acl check completed */
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ return 0;
}
static void
aclLookupProxyAuthStart(aclCheck_t * checklist)
{
- LOCAL_ARRAY(char, login_buf, USER_IDENT_SZ);
- const char *proxy_auth;
- char *user, *password;
- int ok;
- acl_proxy_auth_user *auth_user;
- assert(!checklist->auth_user);
- if (!checklist->request->flags.accelerated) {
- /* Proxy auth on proxy requests */
- proxy_auth = httpHeaderGetStr(&checklist->request->header,
- HDR_PROXY_AUTHORIZATION);
- } else {
- /* WWW auth on accelerated requests */
- proxy_auth = httpHeaderGetStr(&checklist->request->header,
- HDR_AUTHORIZATION);
- }
- ok = aclDecodeProxyAuth(proxy_auth, &user, &password, login_buf,
- sizeof(login_buf));
- /*
- * if aclDecodeProxyAuth() fails, the same call should have failed
- * in aclMatchProxyAuth, and we should never get this far.
- */
- assert(ok);
- debug(28, 4) ("aclLookupProxyAuthStart: going to ask authenticator on %s\n", user);
- /* we must still check this user's password */
- auth_user = memAllocate(MEM_ACL_PROXY_AUTH_USER);
- auth_user->hash.key = xstrdup(user);
- auth_user->passwd = xstrdup(password);
- auth_user->passwd_ok = -1;
- auth_user->expiretime = -1;
- checklist->auth_user = auth_user;
- authenticateStart(checklist->auth_user, aclLookupProxyAuthDone,
- checklist);
+ auth_user_request_t *auth_user_request;
+ assert(checklist->auth_user_request != NULL); /* this is created for us */
+ auth_user_request = checklist->auth_user_request;
+
+ assert(authenticateValidateUser(auth_user_request));
+ authenticateStart(auth_user_request, aclLookupProxyAuthDone, checklist);
}
static int
const char *header;
const char *browser;
int k;
+ http_hdr_type headertype;
if (!ae)
return 0;
switch (ae->type) {
/* NOTREACHED */
#if USE_IDENT
case ACL_IDENT:
- if (checklist->ident[0]) {
- return aclMatchUser(ae->data, checklist->ident);
+ if (checklist->rfc931[0]) {
+ return aclMatchUser(ae->data, checklist->rfc931);
} else {
checklist->state[ACL_IDENT] = ACL_LOOKUP_NEEDED;
return 0;
}
/* NOTREACHED */
case ACL_IDENT_REGEX:
- if (checklist->ident[0]) {
- return aclMatchRegex(ae->data, checklist->ident);
+ if (checklist->rfc931[0]) {
+ return aclMatchRegex(ae->data, checklist->rfc931);
} else {
checklist->state[ACL_IDENT] = ACL_LOOKUP_NEEDED;
return 0;
return -1;
} else if (!r->flags.accelerated) {
/* Proxy authorization on proxy requests */
- header = httpHeaderGetStr(&checklist->request->header,
- HDR_PROXY_AUTHORIZATION);
+ headertype = HDR_PROXY_AUTHORIZATION;
} else if (r->flags.internal) {
/* WWW authorization on accelerated internal requests */
- header = httpHeaderGetStr(&checklist->request->header,
- HDR_AUTHORIZATION);
+ headertype = HDR_AUTHORIZATION;
} else {
#if AUTH_ON_ACCELERATION
/* WWW authorization on accelerated requests */
- header = httpHeaderGetStr(&checklist->request->header,
- HDR_AUTHORIZATION);
+ headertype = HDR_AUTHORIZATION;
#else
debug(28, 1) ("aclMatchAcl: proxy_auth %s not applicable on accelerated requests.\n", ae->name);
return -1;
#endif
}
- /*
- * Register that we used the proxy authentication header so that
- * it is not forwarded to the next proxy
- */
- r->flags.used_proxy_auth = 1;
- /* Check the password */
- switch (aclMatchProxyAuth(ae->data,
- header,
- checklist->auth_user,
- checklist,
- ae->type)) {
+ /* Check the credentials */
+ switch (aclMatchProxyAuth(ae->data, headertype,
+ checklist->auth_user_request, checklist, ae->type)) {
case 0:
- /* Correct password, but was not allowed in this ACL */
+ debug(28, 4) ("aclMatchAcl: returning 0 user authenticated but not authorised.\n");
+ /* Authenticated but not Authorised for this ACL */
return 0;
case 1:
- /* user validated OK */
+ debug(28, 4) ("aclMatchAcl: returning 1 user authenticated and authorised.\n");
+ /* Authenticated and Authorised for this ACL */
return 1;
case -2:
- /* no such user OR we need a proxy authentication header */
+ debug(28, 4) ("aclMatchAcl: returning 0 sending authentication challenge.\n");
+ /* Authentication credentials invalid or missing. */
+ /* Or partway through NTLM handshake. A proxy_Authenticate header
+ * gets sent to the client. */
checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED;
- /*
- * XXX This is a bit oddly done.. should perhaps use different
- * return codes here
- */
return 0;
case -1:
+ debug(28, 4) ("aclMatchAcl: returning 0 sending credentials to helper.\n");
/*
* we need to validate the password
*/
checklist);
return;
} else if (checklist->state[ACL_PROXY_AUTH] == ACL_LOOKUP_NEEDED) {
- debug(28, 3) ("aclCheck: checking password via authenticator\n");
+ debug(28, 3)
+ ("aclCheck: checking password via authenticator\n");
aclLookupProxyAuthStart(checklist);
checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_PENDING;
return;
} else if (checklist->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_NEEDED) {
- /* Special case. Client is required to resend the request
- * with authentication. The request is denied.
+ /* Client is required to resend the request with correct authentication
+ * credentials. (This may be part of a stateful auth protocol.
+ * The request is denied.
*/
+ debug(28, 6) ("aclCheck: requiring Proxy Auth header.\n");
allow = ACCESS_REQ_PROXY_AUTH;
match = -1;
}
cbdataUnlock(checklist->callback_data);
checklist->callback = NULL;
checklist->callback_data = NULL;
+ /* XXX: this assert is here to check for misbehaved acl authentication code.
+ * It can probably go sometime soon. */
+ assert(checklist->auth_user_request == NULL);
aclChecklistFree(checklist);
}
{
aclCheck_t *checklist = data;
if (ident) {
- xstrncpy(checklist->ident, ident, sizeof(checklist->ident));
- xstrncpy(checklist->request->user_ident, ident, sizeof(checklist->request->user_ident));
+ xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
+#if DONT
+ xstrncpy(checklist->request->authuser, ident, USER_IDENT_SZ);
+#endif
} else {
- xstrncpy(checklist->ident, "-", sizeof(checklist->ident));
+ xstrncpy(checklist->rfc931, dash_str, USER_IDENT_SZ);
}
/*
* Cache the ident result in the connection, to avoid redoing ident lookup
* over and over on persistent connections
*/
- if (cbdataValid(checklist->conn) && !checklist->conn->ident[0])
- xstrncpy(checklist->conn->ident, checklist->ident, sizeof(checklist->conn->ident));
+ if (cbdataValid(checklist->conn) && !checklist->conn->rfc931[0])
+ xstrncpy(checklist->conn->rfc931, checklist->rfc931, USER_IDENT_SZ);
aclCheck(checklist);
}
#endif
aclLookupProxyAuthDone(void *data, char *result)
{
aclCheck_t *checklist = data;
+ auth_user_request_t *auth_user_request;
checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_DONE;
- debug(28, 4) ("aclLookupProxyAuthDone: result = %s\n",
- result ? result : "NULL");
- if (NULL == result)
- checklist->auth_user->passwd_ok = 0;
- else if (0 == strncasecmp(result, "OK", 2))
- checklist->auth_user->passwd_ok = 1;
- else {
- if (strlen(result) > sizeof("ERR "))
- checklist->auth_user->message = xstrdup(result + 4);
- checklist->auth_user->passwd_ok = 0;
+ if (result != NULL)
+ fatal("AclLookupProxyAuthDone: Old code floating around somewhere.\nMake clean and if that doesn't work, report a bug to the squid developers.\n");
+ /* state info check */
+ assert(checklist->conn != NULL);
+ auth_user_request = checklist->auth_user_request;
+ if (!authenticateValidateUser(auth_user_request)) {
+ /* credentials could not be checked either way
+ * restart the whole process */
+ checklist->conn->auth_user_request = NULL;
+ checklist->conn->auth_type = AUTH_BROKEN;
+ checklist->auth_user_request = NULL;
+ authenticateAuthUserRequestUnlock(auth_user_request);
+ aclCheck(checklist);
+ return;
}
aclCheck(checklist);
}
checklist->state[i] = ACL_LOOKUP_NONE;
#if USE_IDENT
if (ident)
- xstrncpy(checklist->ident, ident, USER_IDENT_SZ);
+ xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
#endif
- checklist->auth_user = NULL; /* init to NULL */
+ checklist->auth_user_request = NULL;
return checklist;
}
}
}
-static void
-aclFreeProxyAuthUser(void *data)
-{
- acl_proxy_auth_user *u = data;
- xfree(u->hash.key);
- xfree(u->passwd);
- memFree(u, MEM_ACL_PROXY_AUTH_USER);
-}
-
static void
aclFreeIpData(void *p)
{
--- /dev/null
+# Makefile for authentication modules in the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:42 hno Exp $
+#
+
+SUBDIRS = @AUTH_MODULES@
+OUTLIBS = @AUTH_LIBS@
+
+all install:
+ @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \
+ sh -c "cd $$dir && $(MAKE) $(MFLAGS) $@" || exit 1; \
+ done;
+
+$(OUTLIBS):
+ @sh -c "cd `basename $@ .a` && $(MAKE) $(MFLAGS) ../$@"
+
+clean:
+ -rm -f *.a stamp
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $(MFLAGS) $@" || exit 1;\
+ fi; \
+ done
+
+distclean:
+ -rm -f *.a Makefile
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $(MFLAGS) distclean"; \
+ fi; \
+ done
+
+.DEFAULT:
+ @test -z "$(SUBDIRS)" || for dir in $(SUBDIRS); do \
+ sh -c "cd $$dir && $(MAKE) $(MFLAGS) $@" || exit 1; \
+ done
--- /dev/null
+#
+# Makefile for the basic authenticcation scheme modulefor the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:43 hno Exp $
+#
+
+AUTH = basic
+
+SUBDIRS = helpers
+
+
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+AR_R = @AR_R@
+RANLIB = @RANLIB@
+AC_CFLAGS = @CFLAGS@
+SHELL = /bin/sh
+
+INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT = ../$(AUTH).a
+
+OBJS = \
+ auth_basic.o
+
+
+all install: $(OUT)
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $@" || exit 1; \
+ fi; \
+ done;
+
+$(OUT): $(OBJS)
+ @rm -f ../stamp
+ $(AR_R) $(OUT) $(OBJS)
+ $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+ @rm -f ../stamp
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -rf *.o *pure_* core ../$(AUTH).a
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) clean"; \
+ fi; \
+ done
+
+distclean: clean
+ -rm -f Makefile
+ -rm -f Makefile.bak
+ -rm -f tags
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) distclean"; \
+ fi; \
+ done
+
+tags:
+ ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch]
+
+depend:
+ $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
--- /dev/null
+
+/*
+ * $Id: auth_basic.cc,v 1.1 2001/01/07 23:36:43 hno Exp $
+ *
+ * DEBUG: section 29 Authenticator
+ * AUTHOR: Duane Wessels
+ *
+ * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from the
+ * Internet community. Development is led by Duane Wessels of the
+ * National Laboratory for Applied Network Research and funded by the
+ * National Science Foundation. Squid is Copyrighted (C) 1998 by
+ * the Regents of the University of California. Please see the
+ * COPYRIGHT file for full details. Squid incorporates software
+ * developed and/or copyrighted by other sources. Please see the
+ * CREDITS file for full details.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/* The functions in this file handle authentication.
+ * They DO NOT perform access control or auditing.
+ * See acl.c for access control and client_side.c for auditing */
+
+
+#include "squid.h"
+#include "auth_basic.h"
+
+static void
+authenticateStateFree(authenticateStateData * r)
+{
+ cbdataFree(r);
+}
+
+/* Basic Scheme */
+
+static HLPCB authenticateBasicHandleReply;
+static AUTHSACTIVE authenticateBasicActive;
+static AUTHSAUTHED authenticateBasicAuthenticated;
+static AUTHSAUTHUSER authenticateBasicAuthenticateUser;
+static AUTHSDIRECTION authenticateBasicDirection;
+static AUTHSDECODE authenticateBasicDecodeAuth;
+static AUTHSDUMP authBasicCfgDump;
+static AUTHSFIXERR authenticateBasicFixErrorHeader;
+static AUTHSFREE authenticateBasicFreeUser;
+static AUTHSFREECONFIG authBasicFreeConfig;
+static AUTHSPARSE authBasicParse;
+static AUTHSINIT authBasicInit;
+static AUTHSSTART authenticateBasicStart;
+static AUTHSSTATS authenticateBasicStats;
+static AUTHSUSERNAME authenticateBasicUsername;
+static AUTHSSHUTDOWN authBasicDone;
+
+static helper *basicauthenticators = NULL;
+
+static auth_basic_config *basicConfig = NULL;
+
+static int authbasic_initialised = 0;
+MemPool *basic_data_pool = NULL;
+
+/*
+ *
+ * Private Functions
+ *
+ */
+
+void
+authBasicDone(void)
+{
+// memPoolDestroy(ufs_state_pool);
+ if (basicauthenticators)
+ helperShutdown(basicauthenticators);
+ authbasic_initialised = 0;
+ if (!shutting_down)
+ return;
+ helperFree(basicauthenticators);
+ basicauthenticators = NULL;
+ memPoolDestroy(basic_data_pool);
+}
+
+void
+authSchemeSetup_basic(authscheme_entry_t * authscheme)
+{
+ assert(!authbasic_initialised);
+ authscheme->Active = authenticateBasicActive;
+ authscheme->parse = authBasicParse;
+ authscheme->dump = authBasicCfgDump;
+ authscheme->init = authBasicInit;
+ authscheme->authAuthenticate = authenticateBasicAuthenticateUser;
+ authscheme->authenticated = authenticateBasicAuthenticated;
+ authscheme->authFixHeader = authenticateBasicFixErrorHeader;
+ authscheme->FreeUser = authenticateBasicFreeUser;
+ authscheme->freeconfig = authBasicFreeConfig;
+ authscheme->authStart = authenticateBasicStart;
+ authscheme->authStats = authenticateBasicStats;
+ authscheme->authUserUsername = authenticateBasicUsername;
+ authscheme->getdirection = authenticateBasicDirection;
+ authscheme->oncloseconnection = NULL;
+ authscheme->decodeauth = authenticateBasicDecodeAuth;
+ authscheme->donefunc = authBasicDone;
+}
+
+int
+authenticateBasicActive()
+{
+#if 0
+ if (authbasic_initialised)
+ return 1;
+ else
+ return 0;
+#endif
+ if ((basicConfig != NULL) && (basicConfig->authenticate != NULL) &&
+ (basicConfig->authenticateChildren != 0) && (basicConfig->basicAuthRealm != NULL))
+ return 1;
+ return 0;
+}
+
+int
+authenticateBasicAuthenticated(auth_user_request_t * auth_user_request)
+{
+ basic_data *basic_auth = auth_user_request->auth_user->scheme_data;
+ if ((auth_user_request->auth_user->flags.credentials_ok == 1) && (basic_auth->credentials_checkedtime + basicConfig->credentialsTTL > squid_curtime))
+ return 1;
+ debug(29, 4) ("User not authenticated or credentials need rechecking.\n");
+ return 0;
+}
+
+int
+authenticateBasiccmpUsername(basic_data * u1, basic_data * u2)
+{
+ return strcmp(u1->username, u2->username);
+}
+
+/* log a basic user in
+ */
+static void
+authenticateBasicAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
+{
+#if 0
+ auth_user_hash_pointer *usernamehash, *proxy_auth_hash = NULL;
+#endif
+ auth_user_t *auth_user;
+ basic_data *basic_auth;
+#if 0
+//, *temp_auth;
+ const char *proxy_auth;
+#endif
+
+ assert(auth_user_request->auth_user != NULL);
+ auth_user = auth_user_request->auth_user;
+
+ /* if the password is not ok, do an identity */
+ if (auth_user->flags.credentials_ok != 1)
+ return;
+
+ assert(auth_user->scheme_data != NULL);
+ basic_auth = auth_user->scheme_data;
+
+ /* are we about to recheck the credentials externally? */
+ if ((basic_auth->credentials_checkedtime + basicConfig->credentialsTTL) <= squid_curtime) {
+ debug(29, 4) ("authBasicAuthenticate: credentials expired - rechecking\n");
+ return;
+ }
+#if 0
+ /* get the header. */
+ proxy_auth = httpHeaderGetStr(&request->header, type);
+#endif
+
+ /* we have been through the external helper, and the credentials haven't expired */
+ debug(29, 9) ("authenticateBasicAuthenticateuser: user '%s' authenticated\n",
+ basic_auth->username);
+
+ /* Decode now takes care of finding the auth_user struct in the cache */
+#if 0
+ /* see if this is an existing user with a different proxy_auth string */
+ if ((usernamehash = hash_lookup(proxy_auth_username_cache,
+ basic_auth->username))) {
+ while ((usernamehash->auth_user->auth_type != auth_user->auth_type) &&
+ (usernamehash->next) &&
+ !authenticateBasiccmpUsername(usernamehash->auth_user->scheme_data, basic_auth))
+ usernamehash = usernamehash->next;
+ if (usernamehash->auth_user->auth_type == auth_user->auth_type) {
+ /*
+ * add another link from the new proxy_auth to the
+ * auth_user structure and update the information */
+ assert(proxy_auth_hash == NULL);
+ authenticateProxyAuthCacheAddLink(proxy_auth, usernamehash->auth_user);
+ /* maybe the p/w changed. update in the old structure */
+ temp_auth = usernamehash->auth_user->scheme_data;
+ xfree(temp_auth->passwd);
+ temp_auth->passwd = basic_auth->passwd;
+ basic_auth->passwd = NULL;
+ /* and remove the temporary structure */
+ authenticateAuthUserUnlock(auth_user);
+#if 0
+ authenticateFreeProxyAuthUser(auth_user);
+#endif
+ auth_user = usernamehash->auth_user;
+ /* and reference the existing basic data structure */
+ basic_auth = auth_user->scheme_data;
+ /* lock the structure for this request */
+ authenticateAuthUserLock(auth_user);
+ }
+ } else {
+ /* store user in hash's */
+ authenticateUserNameCacheAdd(auth_user);
+ authenticateProxyAuthCacheAddLink(proxy_auth, auth_user);
+ }
+ /* auth_user is now linked, we reset these values */
+#endif
+ /* after external auth occurs anyway */
+ auth_user->expiretime = current_time.tv_sec;
+ auth_user->ip_expiretime = squid_curtime;
+ return;
+}
+
+int
+authenticateBasicDirection(auth_user_request_t * auth_user_request)
+{
+/* null auth_user is checked for by authenticateDirection */
+ auth_user_t *auth_user = auth_user_request->auth_user;
+ basic_data *basic_auth = auth_user->scheme_data;
+ switch (auth_user->flags.credentials_ok) {
+ case 0: /* not checked */
+ return -1;
+ case 1: /* checked & ok */
+ if (basic_auth->credentials_checkedtime + basicConfig->credentialsTTL <= squid_curtime)
+ return -1;
+ return 0;
+ case 2: /* paused while waiting for a username:password check on another request */
+ return -1;
+ case 3: /* authentication process failed. */
+ return -2;
+ }
+ return -2;
+}
+
+void
+authenticateBasicFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
+{
+ if (basicConfig->authenticate) {
+ debug(29, 9) ("authenticateFixErrorHeader: Sending type:%d header: 'Basic realm=\"%s\"'\n", type, basicConfig->basicAuthRealm);
+ httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", basicConfig->basicAuthRealm);
+ }
+}
+
+/* free any allocated configuration details */
+void
+authBasicFreeConfig(authScheme * scheme)
+{
+ if (basicConfig == NULL)
+ return;
+ assert(basicConfig == scheme->scheme_data);
+ if (basicConfig->authenticate)
+ wordlistDestroy(&basicConfig->authenticate);
+ if (basicConfig->basicAuthRealm)
+ safe_free(basicConfig->basicAuthRealm);
+ xfree(basicConfig);
+ basicConfig = NULL;
+}
+
+void
+authenticateBasicFreeUser(auth_user_t * auth_user)
+{
+ basic_data *basic_auth = auth_user->scheme_data;
+ debug(29, 5) ("authenticateBasicFreeUser: Clearing Basic scheme data\n");
+ if (basic_auth->username)
+ xfree(basic_auth->username);
+ if (basic_auth->passwd)
+ xfree(basic_auth->passwd);
+ memPoolFree(basic_data_pool, auth_user->scheme_data);
+ auth_user->scheme_data = NULL;
+}
+
+static void
+authenticateBasicHandleReply(void *data, char *reply)
+{
+ authenticateStateData *r = data;
+ auth_user_t *auth_user;
+ basic_data *basic_auth;
+ auth_basic_queue_node *node, *tmpnode;
+ int valid;
+ char *t = NULL;
+ debug(29, 9) ("authenticateBasicHandleReply: {%s}\n", reply ? reply : "<NULL>");
+ if (reply) {
+ if ((t = strchr(reply, ' ')))
+ *t = '\0';
+ if (*reply == '\0')
+ reply = NULL;
+ }
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_BASIC);
+ auth_user = r->auth_user_request->auth_user;
+ basic_auth = auth_user->scheme_data;
+ if (reply && (strncasecmp(reply, "OK", 2) == 0))
+ auth_user->flags.credentials_ok = 1;
+ else
+ auth_user->flags.credentials_ok = 3;
+ basic_auth->credentials_checkedtime = squid_curtime;
+ valid = cbdataValid(r->data);
+ cbdataUnlock(r->data);
+ if (valid)
+ r->handler(r->data, NULL);
+ node = basic_auth->auth_queue;
+ while (node) {
+ tmpnode = node->next;
+ valid = cbdataValid(node->data);
+ cbdataUnlock(node->data);
+ if (valid)
+ node->handler(node->data, NULL);
+ xfree(node);
+ node = tmpnode;
+ }
+#if 0
+ if (valid)
+ r->handler(r->data, reply);
+#endif
+ authenticateStateFree(r);
+}
+
+static void
+authBasicCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
+{
+ auth_basic_config *config = scheme->scheme_data;
+ wordlist *list = config->authenticate;
+ storeAppendPrintf(entry, "%s %s", name, "basic");
+ while (list != NULL) {
+ storeAppendPrintf(entry, " %s", list->key);
+ list = list->next;
+ }
+ storeAppendPrintf(entry, "\n%s %s realm %s\n%s %s children %d\n%s %s credentialsttl %d seconds\n",
+ name, "basic", config->basicAuthRealm,
+ name, "basic", config->authenticateChildren,
+ name, "basic", config->credentialsTTL);
+
+}
+
+static void
+authBasicParse(authScheme * scheme, int n_configured, char *param_str)
+{
+ if (scheme->scheme_data == NULL) {
+ assert(basicConfig == NULL);
+ /* this is the first param to be found */
+ scheme->scheme_data = xmalloc(sizeof(auth_basic_config));
+ memset(scheme->scheme_data, 0, sizeof(auth_basic_config));
+ basicConfig = scheme->scheme_data;
+ basicConfig->authenticateChildren = 5;
+ }
+ basicConfig = scheme->scheme_data;
+ if (strcasecmp(param_str, "program") == 0) {
+ parse_wordlist(&basicConfig->authenticate);
+ requirePathnameExists("authparam basic program", basicConfig->authenticate->key);
+ } else if (strcasecmp(param_str, "children") == 0) {
+ parse_int(&basicConfig->authenticateChildren);
+ } else if (strcasecmp(param_str, "realm") == 0) {
+ parse_eol(&basicConfig->basicAuthRealm);
+ } else if (strcasecmp(param_str, "credentialsttl") == 0) {
+ parse_time_t(&basicConfig->credentialsTTL);
+ } else {
+ debug(28, 0) ("unrecognised basic auth scheme parameter '%s'\n", param_str);
+ }
+}
+
+static void
+authenticateBasicStats(StoreEntry * sentry)
+{
+ storeAppendPrintf(sentry, "Basic Authenticator Statistics:\n");
+ helperStats(sentry, basicauthenticators);
+}
+
+CBDATA_TYPE(authenticateStateData);
+
+/* authenticateBasicUsername: return a pointer to the username in the */
+char *
+authenticateBasicUsername(auth_user_t * auth_user)
+{
+ basic_data *basic_auth = auth_user->scheme_data;
+ if (basic_auth)
+ return basic_auth->username;
+ return NULL;
+}
+
+basic_data *
+authBasicDataNew()
+{
+ basic_data *temp;
+ temp = memPoolAlloc(basic_data_pool);
+ assert(temp != NULL);
+ temp->username = NULL;
+ temp->passwd = NULL;
+ temp->auth_queue = NULL;
+ return temp;
+}
+
+void
+authBasicDataFree(basic_data * basic_auth)
+{
+}
+
+auth_user_t *
+authBasicAuthUserFindUsername(const char *username)
+{
+ auth_user_hash_pointer *usernamehash;
+ auth_user_t *auth_user;
+ debug(29, 9) ("authBasicAuthUserFindUsername: Looking for user '%s'\n", username);
+ if (username && (usernamehash = hash_lookup(proxy_auth_username_cache, username))) {
+ while ((usernamehash->auth_user->auth_type != AUTH_BASIC) &&
+ (usernamehash->next))
+ usernamehash = usernamehash->next;
+ auth_user = NULL;
+ if (usernamehash->auth_user->auth_type == AUTH_BASIC) {
+ auth_user = usernamehash->auth_user;
+ }
+ return auth_user;
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Decode a Basic [Proxy-]Auth string, linking the passed auth_user_request structure
+ * to any existing user structure or creating one if needed. Note that just returning
+ * will be treated as "cannot decode credentials". Use the message field to return a
+ * descriptive message to the user.
+ */
+
+static void
+authenticateBasicDecodeAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
+{
+ char *sent_auth;
+ char *cleartext;
+ basic_data *basic_auth, local_basic;
+ auth_user_t *auth_user;
+ dlink_node *node;
+
+ /* decode the username */
+ /* trim BASIC from string */
+ while (!xisspace(*proxy_auth))
+ proxy_auth++;
+
+ local_basic.passwd = NULL;
+
+ /* Trim leading whitespace before decoding */
+ while (xisspace(*proxy_auth))
+ proxy_auth++;
+ /* username and password */
+ sent_auth = xstrdup(proxy_auth);
+ /* Trim trailing \n before decoding */
+ strtok(sent_auth, "\n");
+ cleartext = uudecode(sent_auth);
+ xfree(sent_auth);
+ /*
+ * Don't allow NL or CR in the credentials.
+ * Oezguer Kesim <oec@codeblau.de>
+ */
+ strtok(cleartext, "\r\n");
+ debug(29, 9) ("authenticateBasicDecodeAuth: cleartext = '%s'\n", cleartext);
+ local_basic.username = xstrndup(cleartext, USER_IDENT_SZ);
+ xfree(cleartext);
+ if ((cleartext = strchr(local_basic.username, ':')) != NULL)
+ *(cleartext)++ = '\0';
+ local_basic.passwd = cleartext;
+ if (cleartext == NULL) {
+ debug(29, 4) ("authenticateBasicDecodeAuth: no password in proxy authorization header '%s'\n",
+ proxy_auth);
+ local_basic.passwd = NULL;
+ auth_user_request->message = xstrdup("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
+ } else if (*cleartext == '\0') {
+ debug(29, 4) ("authenticateBasicDecodeAuth: Disallowing empty password,"
+ "user is '%s'\n", local_basic.username);
+ local_basic.passwd = NULL;
+ auth_user_request->message = xstrdup("Request denied because you provided an empty password. Users MUST have a password.");
+ }
+ /* special case: we have to free the strings for user and password
+ * if we are not returning a filled out structure
+ */
+ if (local_basic.passwd == NULL) {
+ if (local_basic.username) {
+ /* log the username */
+ debug(29, 9) ("authBasicDecodeAuth: Creating new user for logging '%s'\n", local_basic.username);
+ /* new auth_user */
+ auth_user = authenticateAuthUserNew("basic");
+ /* new scheme data */
+ basic_auth = authBasicDataNew();
+ /* save the credentials */
+ basic_auth->username = local_basic.username;
+ /* link the scheme data in */
+ auth_user->scheme_data = basic_auth;
+ /* set the auth_user type */
+ auth_user->auth_type = AUTH_BROKEN;
+ /* link the request to the user */
+ auth_user_request->auth_user = auth_user;
+ /* lock for the auth_user_request link */
+ authenticateAuthUserLock(auth_user);
+ node = dlinkNodeNew();
+ dlinkAdd(auth_user_request, node, &auth_user->requests);
+#if 0
+ xfree(local_basic.username);
+#endif
+ }
+#if 0
+ memPoolFree(basic_data_pool, basic_auth);
+ auth_user->scheme_data = NULL;
+#endif
+ return;
+ } else {
+ local_basic.passwd = xstrndup(cleartext, USER_IDENT_SZ);
+ }
+
+ /* now lookup and see if we have a matching auth_user structure in memory. */
+
+ if ((auth_user = authBasicAuthUserFindUsername(local_basic.username)) == NULL) {
+ /* the user doesn't exist in the username cache yet */
+ debug(29, 9) ("authBasicDecodeAuth: Creating new user '%s'\n", local_basic.username);
+ /* new auth_user */
+ auth_user = authenticateAuthUserNew("basic");
+ /* new scheme data */
+ basic_auth = authBasicDataNew();
+ /* save the credentials */
+ basic_auth->username = local_basic.username;
+ basic_auth->passwd = local_basic.passwd;
+ /* link the scheme data in */
+ auth_user->scheme_data = basic_auth;
+ /* set the auth_user type */
+ auth_user->auth_type = AUTH_BASIC;
+ /* current time for timeouts */
+ auth_user->expiretime = current_time.tv_sec;
+ auth_user->ip_expiretime = squid_curtime;
+
+ /* this auth_user struct is the 'lucky one' to get added to the username cache */
+ /* the requests after this link to the auth_user */
+ /* store user in hash */
+ authenticateUserNameCacheAdd(auth_user);
+ } else {
+ debug(29, 9) ("authBasicDecodeAuth: Found user '%s' in the user cache as '%d'\n", local_basic.username, auth_user);
+ xfree(local_basic.username);
+ basic_auth = auth_user->scheme_data;
+ if (strcmp(local_basic.passwd, basic_auth->passwd)) {
+ debug(29, 4) ("authBasicDecodeAuth: new password found. Updating in user master record and resetting auth state to unchecked\n");
+ auth_user->flags.credentials_ok = 0;
+ xfree(basic_auth->passwd);
+ basic_auth->passwd = local_basic.passwd;
+ } else
+ xfree(local_basic.passwd);
+ }
+ /* link the request to the user */
+ auth_user_request->auth_user = auth_user;
+ /* lock for the auth_user_request link */
+ authenticateAuthUserLock(auth_user);
+ node = dlinkNodeNew();
+ dlinkAdd(auth_user_request, node, &auth_user->requests);
+ return;
+}
+
+/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
+ * config file */
+static void
+authBasicInit(authScheme * scheme)
+{
+ static int init = 0;
+ if (basicConfig->authenticate) {
+ if (!basic_data_pool)
+ basic_data_pool = memPoolCreate("Basic Scheme User Data", sizeof(basic_data));
+ authbasic_initialised = 1;
+ if (basicauthenticators == NULL)
+ basicauthenticators = helperCreate("basicauthenticator");
+ basicauthenticators->cmdline = basicConfig->authenticate;
+ basicauthenticators->n_to_start = basicConfig->authenticateChildren;
+ basicauthenticators->ipc_type = IPC_TCP_SOCKET;
+ helperOpenServers(basicauthenticators);
+ if (!init) {
+ cachemgrRegister("basicauthenticator",
+ "User Authenticator Stats",
+ authenticateBasicStats, 0, 1);
+ init++;
+ }
+ CBDATA_INIT_TYPE(authenticateStateData);
+ }
+}
+
+/* send the initial data to a basic authenticator module */
+static void
+authenticateBasicStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ char buf[8192];
+ basic_data *basic_auth;
+ assert(auth_user_request);
+ assert(handler);
+ assert(auth_user_request->auth_user->auth_type == AUTH_BASIC);
+ assert(auth_user_request->auth_user->scheme_data != NULL);
+ basic_auth = auth_user_request->auth_user->scheme_data;
+ debug(29, 9) ("authenticateStart: '%s:%s'\n", basic_auth->username,
+ basic_auth->passwd);
+ if (basicConfig->authenticate == NULL) {
+ handler(data, NULL);
+ return;
+ }
+ /* check to see if the auth_user already has a request outstanding */
+ if (auth_user_request->auth_user->flags.credentials_ok == 2) {
+ /* there is a request with the same credentials already being verified */
+ auth_basic_queue_node *node;
+ node = xmalloc(sizeof(auth_basic_queue_node));
+ assert(node);
+ /* save the details */
+ node->next = basic_auth->auth_queue;
+ basic_auth->auth_queue = node;
+ node->auth_user_request = auth_user_request;
+ node->handler = handler;
+ node->data = data;
+ cbdataLock(data);
+ return;
+ } else {
+ r = CBDATA_ALLOC(authenticateStateData, NULL);
+ r->handler = handler;
+ cbdataLock(data);
+ r->data = data;
+ r->auth_user_request = auth_user_request;
+ /* mark the user as haveing verification in progress */
+ auth_user_request->auth_user->flags.credentials_ok = 2;
+ snprintf(buf, 8192, "%s %s\n", basic_auth->username, basic_auth->passwd);
+ helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
+ }
+}
--- /dev/null
+/*
+ * auth_basic.h
+ * Internal declarations for the basic auth module
+ */
+
+#ifndef __AUTH_BASIC_H__
+#define __AUTH_BASIC_H__
+
+
+#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
+
+/* Generic */
+typedef struct {
+ void *data;
+ auth_user_request_t *auth_user_request;
+ RH *handler;
+} authenticateStateData;
+
+typedef struct _auth_basic_queue_node auth_basic_queue_node;
+
+/* queue of auth requests waiting for verification to occur */
+struct _auth_basic_queue_node {
+ auth_basic_queue_node *next;
+ auth_user_request_t *auth_user_request;
+ RH *handler;
+ void *data;
+};
+
+struct _basic_data {
+ char *username;
+ char *passwd;
+ time_t credentials_checkedtime;
+ auth_basic_queue_node *auth_queue;
+};
+
+/* configuration runtime data */
+struct _auth_basic_config {
+ int authenticateChildren;
+ char *basicAuthRealm;
+ wordlist *authenticate;
+ time_t credentialsTTL;
+};
+
+typedef struct _auth_basic_config auth_basic_config;
+
+typedef struct _basic_data basic_data;
+
+
+#endif
--- /dev/null
+#
+# Makefile for the ntlm authentication scheme module for the Squid Object Cache server
+#
+# $Id: Makefile.in,v 1.1 2001/01/07 23:36:48 hno Exp $
+#
+
+AUTH = ntlm
+
+SUBDIRS = helpers
+
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+CC = @CC@
+MAKEDEPEND = @MAKEDEPEND@
+AR_R = @AR_R@
+RANLIB = @RANLIB@
+AC_CFLAGS = @CFLAGS@
+SHELL = /bin/sh
+
+INCLUDE = -I../../../include -I$(top_srcdir)/include -I$(top_srcdir)/src/
+CFLAGS = $(AC_CFLAGS) $(INCLUDE) $(DEFINES)
+
+OUT = ../$(AUTH).a
+
+OBJS = \
+ auth_ntlm.o
+
+
+all install: $(OUT)
+ @for dir in $(SUBDIRS); do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) $@" || exit 1; \
+ fi; \
+ done;
+
+$(OUT): $(OBJS)
+ @rm -f ../stamp
+ $(AR_R) $(OUT) $(OBJS)
+ $(RANLIB) $(OUT)
+
+$(OBJS): $(top_srcdir)/include/version.h ../../../include/autoconf.h
+
+.c.o:
+ @rm -f ../stamp
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -rf *.o *pure_* core ../$(AUTH).a
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) clean"; \
+ fi; \
+ done
+
+distclean: clean
+ -rm -f Makefile
+ -rm -f Makefile.bak
+ -rm -f tags
+ -for dir in *; do \
+ if [ -f $$dir/Makefile ]; then \
+ sh -c "cd $$dir && $(MAKE) distclean"; \
+ fi; \
+ done
+
+tags:
+ ctags *.[ch] $(top_srcdir)/src/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[ch]
+
+depend:
+ $(MAKEDEPEND) $(INCLUDE) -fMakefile *.c
--- /dev/null
+
+/*
+ * $Id: auth_ntlm.cc,v 1.1 2001/01/07 23:36:48 hno Exp $
+ *
+ * DEBUG: section 29 NTLM Authenticator
+ * AUTHOR: Robert Collins
+ *
+ * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
+ * ----------------------------------------------------------
+ *
+ * Squid is the result of efforts by numerous individuals from the
+ * Internet community. Development is led by Duane Wessels of the
+ * National Laboratory for Applied Network Research and funded by the
+ * National Science Foundation. Squid is Copyrighted (C) 1998 by
+ * the Regents of the University of California. Please see the
+ * COPYRIGHT file for full details. Squid incorporates software
+ * developed and/or copyrighted by other sources. Please see the
+ * CREDITS file for full details.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/* The functions in this file handle authentication.
+ * They DO NOT perform access control or auditing.
+ * See acl.c for access control and client_side.c for auditing */
+
+
+#include "squid.h"
+#include "auth_ntlm.h"
+
+static void
+authenticateStateFree(authenticateStateData * r)
+{
+ cbdataFree(r);
+}
+
+/* NTLM Scheme */
+static HLPSCB authenticateNTLMHandleReply;
+static HLPSCB authenticateNTLMHandleplaceholder;
+static AUTHSACTIVE authenticateNTLMActive;
+static AUTHSAUTHED authNTLMAuthenticated;
+static AUTHSAUTHUSER authenticateNTLMAuthenticateUser;
+static AUTHSFIXERR authenticateNTLMFixErrorHeader;
+static AUTHSFREE authenticateNTLMFreeUser;
+static AUTHSDIRECTION authenticateNTLMDirection;
+static AUTHSDECODE authenticateDecodeNTLMAuth;
+static AUTHSDUMP authNTLMCfgDump;
+static AUTHSFREECONFIG authNTLMFreeConfig;
+static AUTHSINIT authNTLMInit;
+static AUTHSONCLOSEC authenticateNTLMOnCloseConnection;
+static AUTHSUSERNAME authenticateNTLMUsername;
+static AUTHSREQFREE authNTLMAURequestFree;
+static AUTHSPARSE authNTLMParse;
+static AUTHSSTART authenticateNTLMStart;
+static AUTHSSTATS authenticateNTLMStats;
+static AUTHSSHUTDOWN authNTLMDone;
+
+/* helper callbacks to handle per server state data */
+static HLPSAVAIL authenticateNTLMHelperServerAvailable;
+static HLPSONEQ authenticateNTLMHelperServerOnEmpty;
+
+static statefulhelper *ntlmauthenticators = NULL;
+
+CBDATA_TYPE(authenticateStateData);
+
+static int authntlm_initialised = 0;
+
+MemPool *ntlm_helper_state_pool = NULL;
+MemPool *ntlm_user_pool = NULL;
+MemPool *ntlm_request_pool = NULL;
+static auth_ntlm_config *ntlmConfig = NULL;
+
+static hash_table *proxy_auth_cache = NULL;
+
+/*
+ *
+ * Private Functions
+ *
+ */
+
+void
+authNTLMDone(void)
+{
+// memPoolDestroy(ufs_state_pool);
+
+ if (ntlmauthenticators)
+ helperStatefulShutdown(ntlmauthenticators);
+ authntlm_initialised = 0;
+ if (!shutting_down)
+ return;
+ helperStatefulFree(ntlmauthenticators);
+ ntlmauthenticators = NULL;
+ memPoolDestroy(ntlm_helper_state_pool);
+ memPoolDestroy(ntlm_request_pool);
+ memPoolDestroy(ntlm_user_pool);
+
+}
+
+/* free any allocated configuration details */
+void
+authNTLMFreeConfig(authScheme * scheme)
+{
+ if (ntlmConfig == NULL)
+ return;
+ assert(ntlmConfig == scheme->scheme_data);
+ if (ntlmConfig->authenticate)
+ wordlistDestroy(&ntlmConfig->authenticate);
+ xfree(ntlmConfig);
+ ntlmConfig = NULL;
+}
+
+static void
+authNTLMCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
+{
+ auth_ntlm_config *config = scheme->scheme_data;
+ wordlist *list = config->authenticate;
+ storeAppendPrintf(entry, "%s %s", name, "ntlm");
+ while (list != NULL) {
+ storeAppendPrintf(entry, " %s", list->key);
+ list = list->next;
+ }
+ storeAppendPrintf(entry, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
+ name, "ntlm", config->authenticateChildren,
+ name, "ntlm", config->challengeuses,
+ name, "ntlm", config->challengelifetime);
+
+}
+
+static void
+authNTLMParse(authScheme * scheme, int n_configured, char *param_str)
+{
+ if (scheme->scheme_data == NULL) {
+ assert(ntlmConfig == NULL);
+ /* this is the first param to be found */
+ scheme->scheme_data = xmalloc(sizeof(auth_ntlm_config));
+ memset(scheme->scheme_data, 0, sizeof(auth_ntlm_config));
+ ntlmConfig = scheme->scheme_data;
+ ntlmConfig->authenticateChildren = 5;
+ ntlmConfig->challengeuses = 0;
+ ntlmConfig->challengelifetime = 60;
+ }
+ ntlmConfig = scheme->scheme_data;
+ if (strcasecmp(param_str, "program") == 0) {
+ parse_wordlist(&ntlmConfig->authenticate);
+ requirePathnameExists("authparam ntlm program", ntlmConfig->authenticate->key);
+ } else if (strcasecmp(param_str, "children") == 0) {
+ parse_int(&ntlmConfig->authenticateChildren);
+ } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
+ parse_int(&ntlmConfig->challengeuses);
+ } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
+ parse_time_t(&ntlmConfig->challengelifetime);
+ } else {
+ debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str);
+ }
+}
+
+
+void
+authSchemeSetup_ntlm(authscheme_entry_t * authscheme)
+{
+#if 0
+ static int ntlminit = 0;
+#endif
+ assert(!authntlm_initialised);
+ authscheme->Active = authenticateNTLMActive;
+ authscheme->parse = authNTLMParse;
+ authscheme->dump = authNTLMCfgDump;
+ authscheme->requestFree = authNTLMAURequestFree;
+ authscheme->freeconfig = authNTLMFreeConfig;
+ authscheme->init = authNTLMInit;
+ authscheme->authAuthenticate = authenticateNTLMAuthenticateUser;
+ authscheme->authenticated = authNTLMAuthenticated;
+ authscheme->authFixHeader = authenticateNTLMFixErrorHeader;
+ authscheme->FreeUser = authenticateNTLMFreeUser;
+ authscheme->authStart = authenticateNTLMStart;
+ authscheme->authStats = authenticateNTLMStats;
+ authscheme->authUserUsername = authenticateNTLMUsername;
+ authscheme->getdirection = authenticateNTLMDirection;
+ authscheme->decodeauth = authenticateDecodeNTLMAuth;
+ authscheme->donefunc = authNTLMDone;
+ authscheme->oncloseconnection = authenticateNTLMOnCloseConnection;
+}
+
+/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
+ * config file */
+static void
+authNTLMInit(authScheme * scheme)
+{
+ static int ntlminit = 0;
+ if (ntlmConfig->authenticate) {
+ if (!ntlm_helper_state_pool)
+ ntlm_helper_state_pool = memPoolCreate("NTLM Helper State data", sizeof(ntlm_helper_state_t));
+ if (!ntlm_user_pool)
+ ntlm_user_pool = memPoolCreate("NTLM Scheme User Data", sizeof(ntlm_user_t));
+ if (!ntlm_request_pool)
+ ntlm_request_pool = memPoolCreate("NTLM Scheme Request Data", sizeof(ntlm_request_t));
+ authntlm_initialised = 1;
+ if (ntlmauthenticators == NULL)
+ ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
+ if (!proxy_auth_cache)
+ proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
+ assert(proxy_auth_cache);
+ ntlmauthenticators->cmdline = ntlmConfig->authenticate;
+ ntlmauthenticators->n_to_start = ntlmConfig->authenticateChildren;
+ ntlmauthenticators->ipc_type = IPC_TCP_SOCKET;
+ ntlmauthenticators->datapool = ntlm_helper_state_pool;
+ ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
+ ntlmauthenticators->OnEmptyQueue = authenticateNTLMHelperServerOnEmpty;
+ helperStatefulOpenServers(ntlmauthenticators);
+ /* TODO: In here send the initial YR to preinitialise the challenge cache */
+ /* Think about this... currently we ask when the challenge is needed. Better? */
+ if (!ntlminit) {
+ cachemgrRegister("ntlmauthenticator",
+ "User NTLM Authenticator Stats",
+ authenticateNTLMStats, 0, 1);
+ ntlminit++;
+ }
+ CBDATA_INIT_TYPE(authenticateStateData);
+ }
+}
+
+int
+authenticateNTLMActive()
+{
+ if (authntlm_initialised)
+ return 1;
+ else
+ return 0;
+}
+
+/* NTLM Scheme */
+
+int
+authenticateNTLMDirection(auth_user_request_t * auth_user_request)
+{
+ ntlm_request_t *ntlm_request = auth_user_request->scheme_data;
+/* null auth_user is checked for by authenticateDirection */
+ switch (ntlm_request->auth_state) {
+ case AUTHENTICATE_STATE_NONE: /* no progress at all. */
+ debug(28, 1) ("authenticateNTLMDirection: called before NTLM Authenticate!. Report a bug to squid-dev.\n");
+ return -2;
+ case AUTHENTICATE_STATE_NEGOTIATE: /* send to helper */
+ case AUTHENTICATE_STATE_RESPONSE: /*send to helper */
+ return -1;
+ case AUTHENTICATE_STATE_CHALLENGE: /* send to client */
+ return 1;
+ case AUTHENTICATE_STATE_DONE: /* do nothing.. */
+ return 0;
+ }
+ return -2;
+}
+
+/*
+ * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
+ * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
+ * else.
+ */
+void
+authenticateNTLMFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
+{
+ ntlm_request_t *ntlm_request;
+ if (ntlmConfig->authenticate) {
+ /* New request, no user details */
+ if (auth_user_request == NULL) {
+ debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+ /* drop the connection */
+ httpHeaderDelByName(&rep->header, "keep-alive");
+ /* NTLM has problems if the initial connection is not dropped
+ * I haven't checked the RFC compliance of this hack - RBCollins */
+ request->flags.proxy_keepalive = 0;
+ } else {
+ ntlm_request = auth_user_request->scheme_data;
+ switch (ntlm_request->auth_state) {
+ case AUTHENTICATE_STATE_NONE:
+ debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
+ httpHeaderPutStrf(&rep->header, type, "NTLM");
+ /* drop the connection */
+ httpHeaderDelByName(&rep->header, "keep-alive");
+ /* NTLM has problems if the initial connection is not dropped
+ * I haven't checked the RFC compliance of this hack - RBCollins */
+ request->flags.proxy_keepalive = 0;
+ break;
+ case AUTHENTICATE_STATE_CHALLENGE:
+ /* we are 'waiting' for a response */
+ /* pass the challenge to the client */
+ debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->authchallenge);
+ httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->authchallenge);
+ break;
+ default:
+ debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
+ fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
+ }
+ }
+ }
+}
+
+void
+authNTLMRequestFree(ntlm_request_t * ntlm_request)
+{
+ if (!ntlm_request)
+ return;
+ if (ntlm_request->ntlmnegotiate)
+ xfree(ntlm_request->ntlmnegotiate);
+ if (ntlm_request->authchallenge)
+ xfree(ntlm_request->authchallenge);
+ if (ntlm_request->ntlmauthenticate)
+ xfree(ntlm_request->ntlmauthenticate);
+ memPoolFree(ntlm_request_pool, ntlm_request);
+}
+
+void
+authNTLMAURequestFree(auth_user_request_t * auth_user_request)
+{
+ if (auth_user_request->scheme_data)
+ authNTLMRequestFree((ntlm_request_t *) auth_user_request->scheme_data);
+ auth_user_request->scheme_data = NULL;
+}
+
+void
+authenticateNTLMFreeUser(auth_user_t * auth_user)
+{
+ dlink_node *link, *tmplink;
+ ntlm_user_t *ntlm_user = auth_user->scheme_data;
+ auth_user_hash_pointer *proxy_auth_hash;
+
+ debug(29, 5) ("authenticateNTLMFreeUser: Clearing NTLM scheme data\n");
+ if (ntlm_user->username)
+ xfree(ntlm_user->username);
+ /* were they linked in by one or more proxy-authenticate headers */
+ link = ntlm_user->proxy_auth_list.head;
+ while (link) {
+ debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%d'\n", link->data);
+ proxy_auth_hash = link->data;
+ tmplink = link;
+ link = link->next;
+ dlinkDelete(tmplink, &ntlm_user->proxy_auth_list);
+ hash_remove_link(proxy_auth_cache, (hash_link *) proxy_auth_hash);
+ /* free the key (usually the proxy_auth header) */
+ xfree(proxy_auth_hash->key);
+ memFree(proxy_auth_hash, MEM_AUTH_USER_HASH);
+ }
+ memPoolFree(ntlm_user_pool, ntlm_user);
+ auth_user->scheme_data = NULL;
+}
+
+static stateful_helper_callback_t
+authenticateNTLMHandleplaceholder(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = data;
+ stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+ int valid;
+ /* we should only be called for placeholder requests - which have no reply string */
+ assert(reply == NULL);
+ assert(r->auth_user_request);
+ /* standard callback stuff */
+ valid = cbdataValid(r->data);
+ cbdataUnlock(r->data);
+ /* call authenticateNTLMStart to retry this request */
+ debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
+ authenticateNTLMStart(r->auth_user_request, r->handler, r->data);
+ authenticateStateFree(r);
+ return result;
+}
+
+static stateful_helper_callback_t
+authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
+{
+#if 0
+ authenticateStatefulStateData *r = data;
+#endif
+ authenticateStateData *r = data;
+ ntlm_helper_state_t *helperstate;
+ int valid;
+ stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+#if 0
+ void *nextserver = NULL;
+#endif
+ char *t = NULL;
+ auth_user_request_t *auth_user_request;
+ auth_user_t *auth_user;
+ ntlm_user_t *ntlm_user;
+ ntlm_request_t *ntlm_request;
+ debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%d' {%s}\n", lastserver, reply ? reply : "<NULL>");
+ valid = cbdataValid(r->data);
+ cbdataUnlock(r->data);
+ if (valid) {
+ if (reply) {
+ /* seperate out the useful data */
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ reply += 3;
+ /* we have been given a Challenge */
+ /* we should check we weren't given an empty challenge */
+#if 0
+ result = S_HELPER_RESERVE;
+#endif
+ /* copy the challenge to the state data */
+ helperstate = helperStatefulServerGetData(lastserver);
+ if (helperstate == NULL)
+ fatal("lost NTLm helper state! quitting\n");
+ helperstate->challenge = xstrndup(reply, NTLM_CHALLENGE_SZ + 5);
+ helperstate->challengeuses = 0;
+ helperstate->renewed = squid_curtime;
+ /* and we satisfy the request that happended on the refresh boundary */
+ /* note this code is now in two places FIXME */
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ auth_user_request = r->auth_user_request;
+ ntlm_request = auth_user_request->scheme_data;
+ assert(ntlm_request != NULL);
+ result = S_HELPER_DEFER;
+#if 0
+ nextserver = lastserver;
+#endif
+ debug(29, 9) ("authenticateNTLMHandleReply: helper '%d'\n", lastserver);
+ assert(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
+// auth_user->auth_data.ntlm_auth.auth_state = AUTHENTICATE_STATE_CHALLENGE;
+ ntlm_request->authhelper = lastserver;
+ ntlm_request->authchallenge = xstrndup(reply, NTLM_CHALLENGE_SZ + 5);
+ } else if (strncasecmp(reply, "AF ", 3) == 0) {
+ /* we're finished, release the helper */
+ reply += 3;
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ auth_user_request = r->auth_user_request;
+ assert(auth_user_request->scheme_data != NULL);
+ ntlm_request = auth_user_request->scheme_data;
+ auth_user = auth_user_request->auth_user;
+ ntlm_user = auth_user_request->auth_user->scheme_data;
+ assert(ntlm_user != NULL);
+ result = S_HELPER_RELEASE;
+ /* we only expect OK when finishing the handshake */
+ assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
+ ntlm_user->username = xstrndup(reply, MAX_LOGIN_SZ);
+ ntlm_request->authhelper = NULL;
+ auth_user->flags.credentials_ok = 1; /* login ok */
+ } else if (strncasecmp(reply, "NA ", 3) == 0) {
+ /* TODO: only work with auth_user here if it exists */
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ auth_user_request = r->auth_user_request;
+ auth_user = auth_user_request->auth_user;
+ assert(auth_user != NULL);
+ ntlm_user = auth_user->scheme_data;
+ ntlm_request = auth_user_request->scheme_data;
+ assert((ntlm_user != NULL) && (ntlm_request != NULL));
+ /* todo: action of Negotiate state on error */
+ result = S_HELPER_RELEASE; /*some error has occured. no more requests */
+ ntlm_request->authhelper = NULL;
+ auth_user->flags.credentials_ok = 2; /* Login/Usercode failed */
+ debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM.\n");
+ ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
+ if ((t = strchr(reply, ' '))) /* strip after a space */
+ *t = '\0';
+ } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ /* TODO kick off a refresh process. This can occur after a YR or after
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate NTLM start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
+ * Needing YR. */
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ auth_user_request = r->auth_user_request;
+ auth_user = auth_user_request->auth_user;
+ assert(auth_user != NULL);
+ ntlm_user = auth_user->scheme_data;
+ ntlm_request = auth_user_request->scheme_data;
+ assert((ntlm_user != NULL) && (ntlm_request != NULL));
+ result = S_HELPER_RELEASE; /*some error has occured. no more requests for
+ * this helper */
+ helperstate = helperStatefulServerGetData(ntlm_request->authhelper);
+ ntlm_request->authhelper = NULL;
+ if (ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE) {
+ /* The helper broke on YR. It automatically
+ * resets */
+ auth_user->flags.credentials_ok = 3; /* cannot process */
+ debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %d.\n", lastserver);
+ /* mark it for starving */
+ helperstate->starve = 1;
+ /* resubmit the request. This helper is currently busy, so we will get
+ * a different one. */
+ authenticateNTLMStart(auth_user_request, r->handler, r->data);
+ } else {
+ /* the helper broke on a KK */
+ /* first the standard KK stuff */
+ auth_user->flags.credentials_ok = 2; /* Login/Usercode failed */
+ debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM.\n");
+ ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
+ if ((t = strchr(reply, ' '))) /* strip after a space */
+ *t = '\0';
+ /* now we mark the helper for resetting. */
+ helperstate->starve = 1;
+ }
+ ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
+ } else {
+ /* TODO: only work with auth_user here if it exists */
+ assert(r->auth_user_request != NULL);
+ assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ auth_user_request = r->auth_user_request;
+ auth_user = auth_user_request->auth_user;
+ assert(auth_user != NULL);
+ ntlm_user = auth_user->scheme_data;
+ ntlm_request = auth_user_request->scheme_data;
+ assert((ntlm_user != NULL) && (ntlm_request != NULL));
+ debug(29, 1) ("authenticateNTLMHandleReply: Unsupported helper response, '%s'\n", reply);
+ /* restart the authentication process */
+ ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
+ auth_user->flags.credentials_ok = 3; /* cannot process */
+ ntlm_request->authhelper = NULL;
+ }
+ } else {
+ fatal("authenticateNTLMHandleReply: called with no result string\n");
+ }
+ r->handler(r->data, NULL);
+ } else {
+ debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%d'.\n", lastserver);
+ result = S_HELPER_RELEASE;
+ }
+ authenticateStateFree(r);
+ debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
+ return result;
+}
+
+#if 0
+static void
+authenticateNTLMStateFree(authenticateNTLMStateData * r)
+{
+ cbdataFree(r);
+}
+
+#endif
+
+static void
+authenticateNTLMStats(StoreEntry * sentry)
+{
+ storeAppendPrintf(sentry, "NTLM Authenticator Statistics:\n");
+ helperStatefulStats(sentry, ntlmauthenticators);
+}
+
+/* is a particular challenge still valid ? */
+int
+authenticateNTLMValidChallenge(ntlm_helper_state_t * helperstate)
+{
+ if (helperstate->challenge == NULL)
+ return 0;
+ return 1;
+}
+
+/* does our policy call for changing the challenge now? */
+int
+authenticateNTLMChangeChallenge(ntlm_helper_state_t * helperstate)
+{
+ /* don't check for invalid challenges just for expiry choices */
+ /* this is needed because we have to starve the helper until all old
+ * requests have been satisfied */
+ if (helperstate->challengeuses > ntlmConfig->challengeuses)
+ return 1;
+ if (helperstate->renewed + ntlmConfig->challengelifetime >= squid_curtime)
+ return 1;
+ return 0;
+}
+
+/* send the initial data to a stateful ntlm authenticator module */
+static void
+authenticateNTLMStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
+{
+#if 0
+ authenticateStatefulStateData *r = NULL;
+#endif
+ authenticateStateData *r = NULL;
+ helper_stateful_server *server;
+ ntlm_helper_state_t *helperstate;
+ char buf[8192];
+ char *sent_string = NULL;
+ ntlm_user_t *ntlm_user;
+ ntlm_request_t *ntlm_request;
+ auth_user_t *auth_user;
+
+ assert(auth_user_request);
+ auth_user = auth_user_request->auth_user;
+ ntlm_user = auth_user->scheme_data;
+ ntlm_request = auth_user_request->scheme_data;
+ assert(ntlm_user);
+ assert(ntlm_request);
+ assert(handler);
+ assert(data);
+ assert(auth_user->auth_type = AUTH_NTLM);
+ debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", ntlm_request->auth_state);
+ switch (ntlm_request->auth_state) {
+ case AUTHENTICATE_STATE_NEGOTIATE:
+ sent_string = xstrdup(ntlm_request->ntlmnegotiate);
+ break;
+ case AUTHENTICATE_STATE_RESPONSE:
+ sent_string = xstrdup(ntlm_request->ntlmauthenticate);
+ assert(ntlm_request->authhelper);
+ debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%d'.\n", ntlm_request->authhelper);
+ break;
+ default:
+ fatal("Invalid authenticate state for NTLMStart");
+ }
+
+ while (!xisspace(*sent_string)) /*trim NTLM */
+ sent_string++;
+
+ while (xisspace(*sent_string)) /*trim leading spaces */
+ sent_string++;
+
+ debug(29, 9) ("authenticateNTLMStart: state '%d'\n", ntlm_request->auth_state);
+ debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
+ if (ntlmConfig->authenticate == NULL) {
+ debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string);
+// handler(data,0, NULL);
+ handler(data, NULL);
+ return;
+ }
+#ifdef NTLMHELPPROTOCOLV2
+ r = CBDATA_ALLOC(authenticateStateData, NULL);
+ r->handler = handler;
+ cbdataLock(data);
+ r->data = data;
+ r->auth_user_request = auth_user_request;
+ snprintf(buf, 8192, "%s\n", sent_string);
+ helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authhelper);
+ debug(29, 9) ("authenticateNTLMstart: finished\n");
+#else
+ /* this is ugly TODO: move the challenge generation routines to their own function and
+ * tidy the logic up to make use of the efficiency we now have */
+ switch (ntlm_request->auth_state) {
+ case AUTHENTICATE_STATE_NEGOTIATE:
+ /*
+ * 1: get a helper server
+ * 2: does it have a challenge?
+ * 3: tell it to get a challenge, or give ntlmauthdone the challenge
+ */
+ server = helperStatefulDefer(ntlmauthenticators);
+ helperstate = server ? helperStatefulServerGetData(server) : NULL;
+ while ((server != NULL) &&
+ authenticateNTLMChangeChallenge(helperstate)) {
+ /* flag this helper for challenge changing */
+ helperstate->starve = 1;
+ /* and release the deferred request */
+ helperStatefulReleaseServer(server);
+ server = helperStatefulDefer(ntlmauthenticators);
+ if (server != NULL)
+ helperstate = helperStatefulServerGetData(server);
+ }
+ if (server == NULL)
+ debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
+
+ ntlm_request->authhelper = server;
+ /* tell the log what helper we have been given */
+ debug(29, 9) ("authenticateNTLMStart: helper '%d' assigned\n", server);
+ /* valid challenge? */
+ if ((server == NULL) || !authenticateNTLMValidChallenge(helperstate)) {
+ r = CBDATA_ALLOC(authenticateStateData, NULL);
+ r->handler = handler;
+ cbdataLock(data);
+ r->data = data;
+ r->auth_user_request = auth_user_request;
+ if (server == NULL) {
+ helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, ntlm_request->authhelper);
+ } else {
+ snprintf(buf, 8192, "YR\n");
+ helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authhelper);
+ }
+ } else {
+ /* we have a valid challenge */
+ /* TODO: turn the below into a function and call from here and handlereply */
+ /* increment the challenge uses */
+ helperstate->challengeuses++;
+ /* assign the challenge */
+ ntlm_request->authchallenge =
+ xstrndup(helperstate->challenge, NTLM_CHALLENGE_SZ + 5);
+ handler(data, NULL);
+ }
+
+ break;
+ case AUTHENTICATE_STATE_RESPONSE:
+ r = CBDATA_ALLOC(authenticateStateData, NULL);
+ r->handler = handler;
+ cbdataLock(data);
+ r->data = data;
+ r->auth_user_request = auth_user_request;
+ snprintf(buf, 8192, "KK %s\n", sent_string);
+ helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authhelper);
+ debug(29, 9) ("authenticateNTLMstart: finished\n");
+ break;
+ default:
+ fatal("Invalid authenticate state for NTLMStart");
+ }
+#endif
+}
+
+/* callback used by stateful helper routines */
+int
+authenticateNTLMHelperServerAvailable(void *data)
+{
+ ntlm_helper_state_t *statedata = data;
+ if (statedata != NULL) {
+ if (statedata->starve) {
+ debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
+ return 0;
+ } else {
+ debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
+ return 1;
+ }
+ }
+ debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
+ return 0;
+}
+
+void
+authenticateNTLMHelperServerOnEmpty(void *data)
+{
+ ntlm_helper_state_t *statedata = data;
+ if (statedata == NULL)
+ return;
+ if (statedata->starve) {
+ /* we have been starving the helper */
+ debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
+ statedata->starve = 0;
+ statedata->challengeuses = 0;
+ statedata->renewed = 0;
+ xfree(statedata->challenge);
+ statedata->challenge = NULL;
+ }
+}
+
+
+/* clear the NTLM helper of being reserved for future requests */
+void
+authenticateNTLMReleasehelper(auth_user_request_t * auth_user_request)
+{
+ ntlm_request_t *ntlm_request;
+ assert(auth_user_request->auth_user->auth_type == AUTH_NTLM);
+ assert(auth_user_request->scheme_data != NULL);
+ ntlm_request = auth_user_request->scheme_data;
+ debug(29, 9) ("authenticateNTLMReleasehelper: releasing helper '%d'\n", ntlm_request->authhelper);
+ helperStatefulReleaseServer(ntlm_request->authhelper);
+ ntlm_request->authhelper = NULL;
+}
+
+/* clear any connection related authentication details */
+void
+authenticateNTLMOnCloseConnection(ConnStateData * conn)
+{
+ ntlm_request_t *ntlm_request;
+ assert(conn != NULL);
+ if (conn->auth_user_request != NULL) {
+ assert(conn->auth_user_request->scheme_data != NULL);
+ ntlm_request = conn->auth_user_request->scheme_data;
+ if (ntlm_request->authhelper != NULL)
+ authenticateNTLMReleasehelper(conn->auth_user_request);
+ /* unlock the connection based lock */
+ debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
+ authenticateAuthUserRequestUnlock(conn->auth_user_request);
+ conn->auth_user_request = NULL;
+ }
+}
+
+/* authenticateUserUsername: return a pointer to the username in the */
+char *
+authenticateNTLMUsername(auth_user_t * auth_user)
+{
+ ntlm_user_t *ntlm_user = auth_user->scheme_data;
+ if (ntlm_user)
+ return ntlm_user->username;
+ return NULL;
+}
+
+
+/*
+ * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
+ * Auth_user structure.
+ */
+
+void
+authenticateDecodeNTLMAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
+{
+ dlink_node *node;
+ assert(auth_user_request->auth_user == NULL);
+ auth_user_request->auth_user = authenticateAuthUserNew("ntlm");
+ auth_user_request->auth_user->auth_type = AUTH_NTLM;
+ auth_user_request->auth_user->scheme_data = memPoolAlloc(ntlm_user_pool);
+ auth_user_request->scheme_data = memPoolAlloc(ntlm_request_pool);
+ /* lock for the auth_user_request link */
+ authenticateAuthUserLock(auth_user_request->auth_user);
+ node = dlinkNodeNew();
+ dlinkAdd(auth_user_request, node, &auth_user_request->auth_user->requests);
+
+ /* all we have to do is identify that it's NTLM - the helper does the rest */
+ debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
+ return;
+}
+
+int
+authenticateNTLMcmpUsername(ntlm_user_t * u1, ntlm_user_t * u2)
+{
+ return strcmp(u1->username, u2->username);
+}
+
+void
+authenticateProxyAuthCacheAddLink(const char *key, auth_user_t * auth_user)
+{
+ auth_user_hash_pointer *proxy_auth_hash;
+ ntlm_user_t *ntlm_user;
+ proxy_auth_hash =
+ memAllocate(MEM_AUTH_USER_HASH);
+ proxy_auth_hash->key = xstrdup(key);
+ proxy_auth_hash->auth_user = auth_user;
+ ntlm_user = auth_user->scheme_data;
+ dlinkAddTail(proxy_auth_hash, &proxy_auth_hash->link,
+ &ntlm_user->proxy_auth_list);
+ hash_join(proxy_auth_cache, (hash_link *) proxy_auth_hash);
+}
+
+
+int
+authNTLMAuthenticated(auth_user_request_t * auth_user_request)
+{
+ ntlm_request_t *ntlm_request = auth_user_request->scheme_data;
+ if (ntlm_request->auth_state == AUTHENTICATE_STATE_DONE)
+ return 1;
+ debug(29, 9) ("User not fully authenticated.\n");
+ return 0;
+}
+
+#if 0
+static acl_proxy_auth_user *
+authenticateNTLMAuthenticateUser(void *data, const char *proxy_auth, ConnStateData * conn)
+#else
+static void
+authenticateNTLMAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
+#endif
+{
+ const char *proxy_auth;
+ auth_user_hash_pointer *usernamehash, *proxy_auth_hash = NULL;
+ auth_user_t *auth_user;
+ ntlm_request_t *ntlm_request;
+ ntlm_user_t *ntlm_user;
+ LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
+ /* get header */
+ proxy_auth = httpHeaderGetStr(&request->header, type);
+
+ auth_user = auth_user_request->auth_user;
+ assert(auth_user);
+ assert(auth_user->auth_type == AUTH_NTLM);
+ assert(auth_user->scheme_data != NULL);
+ assert(auth_user_request->scheme_data != NULL);
+ ntlm_user = auth_user->scheme_data;
+ ntlm_request = auth_user_request->scheme_data;
+ switch (ntlm_request->auth_state) {
+ case AUTHENTICATE_STATE_NONE:
+ /* we've recieved a negotiate request. pass to a helper */
+ debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n",
+ proxy_auth);
+ ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
+ ntlm_request->ntlmnegotiate = xstrndup(proxy_auth, NTLM_CHALLENGE_SZ + 5);
+ conn->auth_type = AUTH_NTLM;
+ conn->auth_user_request = auth_user_request;
+ /* and lock for the connection duration */
+ debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
+ authenticateAuthUserRequestLock(auth_user_request);
+ return;
+ break;
+ case AUTHENTICATE_STATE_NEGOTIATE:
+ ntlm_request->auth_state = AUTHENTICATE_STATE_CHALLENGE;
+ return;
+ break;
+ case AUTHENTICATE_STATE_CHALLENGE:
+ /* we should have recieved a NTLM challenge. pass it to the same
+ * helper process */
+ debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
+ /* do a cache lookup here. If it matches it's a successful ntlm
+ * challenge - release the helper and use the existing auth_user
+ * details. */
+ if (strncmp("NTLM ", proxy_auth, 5) == 0) {
+ ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
+ } else {
+ fatal("Incorrect scheme in auth header\n");
+ /* TODO: more fault tolerance.. reset the auth scheme here */
+ }
+ /* cache entries have authenticateauthheaderchallengestring */
+ snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
+ ntlm_request->ntlmauthenticate,
+ ntlm_request->authchallenge);
+ /* see if we already know this user's authenticate */
+ debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
+ assert(proxy_auth_cache != NULL);
+ proxy_auth_hash = hash_lookup(proxy_auth_cache, ntlmhash);
+ if (!proxy_auth_hash) { /* not in the hash table */
+ debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
+ ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
+ /* verify with the ntlm helper */
+ } else {
+ debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
+ /* throw away the temporary entry */
+ authenticateNTLMReleasehelper(auth_user_request);
+ authenticateAuthUserMerge(auth_user, proxy_auth_hash->auth_user);
+ auth_user = proxy_auth_hash->auth_user;
+ auth_user_request->auth_user = auth_user;
+ ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
+ /* we found one */
+ debug(29, 9) ("found matching cache entry\n");
+ assert(auth_user->auth_type == AUTH_NTLM);
+ /* get the existing entries details */
+ ntlm_user = auth_user->scheme_data;
+ debug(29, 9) ("Username to be used is %s\n",
+ ntlm_user->username);
+ auth_user->flags.credentials_ok = 1; /* authenticated ok */
+ /* on ntlm auth we do not unlock the auth_user until the
+ * connection is dropped. Thank MS for this quirk */
+ auth_user->expiretime = current_time.tv_sec;
+ auth_user->ip_expiretime = squid_curtime;
+ }
+ return;
+ break;
+ case AUTHENTICATE_STATE_RESPONSE:
+ /* auth-challenge pair cache miss. We've just got the response */
+ /*add to cache and let them through */
+ ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
+ /* this connection is authenticated */
+ debug(29, 4) ("authenticated\nch %s\nauth %s\nauthuser %s\n",
+ ntlm_request->authchallenge,
+ ntlm_request->ntlmauthenticate,
+ ntlm_user->username);
+ /* cache entries have authenticateauthheaderchallengestring */
+ snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
+ ntlm_request->ntlmauthenticate,
+ ntlm_request->authchallenge);
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ if ((usernamehash = hash_lookup(proxy_auth_username_cache,
+ ntlm_user->username))) {
+ while ((usernamehash->auth_user->auth_type !=
+ auth_user->auth_type) && (usernamehash->next) &&
+ !authenticateNTLMcmpUsername(usernamehash->auth_user->scheme_data, ntlm_user))
+ usernamehash = usernamehash->next;
+ if (usernamehash->auth_user->auth_type == auth_user->auth_type) {
+ /*
+ * add another link from the new proxy_auth to the
+ * auth_user structure and update the information */
+ assert(proxy_auth_hash == NULL);
+ authenticateProxyAuthCacheAddLink(ntlmhash, usernamehash->auth_user);
+ /* we can't seamlessly recheck the username due to the
+ * challenge nature of the protocol. Just free the
+ * temporary auth_user */
+ authenticateAuthUserMerge(auth_user, usernamehash->auth_user);
+ auth_user = usernamehash->auth_user;
+ auth_user_request->auth_user = auth_user;
+#if 0
+ conn->auth_user = auth_user;
+#endif
+ }
+ } else {
+ /* store user in hash's */
+ authenticateUserNameCacheAdd(auth_user);
+ authenticateProxyAuthCacheAddLink(ntlmhash, auth_user);
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ auth_user->expiretime = current_time.tv_sec;
+ auth_user->ip_expiretime = squid_curtime;
+ auth_user->flags.credentials_ok = 1; /*authenticated ok */
+ return;
+ break;
+ case AUTHENTICATE_STATE_DONE:
+ fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
+#if 0 /* done in acl.c */
+ case AUTHENTICATE_STATE_DONE:
+ debug(28, 5) ("aclMatchProxyAuth: connection in state Done. using connection credentials for the request. \n");
+ /* is it working right? */
+ assert(checklist->auth_user == NULL);
+ assert(checklist->conn->auth_user != NULL);
+ /* we have a valid username. */
+ auth_user = checklist->conn->auth_user;
+ /* store the username in the request for logging */
+ xstrncpy(checklist->request->authuser,
+ auth_user->auth_data.ntlm_auth.username,
+ USER_IDENT_SZ);
+ if (auth_user->expiretime + Config.authenticateTTL > current_time.tv_sec
+ ) {
+ auth_user->expiretime = current_time.tv_sec;
+ } else {
+ //user passed externa; authentication in every case to get here. f.
+ Let it through
+ } /* we don't unlock the auth_user until the connection is dropped. Thank
+ * MS for this quirk. */ if (authenticateCheckAuthUserIP(checklist->src_addr, auth_user)) {
+ /* Once the match is completed we have finished with the
+ * auth_user structure */
+ /* check to see if we have matched the user-acl before */
+ return aclCacheMatchAcl(&auth_user->proxy_match_cache, acltype,
+ data, auth_user->auth_data.ntlm_auth.username);
+ } else {
+ return 0;
+ }
+ break;
+#endif
+ }
+
+ return;
+}
--- /dev/null
+/*
+ * auth_ntlm.h
+ * Internal declarations for the ntlm auth module
+ */
+
+#ifndef __AUTH_NTLM_H__
+#define __AUTH_NTLM_H__
+
+#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
+
+/* Generic */
+typedef struct {
+ void *data;
+ auth_user_request_t *auth_user_request;
+ RH *handler;
+} authenticateStateData;
+
+struct _ntlm_user {
+ /* what username did this connection get? */
+ char *username;
+ dlink_list proxy_auth_list;
+};
+
+struct _ntlm_request {
+ /* what negotiate string did the client use? */
+ char *ntlmnegotiate;
+ /* what challenge did we give the client? */
+ char *authchallenge;
+ /* what authenticate string did we get? */
+ char *ntlmauthenticate;
+ /*we need to store the NTLM helper between requests */
+ helper_stateful_server *authhelper;
+ /* how far through the authentication process are we? */
+ auth_state_t auth_state;
+};
+
+struct _ntlm_helper_state_t {
+ char *challenge; /* the challenge to use with this helper */
+ int starve; /* 0= normal operation. 1=don't hand out any more challenges */
+ int challengeuses; /* the number of times this challenge has been issued */
+ time_t renewed;
+};
+
+/* configuration runtime data */
+struct _auth_ntlm_config {
+ int authenticateChildren;
+ wordlist *authenticate;
+ int challengeuses;
+ time_t challengelifetime;
+#if 0
+ char *authenticate_ntlm_default_domain;
+#endif
+};
+
+typedef struct _ntlm_user ntlm_user_t;
+typedef struct _ntlm_request ntlm_request_t;
+typedef struct _ntlm_helper_state_t ntlm_helper_state_t;
+typedef struct _auth_ntlm_config auth_ntlm_config;
+
+extern MemPool *ntlm_helper_state_pool;
+extern MemPool *ntlm_user_pool;
+extern MemPool *ntlm_request_pool;
+
+#endif
--- /dev/null
+#!/bin/sh
+echo "/* automatically generated by $0 $*"
+echo " * do not edit"
+echo " */"
+echo "#include \"squid.h\""
+echo ""
+for module in "$@"; do
+ echo "extern AUTHSSETUP authSchemeSetup_${module};"
+done
+echo "void authSchemeSetup(void)"
+echo "{"
+for module in "$@"; do
+ echo " authSchemeAdd(\"$module\", authSchemeSetup_${module});"
+done
+echo "}"
/*
- * $Id: authenticate.cc,v 1.14 2001/01/05 09:51:36 adrian Exp $
+ * $Id: authenticate.cc,v 1.15 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 29 Authenticator
* AUTHOR: Duane Wessels
*
*/
-#include "squid.h"
+/* The functions in this file handle authentication.
+ * They DO NOT perform access control or auditing.
+ * See acl.c for access control and client_side.c for auditing */
-typedef struct {
- void *data;
- acl_proxy_auth_user *auth_user;
- RH *handler;
-} authenticateStateData;
-static HLPCB authenticateHandleReply;
-static void authenticateStateFree(authenticateStateData * r);
-static helper *authenticators = NULL;
+#include "squid.h"
static void
-authenticateHandleReply(void *data, char *reply)
+ authenticateDecodeAuth(const char *proxy_auth, auth_user_request_t * auth_user_request);
+
+/*
+ *
+ * Private Data
+ *
+ */
+
+MemPool *auth_user_request_pool = NULL;
+
+/* Generic Functions */
+
+
+int
+authenticateAuthSchemeConfigured(const char *proxy_auth)
{
- authenticateStateData *r = data;
- int valid;
- char *t = NULL;
- debug(29, 5) ("authenticateHandleReply: {%s}\n", reply ? reply : "<NULL>");
- if (reply) {
- if ((t = strchr(reply, ' ')))
- *t = '\0';
- if (*reply == '\0')
- reply = NULL;
+ authScheme *scheme;
+ int i;
+ for (i = 0; i < Config.authConfig.n_configured; i++) {
+ scheme = Config.authConfig.schemes + i;
+ if (strncasecmp(proxy_auth, scheme->typestr, strlen(scheme->typestr)) == 0)
+ return 1;
}
- valid = cbdataValid(r->data);
- cbdataUnlock(r->data);
- if (valid)
- r->handler(r->data, reply);
- authenticateStateFree(r);
+ return 0;
}
-static void
-authenticateStateFree(authenticateStateData * r)
+int
+authenticateAuthSchemeId(const char *typestr)
{
- cbdataFree(r);
+ int i = 0;
+ for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) {
+ if (strncasecmp(typestr, authscheme_list[i].typestr, strlen(authscheme_list[i].typestr)) == 0) {
+ return i;
+ }
+ }
+ return -1;
}
-static void
-authenticateStats(StoreEntry * sentry)
+void
+authenticateDecodeAuth(const char *proxy_auth, auth_user_request_t * auth_user_request)
{
- storeAppendPrintf(sentry, "Authenticator Statistics:\n");
- helperStats(sentry, authenticators);
+ int i = 0;
+ assert(proxy_auth != NULL);
+ assert(auth_user_request != NULL); /* we need this created for us. */
+ debug(29, 9) ("authenticateDecodeAuth: header = '%s'\n", proxy_auth);
+ if (authenticateAuthSchemeConfigured(proxy_auth)) {
+ /* we're configured to use this scheme - but is it active ? */
+ if ((i = authenticateAuthSchemeId(proxy_auth)) != -1) {
+ authscheme_list[i].decodeauth(auth_user_request, proxy_auth);
+ auth_user_request->auth_user->auth_module = i + 1;
+ return;
+ }
+ }
+ debug(29, 1)
+ ("authenticateDecodeAuth: Unsupported or unconfigured proxy-auth scheme, '%s'\n",
+ proxy_auth);
+ return;
}
-CBDATA_TYPE(authenticateStateData);
-
-/**** PUBLIC FUNCTIONS ****/
+/* clear any connection related authentication details */
+void
+authenticateOnCloseConnection(ConnStateData * conn)
+{
+ auth_user_request_t *auth_user_request;
+ assert(conn != NULL);
+ if (conn->auth_user_request != NULL) {
+ auth_user_request = conn->auth_user_request;
+ if (authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection) {
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].oncloseconnection(conn);
+ }
+ }
+}
+/**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
+/* send the initial data to an authenticator module */
void
-authenticateStart(acl_proxy_auth_user * auth_user, RH * handler, void *data)
+authenticateStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
{
- authenticateStateData *r = NULL;
- char buf[8192];
- assert(auth_user);
+ assert(auth_user_request);
assert(handler);
- debug(29, 5) ("authenticateStart: '%s:%s'\n", hashKeyStr(&auth_user->hash),
- auth_user->passwd);
- if (Config.Program.authenticate == NULL) {
+ debug(29, 9) ("authenticateStart: auth_user_request '%d'\n", auth_user_request);
+ if (auth_user_request->auth_user->auth_module > 0)
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].authStart(auth_user_request, handler, data);
+ else
handler(data, NULL);
- return;
+}
+
+/*
+ * Check a auth_user pointer for validity. Does not check passwords, just data
+ * sensability. Broken or Unknown auth_types are not valid for use...
+ */
+
+int
+authenticateValidateUser(auth_user_request_t * auth_user_request)
+{
+ debug(29, 9) ("authenticateValidateUser: Validating Auth_user request '%d'.\n", auth_user_request);
+ if (auth_user_request == NULL) {
+ debug(29, 4) ("authenticateValidateUser: Auth_user_request was NULL!\n");
+ return 0;
}
- r = CBDATA_ALLOC(authenticateStateData, NULL);
- r->handler = handler;
- cbdataLock(data);
- r->data = data;
- r->auth_user = auth_user;
- snprintf(buf, 8192, "%s %s\n", hashKeyStr(&r->auth_user->hash),
- r->auth_user->passwd);
- helperSubmit(authenticators, buf, authenticateHandleReply, r);
+ if (auth_user_request->auth_user == NULL) {
+ debug(29, 4) ("authenticateValidateUser: No associated auth_user structure\n");
+ return 0;
+ }
+ if (auth_user_request->auth_user->auth_type == AUTH_UNKNOWN) {
+ debug(29, 4) ("authenticateValidateUser: Auth_user '%d' uses unknown scheme.\n", auth_user_request->auth_user);
+ return 0;
+ }
+ if (auth_user_request->auth_user->auth_type == AUTH_BROKEN) {
+ debug(29, 4) ("authenticateValidateUser: Auth_user '%d' is broken for it's scheme.\n", auth_user_request->auth_user);
+ return 0;
+ }
+ /* any other sanity checks that we need in the future */
+
+ /* Thus should a module call to something like authValidate */
+
+ /* finally return ok */
+ debug(29, 4) ("authenticateValidateUser: Validated Auth_user request '%d'.\n", auth_user_request);
+ return 1;
+
+}
+
+auth_user_t *
+authenticateAuthUserNew(const char *scheme)
+{
+ auth_user_t *temp_auth;
+ temp_auth = memAllocate(MEM_AUTH_USER_T);
+ assert(temp_auth != NULL);
+ temp_auth->auth_type = AUTH_UNKNOWN;
+ temp_auth->references = 0;
+ temp_auth->auth_module = authenticateAuthSchemeId(scheme) + 1;
+ return temp_auth;
+}
+
+auth_user_request_t *
+authenticateAuthUserRequestNew()
+{
+ auth_user_request_t *temp_request;
+ if (!auth_user_request_pool)
+ auth_user_request_pool = memPoolCreate("Authenticate Request Data", sizeof(auth_user_request_t));
+ temp_request = memPoolAlloc(auth_user_request_pool);
+ assert(temp_request != NULL);
+ temp_request->auth_user = NULL;
+ temp_request->message = NULL;
+ temp_request->scheme_data = NULL;
+ temp_request->references = 0;
+ return temp_request;
}
void
-authenticateInit(void)
+authenticateAuthUserRequestFree(auth_user_request_t * auth_user_request)
{
- static int init = 0;
- if (!Config.Program.authenticate)
+ dlink_node *link;
+ debug(29, 5) ("authenticateAuthUserRequestFree: freeing request %d\n", auth_user_request);
+ if (!auth_user_request)
return;
- if (authenticators == NULL)
- authenticators = helperCreate("authenticator");
- authenticators->cmdline = Config.Program.authenticate;
- authenticators->n_to_start = Config.authenticateChildren;
- authenticators->ipc_type = IPC_TCP_SOCKET;
- helperOpenServers(authenticators);
- if (!init) {
- cachemgrRegister("authenticator",
- "User Authenticator Stats",
- authenticateStats, 0, 1);
- init++;
+ assert(auth_user_request->references == 0);
+ if (auth_user_request->auth_user) {
+ if (auth_user_request->scheme_data != NULL) {
+ /* we MUST know the module */
+ assert((auth_user_request->auth_user->auth_module > 0));
+ /* and the module MUST support requestFree if it has created scheme data */
+ assert(authscheme_list[auth_user_request->auth_user->auth_module - 1].requestFree != NULL);
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].requestFree(auth_user_request);
+ }
+ /* unlink from the auth_user struct */
+ link = auth_user_request->auth_user->requests.head;
+ while (link && (link->data != auth_user_request))
+ link = link->next;
+ assert(link != NULL);
+ dlinkDelete(link, &auth_user_request->auth_user->requests);
+ dlinkNodeDelete(link);
+
+ /* unlock the request structure's lock */
+ authenticateAuthUserUnlock(auth_user_request->auth_user);
+ auth_user_request->auth_user = NULL;
+ } else
+ assert(auth_user_request->scheme_data == NULL);
+ if (auth_user_request->message)
+ xfree(auth_user_request->message);
+}
+
+char *
+authenticateAuthUserRequestMessage(auth_user_request_t * auth_user_request)
+{
+ if (auth_user_request)
+ return auth_user_request->message;
+ return NULL;
+}
+
+void
+authenticateAuthUserRequestSetIp(auth_user_request_t * auth_user_request, struct in_addr ipaddr)
+{
+ if (auth_user_request->auth_user)
+ if (!auth_user_request->auth_user->ipaddr.s_addr)
+ auth_user_request->auth_user->ipaddr = ipaddr;
+}
+
+/* Get Auth User: Return a filled out auth_user structure for the given
+ * Proxy Auth (or Auth) header. It may be a cached Auth User or a new
+ * Unauthenticated structure. The structure is given an inital lock here.
+ */
+auth_user_request_t *
+authenticateGetAuthUser(const char *proxy_auth)
+{
+ auth_user_request_t *auth_user_request = authenticateAuthUserRequestNew();
+ /* and lock for the callers instance */
+ authenticateAuthUserRequestLock(auth_user_request);
+ authenticateDecodeAuth(proxy_auth, auth_user_request);
+ return auth_user_request;
+}
+
+/*
+ * authenticateUserAuthenticated: is this auth_user structure logged in ?
+ */
+int
+authenticateUserAuthenticated(auth_user_request_t * auth_user_request)
+{
+ assert(authenticateValidateUser(auth_user_request));
+ if (auth_user_request->auth_user->auth_module > 0)
+ return authscheme_list[auth_user_request->auth_user->auth_module - 1].authenticated(auth_user_request);
+ else
+ return 0;
+}
+
+/*
+ * authenticateAuthenticateUser: log this user request in.
+ * Cache hits may change the auth_user pointer in the structure if needed.
+ * This is basically a handle approach.
+ */
+void
+authenticateAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
+{
+ assert(auth_user_request != NULL);
+ if (auth_user_request->auth_user->auth_module > 0)
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].authAuthenticate(auth_user_request, request, conn, type);
+}
+
+/* authenticateUserUsername: return a pointer to the username in the */
+char *
+authenticateUserUsername(auth_user_t * auth_user)
+{
+ if (!auth_user)
+ return NULL;
+ if (auth_user->auth_module > 0)
+ return authscheme_list[auth_user->auth_module - 1].authUserUsername(auth_user);
+ return NULL;
+}
+
+/* authenticateUserRequestUsername: return a pointer to the username in the */
+char *
+authenticateUserRequestUsername(auth_user_request_t * auth_user_request)
+{
+ assert(auth_user_request != NULL);
+ return authenticateUserUsername(auth_user_request->auth_user);
+}
+
+/* returns
+ * 0: no output needed
+ * 1: send to client
+ * -1: send to helper
+ * -2: authenticate broken in some fashion
+ */
+int
+authenticateDirection(auth_user_request_t * auth_user_request)
+{
+ if (!auth_user_request)
+ return -2;
+ if (authenticateUserAuthenticated(auth_user_request))
+ return 0;
+ if (auth_user_request->auth_user->auth_module > 0)
+ return authscheme_list[auth_user_request->auth_user->auth_module - 1].getdirection(auth_user_request);
+ return -2;
+}
+
+int
+authenticateActiveSchemeCount()
+{
+ int i = 0, rv = 0;
+ for (i = 0; authscheme_list && authscheme_list[i].typestr; i++)
+ if (authscheme_list[i].Active())
+ rv++;
+ debug(29, 9) ("authenticateActiveSchemeCount: %d active.\n", rv);
+ return rv;
+}
+
+int
+authenticateSchemeCount()
+{
+ int i = 0, rv = 0;
+ for (i = 0; authscheme_list && authscheme_list[i].typestr; i++)
+ rv++;
+ debug(29, 9) ("authenticateSchemeCount: %d active.\n", rv);
+ return rv;
+}
+
+void
+authenticateSchemeInit(void)
+{
+ authSchemeSetup();
+}
+
+void
+authenticateInit(authConfig * config)
+{
+ int i;
+ authScheme *scheme;
+ for (i = 0; i < config->n_configured; i++) {
+ if (authscheme_list[i].init) {
+ scheme = config->schemes + i;
+ authscheme_list[i].init(scheme);
+ }
}
- CBDATA_INIT_TYPE(authenticateStateData);
+ if (!proxy_auth_username_cache)
+ authenticateInitUserCache();
}
void
authenticateShutdown(void)
{
- if (!authenticators)
- return;
- helperShutdown(authenticators);
- if (!shutting_down)
- return;
- helperFree(authenticators);
- authenticators = NULL;
+ int i;
+ debug(29, 2) ("authenticateShutdown: shutting down auth schemes\n");
+ /* find the currently known authscheme types */
+ for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) {
+ if (authscheme_list[i].donefunc != NULL)
+ authscheme_list[i].donefunc();
+ else
+ debug(29, 2) ("authenticateShutdown: scheme %s has not registered a shutdown function.\n", authscheme_list[i].typestr);
+ authscheme_list[i].typestr = NULL;
+ }
+}
+
+void
+authenticateFixHeader(HttpReply * rep, auth_user_request_t * auth_user_request, request_t * request, int accelerated)
+/* send the auth types we are configured to support (and have compiled in!) */
+{
+/* auth_type_t auth_type=err->auth_type;
+ * auth_state_t auth_state=err->auth_state;
+ * char *authchallenge=err->authchallenge;
+ * auth_user_request_t *auth_user_request=err->auth_user_request;
+ */
+ int type = 0;
+ switch (rep->sline.status) {
+ case HTTP_PROXY_AUTHENTICATION_REQUIRED:
+ /* Proxy authorisation needed */
+ type = HDR_PROXY_AUTHENTICATE;
+ break;
+ case HTTP_UNAUTHORIZED:
+ /* WWW Authorisation needed */
+ type = HDR_WWW_AUTHENTICATE;
+ break;
+ default:
+ /* Keep GCC happy */
+ /* some other HTTP status */
+ break;
+ }
+ debug(29, 9) ("authenticateFixHeader: headertype:%d authuser:%d\n", type, auth_user_request);
+ if ((rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (rep->sline.status == HTTP_UNAUTHORIZED))
+ /* this is a authenticate-needed response */
+ {
+ if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0))
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].authFixHeader(auth_user_request, rep, type, request);
+ else {
+ int i;
+ authScheme *scheme;
+ /* call each configured authscheme */
+ for (i = 0; i < Config.authConfig.n_configured; i++) {
+ scheme = Config.authConfig.schemes + i;
+ if (authscheme_list[scheme->Id].Active())
+ authscheme_list[scheme->Id].authFixHeader(auth_user_request, rep, type,
+ request);
+ else
+ debug(29, 4) ("authenticateFixHeader: Configured scheme %s not Active\n", scheme->typestr);
+ }
+ }
+ }
+ if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0)
+ && (authscheme_list[auth_user_request->auth_user->auth_module - 1].AddHeader))
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].AddHeader(auth_user_request, rep, accelerated);
+}
+
+/* call the active auth module and allow it to add a trailer to the request */
+void
+authenticateAddTrailer(HttpReply * rep, auth_user_request_t * auth_user_request, request_t * request, int accelerated)
+{
+ if ((auth_user_request != NULL) && (auth_user_request->auth_user->auth_module > 0)
+ && (authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer))
+ authscheme_list[auth_user_request->auth_user->auth_module - 1].AddTrailer(auth_user_request, rep, accelerated);
+}
+
+void
+authenticateAuthUserLock(auth_user_t * auth_user)
+{
+ debug(29, 9) ("authenticateAuthUserLock auth_user '%d'.\n", auth_user);
+ assert(auth_user != NULL);
+ auth_user->references++;
+ debug(29, 9) ("authenticateAuthUserLock auth_user '%d' now at '%d'.\n", auth_user, auth_user->references);
+}
+
+void
+authenticateAuthUserUnlock(auth_user_t * auth_user)
+{
+ debug(29, 9) ("authenticateAuthUserUnlock auth_user '%d'.\n", auth_user);
+ assert(auth_user != NULL);
+ if (auth_user->references > 0) {
+ auth_user->references--;
+ } else {
+ debug(29, 1) ("Attempt to lower Auth User %d refcount below 0!\n", auth_user);
+ }
+ debug(29, 9) ("authenticateAuthUserUnlock auth_user '%d' now at '%d'.\n", auth_user, auth_user->references);
+ if (auth_user->references == 0)
+ authenticateFreeProxyAuthUser(auth_user);
+}
+
+void
+authenticateAuthUserRequestLock(auth_user_request_t * auth_user_request)
+{
+ debug(29, 9) ("authenticateAuthUserRequestLock auth_user request '%d'.\n", auth_user_request);
+ assert(auth_user_request != NULL);
+ auth_user_request->references++;
+ debug(29, 9) ("authenticateAuthUserRequestLock auth_user request '%d' now at '%d'.\n", auth_user_request, auth_user_request->references);
+}
+
+void
+authenticateAuthUserRequestUnlock(auth_user_request_t * auth_user_request)
+{
+ debug(29, 9) ("authenticateAuthUserRequestUnlock auth_user request '%d'.\n", auth_user_request);
+ assert(auth_user_request != NULL);
+ if (auth_user_request->references > 0) {
+ auth_user_request->references--;
+ } else {
+ debug(29, 1) ("Attempt to lower Auth User request %d refcount below 0!\n", auth_user_request);
+ }
+ debug(29, 9) ("authenticateAuthUserRequestUnlock auth_user_request '%d' now at '%d'.\n", auth_user_request, auth_user_request->references);
+ if (auth_user_request->references == 0) {
+ /* not locked anymore */
+ authenticateAuthUserRequestFree(auth_user_request);
+ }
+}
+
+int
+authenticateAuthUserInuse(auth_user_t * auth_user)
+/* returns 0 for not in use */
+{
+ assert(auth_user != NULL);
+ return auth_user->references;
+}
+
+/* Combine two user structs. ONLY to be called from within a scheme module.
+ * The scheme module is responsible for ensuring that the two users _can_ be merged
+ * without invalidating all the request scheme data.
+ * the scheme is also responsible for merging any user related scheme data itself. */
+void
+authenticateAuthUserMerge(auth_user_t * from, auth_user_t * to)
+{
+ dlink_node *link, *tmplink;
+ auth_user_request_t *auth_user_request;
+/* XXX combine two authuser structs. Incomplete: it should merge in hash references
+ * too and ask the module to merge in scheme data */
+ debug(29, 5) ("authenticateAuthUserMerge auth_user '%d' into auth_user '%d'.\n", from, to);
+ link = from->requests.head;
+ while (link) {
+ auth_user_request = link->data;
+ tmplink = link;
+ link = link->next;
+ dlinkDelete(tmplink, &from->requests);
+ dlinkAddTail(auth_user_request, tmplink, &to->requests);
+ auth_user_request->auth_user = to;
+ }
+ to->references += from->references;
+ from->references = 0;
+ authenticateFreeProxyAuthUser(from);
+}
+
+void
+authenticateFreeProxyAuthUser(void *data)
+{
+ auth_user_t *u = data;
+ auth_user_request_t *auth_user_request;
+#if 0
+ auth_user_hash_pointer *proxy_auth_hash;
+#endif
+ dlink_node *link, *tmplink;
+ assert(data != NULL);
+ debug(29, 5) ("authenticateFreeProxyAuthUser: Freeing auth_user '%d' with refcount '%d'.\n", u, u->references);
+ assert(u->references == 0);
+ /* were they linked in by username ? */
+ if (u->usernamehash) {
+ assert(u->usernamehash->auth_user == u);
+ debug(29, 5) ("authenticateFreeProxyAuthUser: removing usernamehash entry '%d'\n", u->usernamehash);
+ hash_remove_link(proxy_auth_username_cache,
+ (hash_link *) u->usernamehash);
+ /* don't free the key as we use the same user string as the auth_user
+ * structure */
+ memFree(u->usernamehash, MEM_AUTH_USER_HASH);
+ }
+ /* remove any outstanding requests */
+ link = u->requests.head;
+ while (link) {
+ debug(29, 5) ("authenticateFreeProxyAuthUser: removing request entry '%d'\n", link->data);
+ auth_user_request = link->data;
+ tmplink = link;
+ link = link->next;
+ dlinkDelete(tmplink, &u->requests);
+ dlinkNodeDelete(tmplink);
+ authenticateAuthUserRequestFree(auth_user_request);
+ }
+ /* free cached acl results */
+ aclCacheMatchFlush(&u->proxy_match_cache);
+ if (u->scheme_data && u->auth_module > 0)
+ authscheme_list[u->auth_module - 1].FreeUser(u);
+ /* prevent accidental reuse */
+ u->auth_type = AUTH_UNKNOWN;
+ memFree(u, MEM_AUTH_USER_T);
+}
+
+void
+authenticateInitUserCache()
+{
+ if (!proxy_auth_username_cache) {
+ /* First time around, 7921 should be big enough */
+ proxy_auth_username_cache =
+ hash_create((HASHCMP *) strcmp, 7921, hash_string);
+ assert(proxy_auth_username_cache);
+ eventAdd("User Cache Maintenance", authenticateProxyUserCacheCleanup, NULL, Config.authenticateGCInterval, 1);
+ }
+}
+
+void
+authenticateProxyUserCacheCleanup(void *datanotused)
+{
+ /*
+ * We walk the hash by username as that is the unique key we use.
+ * For big hashs we could consider stepping through the cache, 100/200
+ * entries at a time. Lets see how it flys first.
+ */
+ auth_user_hash_pointer *usernamehash;
+ auth_user_t *auth_user;
+ char *username = NULL;
+ debug(29, 3) ("authenticateProxyUserCacheCleanup: Cleaning the user cache now\n");
+ debug(29, 3) ("authenticateProxyUserCacheCleanup: Current time: %d\n", current_time.tv_sec);
+ hash_first(proxy_auth_username_cache);
+ while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
+ auth_user = usernamehash->auth_user;
+ username = authenticateUserUsername(auth_user);
+
+ /* if we need to have inpedendent expiry clauses, insert a module call
+ * here */
+ debug(29, 4) ("authenticateProxyUserCacheCleanup: Cache entry:\n\tType: %d\n\tUsername: %s\n\texpires: %d\n\treferences: %d\n", auth_user->auth_type, username, auth_user->expiretime + Config.authenticateTTL, auth_user->references);
+ if (auth_user->expiretime + Config.authenticateTTL <= current_time.tv_sec) {
+ debug(29, 5) ("authenticateProxyUserCacheCleanup: Removing user %s from cache due to timeout.\n", username);
+ /* the minus 1 accounts for the cache lock */
+ if ((authenticateAuthUserInuse(auth_user) - 1))
+ debug(29, 4) ("authenticateProxyUserCacheCleanup: this cache entry has expired AND has a non-zero ref count.\n");
+ else
+ authenticateAuthUserUnlock(auth_user);
+ }
+ }
+ debug(29, 3) ("authenticateProxyUserCacheCleanup: Finished cleaning the user cache.\n");
+ eventAdd("User Cache Maintenance", authenticateProxyUserCacheCleanup, NULL, Config.authenticateGCInterval, 1);
+}
+
+/*
+ * authenticateUserCacheRestart() cleans all config-dependent data from the
+ * auth_user cache. It DOES NOT Flush the user cache.
+ */
+
+void
+authenticateUserCacheRestart()
+{
+ auth_user_hash_pointer *usernamehash;
+ auth_user_t *auth_user;
+ char *username = NULL;
+ debug(29, 3) ("authenticateUserCacheRestart: Clearing config dependent cache data.\n");
+ hash_first(proxy_auth_username_cache);
+ while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
+ auth_user = usernamehash->auth_user;
+ username = authenticateUserUsername(auth_user);
+ debug(29, 5) ("authenticateUserCacheRestat: Clearing cache ACL results for user: %s\n", username);
+ aclCacheMatchFlush(&auth_user->proxy_match_cache);
+ }
+
+}
+
+/*
+ * called to add another auth scheme module
+ */
+void
+authSchemeAdd(char *type, AUTHSSETUP * setup)
+{
+ int i;
+ /* find the number of currently known authscheme types */
+ for (i = 0; authscheme_list && authscheme_list[i].typestr; i++) {
+ assert(strcmp(authscheme_list[i].typestr, type) != 0);
+ }
+ /* add the new type */
+ authscheme_list = xrealloc(authscheme_list, (i + 2) * sizeof(authscheme_entry_t));
+ memset(&authscheme_list[i + 1], 0, sizeof(authscheme_entry_t));
+ authscheme_list[i].typestr = type;
+ /* Call the scheme module to set up capabilities and initialize any global data */
+ setup(&authscheme_list[i]);
+}
+
+
+
+/* UserNameCacheAdd: add a auth_user structure to the username cache */
+void
+authenticateUserNameCacheAdd(auth_user_t * auth_user)
+{
+ auth_user_hash_pointer *usernamehash;
+ usernamehash = memAllocate(MEM_AUTH_USER_HASH);
+ usernamehash->key = authenticateUserUsername(auth_user);
+ usernamehash->auth_user = auth_user;
+ hash_join(proxy_auth_username_cache, (hash_link *) usernamehash);
+ auth_user->usernamehash = usernamehash;
+ /* lock for presence in the cache */
+ authenticateAuthUserLock(auth_user);
+}
+
+
+
+/*
+ * check the user for ip changes timeouts
+ * 0 = failed check
+ * 1 = ip requirements are ok.
+ */
+/* TODO:
+ * ip_expire data should be in a struct of it's own - for code reuse */
+int
+authenticateCheckAuthUserIP(struct in_addr request_src_addr, auth_user_request_t * auth_user_request)
+{
+ char *username = authenticateUserRequestUsername(auth_user_request);
+ if (request_src_addr.s_addr == auth_user_request->auth_user->ipaddr.s_addr || auth_user_request->auth_user->ip_expiretime + Config.authenticateIpTTL <= squid_curtime) {
+ /* user has not moved ip or had the ip timeout expire */
+ if ((auth_user_request->auth_user->auth_type == AUTH_UNKNOWN) ||
+ (auth_user_request->auth_user->auth_type == AUTH_BROKEN)) {
+ debug(29, 1) ("authenticateCheckProxyAuthIP: broken or unknown auth type %d.\n", auth_user_request->auth_user->auth_type);
+ return 0;
+ }
+ username = authenticateUserRequestUsername(auth_user_request);
+ /* Update IP ttl */
+ auth_user_request->auth_user->ip_expiretime = squid_curtime;
+ auth_user_request->auth_user->ipaddr = request_src_addr;
+ return 1;
+ } else {
+ char *ip1 = xstrdup(inet_ntoa(auth_user_request->auth_user->ipaddr));
+ char *ip2 = xstrdup(inet_ntoa(request_src_addr));
+ if (Config.onoff.authenticateIpTTLStrict) {
+ debug(29, 1) ("aclMatchProxyAuth: user '%s' tried to use multiple IP addresses! (%s, %s)\n ", username, ip1, ip2);
+ } else {
+ /* Non-strict mode. Reassign ownership to the new IP */
+ auth_user_request->auth_user->ipaddr.s_addr = request_src_addr.s_addr;
+ debug(29, 1) ("aclMatchProxyAuth: user '%s' has changed IP address (%s, %s)\n ", username, ip1, ip2);
+ }
+ safe_free(ip1);
+ safe_free(ip2);
+ /* and deny access */
+ return 0;
+ }
}
/*
- * $Id: cache_cf.cc,v 1.366 2001/01/05 09:51:36 adrian Exp $
+ * $Id: cache_cf.cc,v 1.367 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 3 Configuration File Parsing
* AUTHOR: Harvest Derived
static void parseTimeLine(time_t * tptr, const char *units);
static void parse_ushort(u_short * var);
static void parse_string(char **);
-static void parse_wordlist(wordlist **);
+void parse_wordlist(wordlist **);
static void default_all(void);
static void defaults_if_none(void);
static int parse_line(char *);
static void parseBytesLine(size_t * bptr, const char *units);
static size_t parseBytesUnits(const char *unit);
static void free_all(void);
-static void requirePathnameExists(const char *name, const char *path);
+void requirePathnameExists(const char *name, const char *path);
static OBJH dump_config;
static void dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[]);
static void parse_http_header_access(header_mangler header[]);
Config.redirectChildren = DefaultRedirectChildrenMax;
}
}
- if (Config.Program.authenticate) {
- if (Config.authenticateChildren < 1) {
- Config.authenticateChildren = 0;
- wordlistDestroy(&Config.Program.authenticate);
- } else if (Config.authenticateChildren > DefaultAuthenticateChildrenMax) {
- debug(3, 0) ("WARNING: authenticate_children was set to a bad value: %d\n",
- Config.authenticateChildren);
- debug(3, 0) ("Setting it to the maximum (%d).\n", DefaultAuthenticateChildrenMax);
- Config.authenticateChildren = DefaultAuthenticateChildrenMax;
- }
- }
if (Config.Accel.host) {
snprintf(buf, BUFSIZ, "http://%s:%d", Config.Accel.host, Config.Accel.port);
Config2.Accel.prefix = xstrdup(buf);
#endif
if (Config.Program.redirect)
requirePathnameExists("redirect_program", Config.Program.redirect->key);
- if (Config.Program.authenticate)
- requirePathnameExists("authenticate_program", Config.Program.authenticate->key);
requirePathnameExists("Icon Directory", Config.icons.directory);
requirePathnameExists("Error Directory", Config.errorDirectory);
#if HTTP_VIOLATIONS
return s == NULL;
}
+void
+allocate_new_authScheme(authConfig * cfg)
+{
+ if (cfg->schemes == NULL) {
+ cfg->n_allocated = 4;
+ cfg->schemes = xcalloc(cfg->n_allocated, sizeof(authScheme));
+ }
+ if (cfg->n_allocated == cfg->n_configured) {
+ authScheme *tmp;
+ cfg->n_allocated <<= 1;
+ tmp = xcalloc(cfg->n_allocated, sizeof(authScheme));
+ xmemcpy(tmp, cfg->schemes, cfg->n_configured * sizeof(authScheme));
+ xfree(cfg->schemes);
+ cfg->schemes = tmp;
+ }
+}
+
+static void
+parse_authparam(authConfig * config)
+{
+ char *type_str;
+ char *param_str;
+ authScheme *scheme = NULL;
+ int type, i;
+
+ if ((type_str = strtok(NULL, w_space)) == NULL)
+ self_destruct();
+
+ if ((param_str = strtok(NULL, w_space)) == NULL)
+ self_destruct();
+
+ if ((type = authenticateAuthSchemeId(type_str)) == -1) {
+ debug(3, 0) ("Parsing Config File: Unknown authentication scheme '%s'.\n", type_str);
+ return;
+ }
+ for (i = 0; i < config->n_configured; i++) {
+ if (config->schemes[i].Id == type) {
+ scheme = config->schemes + i;
+ }
+ }
+
+ if (scheme == NULL) {
+ allocate_new_authScheme(config);
+ scheme = config->schemes + config->n_configured;
+ config->n_configured++;
+ scheme->Id = type;
+ scheme->typestr = authscheme_list[type].typestr;
+ }
+ authscheme_list[type].parse(scheme, config->n_configured, param_str);
+}
+
+static void
+free_authparam(authConfig * cfg)
+{
+ authScheme *scheme;
+ int i;
+ /* DON'T FREE THESE FOR RECONFIGURE */
+ if (reconfiguring)
+ return;
+ for (i = 0; i < cfg->n_configured; i++) {
+ scheme = cfg->schemes + i;
+ authscheme_list[scheme->Id].freeconfig(scheme);
+ }
+ safe_free(cfg->schemes);
+ cfg->schemes = NULL;
+ cfg->n_allocated = 0;
+ cfg->n_configured = 0;
+}
+
+static void
+dump_authparam(StoreEntry * entry, const char *name, authConfig cfg)
+{
+ authScheme *scheme;
+ int i;
+ for (i = 0; i < cfg.n_configured; i++) {
+ scheme = cfg.schemes + i;
+ authscheme_list[scheme->Id].dump(entry, name, scheme);
+ }
+}
+
void
allocate_new_swapdir(cacheSwap * swap)
{
storeAppendPrintf(entry, "%s %d\n", name, var);
}
-static void
+void
parse_int(int *var)
{
int i;
safe_free(*var);
}
-static void
+void
parse_eol(char *volatile *var)
{
char *token = strtok(NULL, null_string);
storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var);
}
-static void
+void
parse_time_t(time_t * var)
{
parseTimeLine(var, T_SECOND_STR);
}
}
-static void
+void
parse_wordlist(wordlist ** list)
{
char *token;
free_all();
}
-static void
+void
requirePathnameExists(const char *name, const char *path)
{
struct stat sb;
/*
- * $Id: cbdata.cc,v 1.33 2001/01/06 11:14:42 hno Exp $
+ * $Id: cbdata.cc,v 1.34 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 45 Callback Data Registry
* ORIGINAL AUTHOR: Duane Wessels
CREATE_CBDATA(generic_cbdata);
CREATE_CBDATA(helper);
CREATE_CBDATA(helper_server);
+ CREATE_CBDATA(statefulhelper);
+ CREATE_CBDATA(helper_stateful_server);
CREATE_CBDATA(HttpStateData);
CREATE_CBDATA(peer);
CREATE_CBDATA(ps_state);
#
-# $Id: cf.data.pre,v 1.204 2001/01/04 21:09:00 wessels Exp $
+# $Id: cf.data.pre,v 1.205 2001/01/07 23:36:37 hno Exp $
#
#
# SQUID Internet Object Cache http://squid.nlanr.net/Squid/
no-digest
no-netdb-exchange
no-delay
- login=user:password
+ login=user:password|PASS
connect-timeout=nn
digest-url=url
allow-miss
use 'login=user:password' if this is a personal/workgroup
proxy and your parent requires proxy authentication.
+ use 'login=PASS' if users must authenticate against
+ the upstream proxy. Note: To combine this with
+ proxy_auth both proxies must share the same user
+ database as HTTP only allows for one proxy login.
+ Also be warned that this will expose your users proxy
+ password to the parent. USE WITH CAUTION
+
use 'connect-timeout=nn' to specify a peer
specific connect timeout (also see the
peer_connect_timeout directive)
are sent.
DOC_END
-
-NAME: authenticate_program
-TYPE: wordlist
-LOC: Config.Program.authenticate
+NAME: auth_param
+TYPE: authparam
+LOC: Config.authConfig
DEFAULT: none
DOC_START
+ This is used to pass parameters to the various authentication
+ schemes.
+ format: auth_param scheme parameter [setting]
+
+ auth_param basic program @DEFAULT_PREFIX@/bin/ncsa_auth @DEFAULT_PREFIX@/etc/passwd
+ would tell the basic authentication scheme it's program parameter.
+
+ The order that authentication prompts are presented to the client_agent
+ is dependant on the order the scheme first appears in config file.
+ IE has a bug (it's not rfc 2617 compliant) in that it will use the basic
+ scheme if basic is the first entry presented, even if more secure schemes
+ are presented. For now use the order in the file below. If other browsers
+ have difficulties (don't recognise the schemes offered even if you are using
+ basic) then either put basic first, or disable the other schemes (by commenting
+ out their program entry).
+
+ === Parameters for the basic scheme follow. ===
+
+ "program" cmdline
Specify the command for the external authenticator. Such a
program reads a line containing "username password" and replies
"OK" or "ERR" in an endless loop. If you use an authenticator,
make sure you have 1 acl of type proxy_auth. By default, the
- authenticator_program is not used.
+ authenticate_program is not used.
If you want to use the traditional proxy authentication,
jump over to the ../auth_modules/NCSA directory and
Then, set this line to something like
- authenticate_program @DEFAULT_PREFIX@/bin/ncsa_auth @DEFAULT_PREFIX@/etc/passwd
-DOC_END
+ auth_param basic program @DEFAULT_PREFIX@/bin/ncsa_auth @DEFAULT_PREFIX@/etc/passwd
-NAME: authenticate_children
-TYPE: int
-DEFAULT: 5
-LOC: Config.authenticateChildren
-DOC_START
- The number of authenticator processes to spawn (default 5). If you
+ "children" numberofchildren
+ The number of authenticator processes to spawn (no default). If you
start too few Squid will have to wait for them to process a backlog
of usercode/password verifications, slowing it down. When password
verifications are done via a (slow) network you are likely to need
lots of authenticator processes.
+ auth_param basic children 5
+
+ "realm" realmstring
+ Specifies the realm name which is to be reported to the client for
+ the basic proxy authentication scheme (part of the text the user will
+ see when prompted their username and password). Their is no default.
+ auth_param basic realm Squid proxy-caching web server
+
+ "credentialsttl" timetolive
+ Specifies how long squid assumes an externally validated username:password
+ pair is valid for - in other words how often the helper program is called
+ for that user. Set this low to force revalidation with short lived passwords.
+ Note that setting this high does not impact your susceptability to replay
+ attacks unless you are using a one-time password system (such as SecureID).
+ If you are using such a system, you will be vulnerable to replay attacks
+ unless you also enable the IP ttl is strict option.
+
+ === NTLM scheme options follow ===
+
+ "program" cmdline
+ Specify the command for the external ntlm authenticator. Such a
+ program reads a line containing the uuencoded NEGOTIATE and replies
+ with the ntlm CHALLENGE, then waits for the response and answers with
+ "OK" or "ERR" in an endless loop. If you use an ntlm authenticator,
+ make sure you have 1 acl of type proxy_auth. By default, the
+ ntlm authenticator_program is not used.
+
+ auth_param ntlm program @DEFAULT_PREFIX@/bin/ntlm_auth
+
+ "children" numberofchildren
+ The number of authenticator processes to spawn (no default). If you
+ start too few Squid will have to wait for them to process a backlog
+ of credential verifications, slowing it down. When crendential
+ verifications are done via a (slow) network you are likely to need
+ lots of authenticator processes.
+ auth_param ntlm children 5
+
+ "max_challenge_reuses" number
+ The maximum number of times a challenge given by a ntlm authentication
+ helper can be reused. Increasing this number increases your exposure
+ to replay attacks on your network. 0 means use the challenge only once.
+ (disable challenge caching)
+ See max_ntlm_challenge_lifetime for more information.
+ auth_param ntlm max_challenge_reuses 0
+
+ "max_challenge_lifetime" timespan
+ The maximum time period that a ntlm challenge is reused over.
+ The actual period will be the minimum of this time AND the number of
+ reused challenges.
+ auth_param ntlm max_challenge_lifetime 2 minutes
+
+NOCOMMENT_START
+#Recommended minimum configuration:
+#auth_param ntlm program <uncomment and complete this line to activate>
+auth_param ntlm children 5
+auth_param ntlm max_challenge_reuses 0
+auth_param ntlm max_challenge_lifetime 2 minutes
+#auth_param basic program <uncomment and complete this line>
+auth_param basic children 5
+auth_param basic realm Squid proxy-caching web server
+auth_param basic credentialsttl 2 hours
+NOCOMMENT_END
+DOC_END
+
+NAME: authenticate_cache_garbage_interval
+TYPE: time_t
+DEFAULT: 1 hour
+LOC: Config.authenticateGCInterval
+DOC_START
+ The time period between garbage collection across the username cache.
+ This is a tradeoff between memory utilisation (long intervals - say
+ 2 days) and CPU (short intervals - say 1 minute). Only change if
+ you have good reason to.
DOC_END
NAME: authenticate_ttl
DEFAULT: 1 hour
LOC: Config.authenticateTTL
DOC_START
- The time a checked username/password combination remains cached.
- If a wrong password is given for a cached user, the user gets
- removed from the username/password cache forcing a revalidation.
+ The time a user & their credentials stay in the logged in user cache
+ since their last request. When the garbage interval passes, all
+ user credentials that have passed their TTL are removed from memory.
DOC_END
NAME: authenticate_ip_ttl
LOC: Config.onoff.authenticateIpTTLStrict
DEFAULT: on
DOC_START
- This option makes authenticate_ip_ttl a bit stricted. With this
+ This option makes authenticate_ip_ttl a bit stricter. With this
enabled authenticate_ip_ttl will deny all access from other IP
addresses until the TTL has expired, and the IP address "owning"
the userid will not be forced to reauthenticate.
the Squid FAQ (http://squid.nlanr.net/Squid/FAQ/FAQ-10.html).
DOC_END
-NAME: proxy_auth_realm
-TYPE: eol
-DEFAULT: Squid proxy-caching web server
-LOC: Config.proxyAuthRealm
-DOC_START
- Specifies the realm name which is to be reported to the client for
- proxy authentication (part of the text the user will see when
- prompted their username and password).
-DOC_END
-
-
NAME: ident_lookup_access
TYPE: acl_access
IFDEF: USE_IDENT
/*
- * $Id: client_side.cc,v 1.521 2001/01/05 09:51:36 adrian Exp $
+ * $Id: client_side.cc,v 1.522 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 33 Client-side Routines
* AUTHOR: Duane Wessels
static log_type clientProcessRequest2(clientHttpRequest * http);
static int clientReplyBodyTooLarge(int clen);
static int clientRequestBodyTooLarge(int clen);
+static void clientProcessBody(ConnStateData * conn);
static int
checkAccelOnly(clientHttpRequest * http)
clientIdentDone(const char *ident, void *data)
{
ConnStateData *conn = data;
- if (ident)
- xstrncpy(conn->ident, ident, sizeof(conn->ident));
- else
- xstrncpy(conn->ident, "-", sizeof(conn->ident));
+ xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
}
+
#endif
static aclCheck_t *
ConnStateData *conn = http->conn;
ch = aclChecklistCreate(acl,
http->request,
- conn->ident);
-#if USE_IDENT
+ conn->rfc931);
+
/*
* hack for ident ACL. It needs to get full addresses, and a
* place to store the ident result on persistent connections...
*/
+ /* connection oriented auth also needs these two lines for it's operation. */
ch->conn = conn;
cbdataLock(ch->conn);
-#endif
+
return ch;
}
RequestMethodStr[http->request->method], http->uri,
answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
AclMatchedName ? AclMatchedName : "NO ACL's");
- if (http->acl_checklist->auth_user)
- proxy_auth_msg = http->acl_checklist->auth_user->message;
+ proxy_auth_msg = authenticateAuthUserRequestMessage(http->conn->auth_user_request ? http->conn->auth_user_request : http->request->auth_user_request);
http->acl_checklist = NULL;
if (answer == ACCESS_ALLOWED) {
safe_free(http->uri);
err = errorCon(page_id, status);
err->request = requestLink(http->request);
err->src_addr = http->conn->peer.sin_addr;
- err->proxy_auth_msg = proxy_auth_msg;
+ if (http->conn->auth_user_request)
+ err->auth_user_request = http->conn->auth_user_request;
+ else if (http->request->auth_user_request)
+ err->auth_user_request = http->request->auth_user_request;
+ /* lock for the error state */
+ if (err->auth_user_request)
+ authenticateAuthUserRequestLock(err->auth_user_request);
err->callback_data = NULL;
errorAppendEntry(http->entry, err);
}
new_request->my_addr = old_request->my_addr;
new_request->my_port = old_request->my_port;
new_request->flags.redirected = 1;
- if (old_request->user_ident[0])
- xstrncpy(new_request->user_ident, old_request->user_ident,
- USER_IDENT_SZ);
- if (old_request->body) {
- new_request->body = xmalloc(old_request->body_sz);
- xmemcpy(new_request->body, old_request->body, old_request->body_sz);
- new_request->body_sz = old_request->body_sz;
+ new_request->auth_user_request = old_request->auth_user_request;
+ if (old_request->body_connection) {
+ new_request->body_connection = old_request->body_connection;
+ old_request->body_connection = NULL;
}
new_request->content_length = old_request->content_length;
new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
MemObject *mem = NULL;
debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http->entry));
if (!clientCheckTransferDone(http)) {
+ if (request && request->body_connection)
+ clientAbortBody(request); /* abort body transter */
#if MYSTERIOUS_CODE
/*
* DW: this seems odd here, is it really needed? It causes
http->al.http.version = request->http_ver;
http->al.headers.request = xstrdup(mb.buf);
http->al.hier = request->hier;
- if (request->user_ident[0])
- http->al.cache.ident = request->user_ident;
- else
- http->al.cache.ident = conn->ident;
+ if (request->auth_user_request) {
+ http->al.cache.authuser = xstrdup(authenticateUserRequestUsername(request->auth_user_request));
+ authenticateAuthUserRequestUnlock(request->auth_user_request);
+ request->auth_user_request = NULL;
+ }
+ if (conn->rfc931[0])
+ http->al.cache.rfc931 = conn->rfc931;
packerClean(&p);
memBufClean(&mb);
}
requestUnlink(http->request);
assert(http != http->next);
assert(http->conn->chr != NULL);
+ /* Unlink us from the clients request list */
H = &http->conn->chr;
while (*H) {
if (*H == http)
clientHttpRequest *http;
debug(33, 3) ("connStateFree: FD %d\n", fd);
assert(connState != NULL);
+ authenticateOnCloseConnection(connState);
clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
while ((http = connState->chr) != NULL) {
assert(http->conn == connState);
if (req->protocol == PROTO_HTTP)
return httpCachable(method);
/* FTP is always cachable */
- if (req->protocol == PROTO_GOPHER)
- return gopherCachable(url);
if (req->protocol == PROTO_WAIS)
return 0;
if (method == METHOD_CONNECT)
return 0;
if (method == METHOD_TRACE)
return 0;
+ if (method == METHOD_PUT)
+ return 0;
+ if (method == METHOD_POST)
+ return 0; /* XXX POST may be cached sometimes.. ignored for now */
+ if (req->protocol == PROTO_GOPHER)
+ return gopherCachable(url);
if (req->protocol == PROTO_CACHEOBJ)
return 0;
return 1;
httpHeaderPutInt(hdr, HDR_AGE,
squid_curtime - http->entry->timestamp);
}
+ /* Handle authentication headers */
+ if (request->auth_user_request)
+ authenticateFixHeader(rep, request->auth_user_request, request, http->flags.accel);
/* Append X-Cache */
httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
is_hit ? "HIT" : "MISS", getMyHostname());
if ((http = conn->chr) == NULL) {
debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
conn->fd);
- fd_note(conn->fd, "Reading next request");
+ fd_note(conn->fd, "Waiting for next request");
/*
* Set the timeout BEFORE calling clientReadRequest().
*/
}
/* yes, continue */
http->log_type = LOG_TCP_MISS;
- } else if (r->content_length >= 0) {
- /*
- * Need to initialize pump even if content-length: 0
- */
- http->log_type = LOG_TCP_MISS;
- /* XXX oof, POST can be cached! */
- pumpInit(fd, r, http->uri);
} else {
http->log_type = clientProcessRequest2(http);
}
clientReadDefer(int fdnotused, void *data)
{
ConnStateData *conn = data;
- return conn->defer.until > squid_curtime;
+ if (conn->body.size_left)
+ return conn->in.offset >= conn->in.size;
+ else
+ return conn->defer.until > squid_curtime;
}
static void
* whole, not individual read() calls. Plus, it breaks our
* lame half-close detection
*/
- commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
- if (size == 0) {
- if (conn->chr == NULL) {
+ if (size > 0) {
+ conn->in.offset += size;
+ conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
+ } else if (size == 0 && len > 0) {
+ if (conn->chr == NULL && conn->in.offset == 0) {
/* no current or pending requests */
+ debug(33, 4) ("clientReadRequest: FD %d closed\n", fd);
comm_close(fd);
return;
} else if (!Config.onoff.half_closed_clients) {
/* admin doesn't want to support half-closed client sockets */
+ debug(33, 3) ("clientReadRequest: FD %d aborted (half_closed_clients disabled)\n", fd);
comm_close(fd);
return;
}
conn->defer.until = squid_curtime + 1;
conn->defer.n++;
fd_note(fd, "half-closed");
- return;
+ /* There is one more close check at the end, to detect aborted
+ * (partial) requests. At this point we can't tell if the request
+ * is partial.
+ */
+ /* Continue to process previously read data */
} else if (size < 0) {
if (!ignoreErrno(errno)) {
debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
return;
}
/* Continue to process previously read data */
- size = 0;
}
- conn->in.offset += size;
- /* Skip leading (and trailing) whitespace */
- while (conn->in.offset > 0) {
+ commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
+ /* Process request body if any */
+ if (conn->in.offset > 0 && conn->body.callback != NULL)
+ clientProcessBody(conn);
+ /* Process next request */
+ while (conn->in.offset > 0 && conn->body.size_left == 0) {
int nrequests;
size_t req_line_sz;
+ /* Skip leading (and trailing) whitespace */
while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
conn->in.offset--;
conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
break;
}
+ conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
+ if (nrequests == 0)
+ fd_note(conn->fd, "Reading next request");
/* Process request */
http = parseHttpRequest(conn,
&method,
errorAppendEntry(http->entry, err);
break;
}
- if (0 == clientCheckContentLength(request)) {
+ if (!clientCheckContentLength(request)) {
err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
err->src_addr = conn->peer.sin_addr;
err->request = requestLink(request);
break;
}
http->request = requestLink(request);
- /*
- * We need to set the keepalive flag before doing some
- * hacks for POST/PUT requests below. Maybe we could
- * set keepalive flag even earlier.
- */
clientSetKeepaliveFlag(http);
- /*
- * break here if the request has a content-length
- * because there is a reqeust body following and we
- * don't want to parse it as though it was new request.
- */
- if (request->content_length >= 0) {
- int copy_len = XMIN(conn->in.offset, request->content_length);
- if (copy_len > 0) {
- assert(conn->in.offset >= copy_len);
- request->body_sz = copy_len;
- request->body = xmalloc(request->body_sz);
- xmemcpy(request->body, conn->in.buf, request->body_sz);
- conn->in.offset -= copy_len;
- if (conn->in.offset)
- xmemmove(conn->in.buf, conn->in.buf + copy_len, conn->in.offset);
- }
- /*
- * if we didn't get the full body now, then more will
- * be arriving on the client socket. Lets cancel
- * the read handler until this request gets forwarded.
- */
- if (request->body_sz < request->content_length)
- commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
- if (request->content_length < 0)
- (void) 0;
- else if (clientRequestBodyTooLarge(request->content_length)) {
+ /* Do we expect a request-body? */
+ if (request->content_length > 0) {
+ conn->body.size_left = request->content_length;
+ request->body_connection = conn;
+ /* Is it too large? */
+ if (clientRequestBodyTooLarge(request->content_length)) {
err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
err->request = requestLink(request);
http->entry = clientCreateStoreEntry(http,
}
}
clientAccessCheck(http);
- continue; /* while offset > 0 */
+ continue; /* while offset > 0 && body.size_left == 0 */
} else if (parser_return_code == 0) {
/*
* Partial request received; reschedule until parseHttpRequest()
}
break;
}
+ } /* while offset > 0 && conn->body.size_left == 0 */
+ /* Check if a half-closed connection was aborted in the middle */
+ if (F->flags.socket_eof) {
+ if (conn->in.offset != conn->body.size_left) { /* != 0 when no request body */
+ /* Partial request received. Abort client connection! */
+ debug(33, 3) ("clientReadRequest: FD %d aborted\n", fd);
+ comm_close(fd);
+ return;
+ }
+ }
+}
+
+/* file_read like function, for reading body content */
+void
+clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata)
+{
+ ConnStateData *conn = request->body_connection;
+ if (!conn) {
+ debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
+ callback(buf, 0, cbdata); /* Signal end of body */
+ return;
}
+ debug(33, 2) ("clientReadBody: start fd=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, conn->body.size_left, conn->in.offset, callback, request);
+ conn->body.callback = callback;
+ conn->body.cbdata = cbdata;
+ conn->body.buf = buf;
+ conn->body.bufsize = size;
+ conn->body.request = requestLink(request);
+ if (conn->in.offset) {
+ /* Data available */
+ clientProcessBody(conn);
+ } else {
+ debug(33, 2) ("clientReadBody: fd %d wait for clientReadRequest\n", conn->fd);
+ }
+}
+
+/* Called by clientReadRequest to process body content */
+static void
+clientProcessBody(ConnStateData * conn)
+{
+ int size;
+ char *buf = conn->body.buf;
+ void *cbdata = conn->body.cbdata;
+ CBCB *callback = conn->body.callback;
+ request_t *request = conn->body.request;
+ /* Note: request is null while eating "aborted" transfers */
+ debug(33, 2) ("clientProcessBody: start fd=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, conn->body.size_left, conn->in.offset, callback, request);
+ /* Some sanity checks... */
+ assert(conn->body.size_left > 0);
+ assert(conn->in.offset > 0);
+ assert(callback != NULL);
+ assert(buf != NULL);
+ /* How much do we have to process? */
+ size = conn->in.offset;
+ if (size > conn->body.size_left) /* only process the body part */
+ size = conn->body.size_left;
+ if (size > conn->body.bufsize) /* don't copy more than requested */
+ size = conn->body.bufsize;
+ xmemcpy(buf, conn->in.buf, size);
+ conn->body.size_left -= size;
+ /* Move any remaining data */
+ conn->in.offset -= size;
+ if (conn->in.offset > 0)
+ xmemmove(conn->in.buf, conn->in.buf + size, conn->in.offset);
+ /* Remove request link if this is the last part of the body, as
+ * clientReadRequest automatically continues to process next request */
+ if (conn->body.size_left <= 0 && request != NULL)
+ request->body_connection = NULL;
+ /* Remove clientReadBody arguments (the call is completed) */
+ conn->body.request = NULL;
+ conn->body.callback = NULL;
+ conn->body.buf = NULL;
+ conn->body.bufsize = 0;
+ /* Remember that we have touched the body, not restartable */
+ if (request != NULL)
+ request->flags.body_sent = 1;
+ /* Invoke callback function */
+ callback(buf, size, cbdata);
+ if (request != NULL)
+ requestUnlink(request); /* Linked in clientReadBody */
+ debug(33, 2) ("clientProcessBody: end fd=%d size=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, size, conn->body.size_left, conn->in.offset, callback, request);
+ return;
+}
+
+/* A dummy handler that throws away a request-body */
+static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
+void
+clientReadBodyAbortHandler(char *buf, size_t size, void *data)
+{
+ ConnStateData *conn = (ConnStateData *) data;
+ debug(33, 2) ("clientReadBodyAbortHandler: fd=%d body_size=%d in.offset=%d\n", conn->fd, conn->body.size_left, conn->in.offset);
+ if (size != 0 && conn->body.size_left != 0) {
+ debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n", conn->fd);
+ conn->body.callback = clientReadBodyAbortHandler;
+ conn->body.buf = bodyAbortBuf;
+ conn->body.bufsize = sizeof(bodyAbortBuf);
+ conn->body.cbdata = data;
+ }
+}
+
+/* Abort a body request */
+int
+clientAbortBody(request_t * request)
+{
+ ConnStateData *conn = request->body_connection;
+ char *buf;
+ CBCB *callback;
+ void *cbdata;
+ request->body_connection = NULL;
+ if (!conn || conn->body.size_left <= 0)
+ return 0; /* No body to abort */
+ if (conn->body.callback != NULL) {
+ buf = conn->body.buf;
+ callback = conn->body.callback;
+ cbdata = conn->body.cbdata;
+ assert(request == conn->body.request);
+ conn->body.buf = NULL;
+ conn->body.callback = NULL;
+ conn->body.cbdata = NULL;
+ conn->body.request = NULL;
+ callback(buf, -1, cbdata); /* Signal abort to clientReadBody caller */
+ requestUnlink(request);
+ }
+ clientReadBodyAbortHandler(NULL, -1, conn); /* Install abort handler */
+ /* clientProcessBody() */
+ return 1; /* Aborted */
}
/* general lifetime handler for HTTP requests */
/*
- * $Id: defines.h,v 1.87 2001/01/06 11:41:37 hno Exp $
+ * $Id: defines.h,v 1.88 2001/01/07 23:36:38 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
#define DefaultDnsChildrenMax 32 /* 32 processes */
#define DefaultRedirectChildrenMax 32 /* 32 processes */
-#define DefaultAuthenticateChildrenMax 32 /* 32 processes */
#define MAXHTTPPORTS 12
#define COMM_OK (0)
#define REDIRECT_DONE 2
#define AUTHENTICATE_AV_FACTOR 1000
+/* AUTHENTICATION */
-#define AUTHENTICATE_NONE 0
-#define AUTHENTICATE_PENDING 1
-#define AUTHENTICATE_DONE 2
+#define NTLM_CHALLENGE_SZ 300
#define CONNECT_PORT 443
/*
- * $Id: enums.h,v 1.181 2001/01/07 20:11:18 hno Exp $
+ * $Id: enums.h,v 1.182 2001/01/07 23:36:38 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
ACL_LOOKUP_NEEDED,
ACL_LOOKUP_PENDING,
ACL_LOOKUP_DONE,
- ACL_PROXY_AUTH_NEEDED
+ ACL_PROXY_AUTH_NEEDED,
} acl_lookup_state;
enum {
ACCESS_REQ_PROXY_AUTH
} allow_t;
+typedef enum {
+ AUTH_UNKNOWN, /* default */
+ AUTH_BASIC,
+ AUTH_NTLM,
+ AUTH_BROKEN /* known type, but broken data */
+} auth_type_t;
+
+typedef enum {
+ AUTHENTICATE_STATE_NONE,
+ AUTHENTICATE_STATE_NEGOTIATE,
+ AUTHENTICATE_STATE_CHALLENGE,
+ AUTHENTICATE_STATE_RESPONSE,
+ AUTHENTICATE_STATE_DONE
+} auth_state_t; /* connection level auth state */
+
+/* stateful helper callback response codes */
+typedef enum {
+ S_HELPER_UNKNOWN,
+ S_HELPER_RESERVE,
+ S_HELPER_RELEASE,
+ S_HELPER_DEFER
+} stateful_helper_callback_t;
+
+/* stateful helper reservation info */
+typedef enum {
+ S_HELPER_FREE, /* available for requests */
+ S_HELPER_RESERVED, /* in a reserved state - no active request, but state data in the helper shouldn't be disturbed */
+ S_HELPER_DEFERRED /* available for requests, and at least one more will come from a previous caller with the server pointer */
+} stateful_helper_reserve_t;
+
+
#if SQUID_SNMP
enum {
SNMP_C_VIEW,
MEM_ACL_IP_DATA,
MEM_ACL_LIST,
MEM_ACL_NAME_LIST,
+ MEM_AUTH_USER_T,
+ MEM_AUTH_USER_HASH,
+ MEM_ACL_PROXY_AUTH_MATCH,
MEM_ACL_USER_DATA,
- MEM_ACL_PROXY_AUTH_USER,
MEM_ACL_TIME_DATA,
MEM_CACHEMGR_PASSWD,
#if USE_CACHE_DIGESTS
MEM_HASH_LINK,
MEM_HASH_TABLE,
MEM_HELPER,
- MEM_HELPER_REQUEST,
+ MEM_HELPER_STATEFUL,
MEM_HELPER_SERVER,
+ MEM_HELPER_STATEFUL_SERVER,
+ MEM_HELPER_REQUEST,
+ MEM_HELPER_STATEFUL_REQUEST,
MEM_HIERARCHYLOGENTRY,
#if USE_HTCP
MEM_HTCP_SPECIFIER,
MEM_PUMP_STATE_DATA,
MEM_CLIENT_REQ_BUF,
MEM_MAX
+#ifdef NTLM_CACHING
+ ,MEM_NTLM_AUTH_CACHE
+#endif
} mem_type;
/*
CBDATA_generic_cbdata,
CBDATA_helper,
CBDATA_helper_server,
+ CBDATA_statefulhelper,
+ CBDATA_helper_stateful_server,
CBDATA_HttpStateData,
CBDATA_peer,
CBDATA_ps_state,
/*
- * $Id: errorpage.cc,v 1.160 2001/01/05 09:51:37 adrian Exp $
+ * $Id: errorpage.cc,v 1.161 2001/01/07 23:36:38 hno Exp $
*
* DEBUG: section 4 Error Generation
* AUTHOR: Duane Wessels
/* local constant and vars */
-static const char *const proxy_auth_challenge_fmt = "Basic realm=\"%s\"";
-
/*
* note: hard coded error messages are not appended with %S automagically
* to give you more control on the format
storeBuffer(entry);
rep = errorBuildReply(err);
/* Add authentication header */
- switch (err->http_status) {
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- /* Proxy authorisation needed */
- httpHeaderPutStrf(&rep->header, HDR_PROXY_AUTHENTICATE,
- proxy_auth_challenge_fmt, Config.proxyAuthRealm);
- break;
- case HTTP_UNAUTHORIZED:
- /* WWW Authorisation needed */
- httpHeaderPutStrf(&rep->header, HDR_WWW_AUTHENTICATE,
- proxy_auth_challenge_fmt, Config.proxyAuthRealm);
- break;
- default:
- /* Keep GCC happy */
- break;
- }
+ /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
+ * depends on authenticate behaviour: all schemes to date send no extra data
+ * on 407/401 responses, and do not check the accel state on 401/407 responses
+ */
+ authenticateFixHeader(rep, err->auth_user_request, err->request, 0);
httpReplySwapOut(rep, entry);
- httpReplyDestroy(rep);
- mem->reply->sline.status = err->http_status;
- mem->reply->content_length = -1;
+ httpReplyAbsorb(mem->reply, rep);
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
storeBufferFlush(entry);
storeComplete(entry);
ErrorState *err = data;
debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
if (errflag != COMM_ERR_CLOSING) {
- if (err->callback)
+ if (err->callback) {
+ debug(4, 3) ("errorSendComplete: callback\n");
err->callback(fd, err->callback_data, size);
- else
+ } else {
comm_close(fd);
+ debug(4, 3) ("errorSendComplete: comm_close\n");
+ }
}
errorStateFree(err);
}
safe_free(err->url);
safe_free(err->host);
safe_free(err->dnsserver_msg);
- safe_free(err->proxy_auth_msg);
safe_free(err->request_hdrs);
wordlistDestroy(&err->ftp.server_msg);
safe_free(err->ftp.request);
safe_free(err->ftp.reply);
+ if (err->auth_user_request)
+ authenticateAuthUserRequestUnlock(err->auth_user_request);
cbdataFree(err);
}
p = "[not available]";
break;
case 'm':
- p = err->proxy_auth_msg ? err->proxy_auth_msg : "[not available]";
+ p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
break;
case 'M':
p = r ? RequestMethodStr[r->method] : "[unkown method]";
/*
- * $Id: forward.cc,v 1.78 2001/01/05 09:51:37 adrian Exp $
+ * $Id: forward.cc,v 1.79 2001/01/07 23:36:38 hno Exp $
*
* DEBUG: section 17 Request Forwarding
* AUTHOR: Duane Wessels
return 0;
if (fwdState->flags.dont_retry)
return 0;
- if (fwdState->request->content_length >= 0)
- if (0 == pumpRestart(fwdState->request))
- return 0;
+ if (fwdState->request->flags.body_sent)
+ return 0;
return 1;
}
fwdState->request->peer_login = p->login;
httpStart(fwdState);
} else {
+ fwdState->request->peer_login = NULL;
switch (request->protocol) {
case PROTO_HTTP:
httpStart(fwdState);
}
if (fwdState->n_tries > 9)
return 0;
- if (fwdState->request->content_length >= 0)
- if (0 == pumpRestart(fwdState->request))
- return 0;
+ if (fwdState->request->flags.body_sent)
+ return 0;
assert(fs);
fwdState->servers = fs->next;
fwdServerFree(fs);
/*
- * $Id: ftp.cc,v 1.304 2001/01/07 10:57:15 hno Exp $
+ * $Id: ftp.cc,v 1.305 2001/01/07 23:36:38 hno Exp $
*
* DEBUG: section 9 File Transfer Protocol (FTP)
* AUTHOR: Harvest Derived
/* Local functions */
static CNCB ftpPasvCallback;
static PF ftpDataRead;
+static PF ftpDataWrite;
+static CWCB ftpDataWriteCallback;
static PF ftpStateFree;
-static PF ftpPumpClosedData;
static PF ftpTimeout;
static PF ftpReadControlReply;
static CWCB ftpWriteCommandCallback;
static void ftpAppendSuccessHeader(FtpStateData * ftpState);
static void ftpAuthRequired(HttpReply * reply, request_t * request, const char *realm);
static void ftpHackShortcut(FtpStateData * ftpState, FTPSM * nextState);
-static void ftpPutStart(FtpStateData *);
-static CWCB ftpPutTransferDone;
static void ftpUnhack(FtpStateData * ftpState);
static void ftpScheduleReadControlReply(FtpStateData *, int);
static void ftpHandleControlReply(FtpStateData *);
static FTPSM ftpGetFile;
static FTPSM ftpSendCwd;
static FTPSM ftpReadCwd;
+static FTPSM ftpRestOrList;
static FTPSM ftpSendList;
static FTPSM ftpSendNlst;
static FTPSM ftpReadList;
static FTPSM ftpSendRetr;
static FTPSM ftpReadRetr;
static FTPSM ftpReadTransferDone;
-static FTPSM ftpSendQuit;
-static FTPSM ftpReadQuit;
-static FTPSM ftpFail;
-static FTPSM ftpDataTransferDone;
-static FTPSM ftpRestOrList;
static FTPSM ftpSendStor;
static FTPSM ftpReadStor;
+static FTPSM ftpWriteTransferDone;
static FTPSM ftpSendReply;
-static FTPSM ftpTryMkdir;
+static FTPSM ftpSendMkdir;
static FTPSM ftpReadMkdir;
+static FTPSM ftpFail;
+static FTPSM ftpSendQuit;
+static FTPSM ftpReadQuit;
/************************************************
** State Machine Description (excluding hacks) **
*************************************************
Pass Type
Type TraverseDirectory / GetFile
TraverseDirectory Cwd / GetFile / ListDir
-Cwd TraverseDirectory
+Cwd TraverseDirectory / Mkdir
GetFile Mdtm
Mdtm Size
Size Pasv
ListDir Pasv
-Pasv RestOrList
-RestOrList Rest / Retr / Nlst / List
+Pasv FileOrList
+FileOrList Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
Rest Retr
-Retr / Nlst / List (ftpDataRead on datachannel)
-(ftpDataRead) ReadTransferDone
+Retr / Nlst / List DataRead* (on datachannel)
+DataRead* ReadTransferDone
ReadTransferDone DataTransferDone
+Stor DataWrite* (on datachannel)
+DataWrite* RequestPutBody** (from client)
+RequestPutBody** DataWrite* / WriteTransferDone
+WriteTransferDone DataTransferDone
DataTransferDone Quit
Quit -
************************************************/
ftpReadStor, /* SENT_STOR */
ftpReadQuit, /* SENT_QUIT */
ftpReadTransferDone, /* READING_DATA (RETR,LIST,NLST) */
- ftpSendReply, /* WRITING_DATA (STOR) */
+ ftpWriteTransferDone, /* WRITING_DATA (STOR) */
+ ftpSendReply, /* WRITTEN_DATA? (STOR) */
ftpReadMkdir /* SENT_MKDIR */
};
}
static void
-ftpReadComplete(FtpStateData * ftpState)
+ftpDataComplete(FtpStateData * ftpState)
{
- debug(9, 3) ("ftpReadComplete\n");
- /* Connection closed; retrieval done. */
+ debug(9, 3) ("ftpDataComplete\n");
+ /* Connection closed; transfer done. */
if (ftpState->data.fd > -1) {
/*
* close data socket so it does not occupy resources while
int j;
int bin;
StoreEntry *entry = ftpState->entry;
- MemObject *mem = entry->mem_obj;
size_t read_sz;
#if DELAY_POOLS
+ MemObject *mem = entry->mem_obj;
delay_id delay_id = delayMostBytesAllowed(mem);
#endif
assert(fd == ftpState->data.fd);
return;
}
} else if (len == 0) {
- ftpReadComplete(ftpState);
+ ftpDataComplete(ftpState);
} else {
if (ftpState->flags.isdir) {
ftpParseListing(ftpState);
storeAppend(entry, ftpState->data.buf, len);
ftpState->data.offset = 0;
}
- if (ftpState->size > 0 && mem->inmem_hi >= ftpState->size + mem->reply->hdr_sz)
- ftpReadComplete(ftpState);
- else
- commSetSelect(fd,
- COMM_SELECT_READ,
- ftpDataRead,
- data,
- Config.Timeout.read);
+ commSetSelect(fd,
+ COMM_SELECT_READ,
+ ftpDataRead,
+ data,
+ Config.Timeout.read);
}
}
if (errflag == COMM_ERR_CLOSING)
return;
if (errflag) {
- debug(50, 1) ("ftpWriteCommandCallback: FD %d: %s\n", fd, xstrerror());
+ debug(9, 1) ("ftpWriteCommandCallback: FD %d: %s\n", fd, xstrerror());
ftpFailed(ftpState, ERR_WRITE_ERROR);
/* ftpFailed closes ctrl.fd and frees ftpState */
return;
if (!ftpState->flags.put)
ftpFail(ftpState);
else
- ftpTryMkdir(ftpState);
+ ftpSendMkdir(ftpState);
}
}
static void
-ftpTryMkdir(FtpStateData * ftpState)
+ftpSendMkdir(FtpStateData * ftpState)
{
char *path = ftpState->filepath;
- debug(9, 3) ("ftpTryMkdir: with path=%s\n", path);
+ debug(9, 3) ("ftpSendMkdir: with path=%s\n", path);
snprintf(cbuf, 1024, "MKD %s\r\n", path);
ftpWriteCommand(cbuf, ftpState);
ftpState->state = SENT_MKDIR;
ftpRestOrList(FtpStateData * ftpState)
{
debug(9, 3) ("This is ftpRestOrList\n");
- if (ftpState->flags.put) {
- debug(9, 3) ("ftpRestOrList: Sending STOR request...\n");
- ftpSendStor(ftpState);
- } else if (ftpState->typecode == 'D') {
- /* XXX This should NOT be here */
- ftpSendNlst(ftpState); /* sec 3.2.2 of RFC 1738 */
+ if (ftpState->typecode == 'D') {
ftpState->flags.isdir = 1;
ftpState->flags.use_base = 1;
+ if (ftpState->flags.put) {
+ ftpSendMkdir(ftpState); /* PUT name;type=d */
+ } else {
+ ftpSendNlst(ftpState); /* GET name;type=d sec 3.2.2 of RFC 1738 */
+ }
+ } else if (ftpState->flags.put) {
+ debug(9, 3) ("ftpRestOrList: Sending STOR request...\n");
+ ftpSendStor(ftpState);
} else if (ftpState->flags.isdir)
ftpSendList(ftpState);
else if (ftpRestartable(ftpState))
if (code == 125 || (code == 150 && ftpState->data.host)) {
/* Begin data transfer */
debug(9, 3) ("ftpReadStor: starting data transfer\n");
+ commSetSelect(ftpState->data.fd,
+ COMM_SELECT_WRITE,
+ ftpDataWrite,
+ ftpState,
+ Config.Timeout.read);
/*
- * Cancel the timeout on the Control socket, pumpStart will
+ * Cancel the timeout on the Control socket and
* establish one on the data socket.
*/
commSetTimeout(ftpState->ctrl.fd, -1, NULL, NULL);
- ftpPutStart(ftpState);
- debug(9, 3) ("ftpReadStor: writing data channel\n");
+ commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout,
+ ftpState);
ftpState->state = WRITING_DATA;
+ debug(9, 3) ("ftpReadStor: writing data channel\n");
} else if (code == 150) {
/* Accept data channel */
debug(9, 3) ("ftpReadStor: accepting data channel\n");
/* Connection closed; retrieval done. */
if (ftpState->flags.html_header_sent)
ftpListingFinish(ftpState);
- if (!ftpState->flags.put) {
- storeTimestampsSet(ftpState->entry);
- fwdComplete(ftpState->fwd);
- }
- ftpDataTransferDone(ftpState);
+ ftpSendQuit(ftpState);
} else { /* != 226 */
debug(9, 1) ("ftpReadTransferDone: Got code %d after reading data\n",
code);
}
}
+/* This will be called when there is data available to put */
static void
-ftpDataTransferDone(FtpStateData * ftpState)
+ftpRequestBody(char *buf, size_t size, void *data)
{
- debug(9, 3) ("This is ftpDataTransferDone\n");
- if (ftpState->data.fd > -1) {
- comm_close(ftpState->data.fd);
- ftpState->data.fd = -1;
+ FtpStateData *ftpState = (FtpStateData *) data;
+ debug(9, 3) ("ftpRequestBody: buf=%p size=%d ftpState=%p\n", buf, size, data);
+ ftpState->data.offset = size;
+ if (size > 0) {
+ /* DataWrite */
+ comm_write(ftpState->data.fd, buf, size, ftpDataWriteCallback, data, NULL);
+ } else if (size < 0) {
+ /* Error */
+ debug(9, 1) ("ftpRequestBody: request aborted");
+ ftpFailed(ftpState, ERR_READ_ERROR);
+ } else if (size == 0) {
+ /* End of transfer */
+ ftpDataComplete(ftpState);
+ }
+}
+
+/* This will be called when the put write is completed */
+static void
+ftpDataWriteCallback(int fd, char *buf, size_t size, int err, void *data)
+{
+ FtpStateData *ftpState = (FtpStateData *) data;
+ if (!err) {
+ /* Shedule the rest of the request */
+ clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
+ } else {
+ debug(9, 1) ("ftpDataWriteCallback: write error: %s\n", xstrerror());
+ ftpFailed(ftpState, ERR_WRITE_ERROR);
}
- ftpSendQuit(ftpState);
+}
+
+static void
+ftpDataWrite(int ftp, void *data)
+{
+ FtpStateData *ftpState = (FtpStateData *) data;
+ debug(9, 3) ("ftpDataWrite\n");
+ /* This starts the body transfer */
+ clientReadBody(ftpState->request, ftpState->data.buf, ftpState->data.size, ftpRequestBody, ftpState);
+}
+
+static void
+ftpWriteTransferDone(FtpStateData * ftpState)
+{
+ int code = ftpState->ctrl.replycode;
+ debug(9, 3) ("This is ftpWriteTransferDone\n");
+ if (code != 226) {
+ debug(9, 1) ("ftpReadTransferDone: Got code %d after sending data\n",
+ code);
+ ftpFailed(ftpState, ERR_FTP_PUT_ERROR);
+ return;
+ }
+ storeTimestampsSet(ftpState->entry); /* XXX Is this needed? */
+ ftpSendReply(ftpState);
}
static void
fwdFail(ftpState->fwd, err);
}
-static void
-ftpPumpClosedData(int data_fd, void *data)
-{
- FtpStateData *ftpState = data;
- assert(data_fd == ftpState->data.fd);
- /*
- * Ugly pump module closed our server-side. Deal with it.
- * The data FD is already closed, so just set it to -1.
- */
- ftpState->data.fd = -1;
- /*
- * Currently, thats all we have to do. Because the upload failed,
- * storeAbort() will be called on the reply entry. That will
- * call fwdAbort, which closes ftpState->ctrl.fd and then
- * ftpStateFree gets called.
- */
-}
-
-static void
-ftpPutStart(FtpStateData * ftpState)
-{
- debug(9, 3) ("ftpPutStart\n");
- /*
- * sigh, we need this gross hack to detect when ugly pump module
- * aborts and wants to close the server-side.
- */
- comm_add_close_handler(ftpState->data.fd, ftpPumpClosedData, ftpState);
- pumpStart(ftpState->data.fd, ftpState->fwd, ftpPutTransferDone, ftpState);
-}
-
-static void
-ftpPutTransferDone(int fd, char *bufnotused, size_t size, int errflag, void *data)
-{
- FtpStateData *ftpState = data;
- if (ftpState->data.fd >= 0) {
- comm_remove_close_handler(fd, ftpPumpClosedData, ftpState);
- comm_close(ftpState->data.fd);
- ftpState->data.fd = -1;
- }
- ftpReadComplete(ftpState);
-}
-
static void
ftpSendReply(FtpStateData * ftpState)
{
/*
- * $Id: globals.h,v 1.99 2000/12/09 01:47:18 wessels Exp $
+ * $Id: globals.h,v 1.100 2001/01/07 23:36:39 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
extern request_flags null_request_flags;
extern int store_open_disk_fd; /* 0 */
extern const char *SwapDirType[];
+extern authscheme_entry_t *authscheme_list; /* NULL */
extern storefs_entry_t *storefs_list; /* NULL */
extern storerepl_entry_t *storerepl_list; /* NULL */
extern int store_swap_low; /* 0 */
extern int store_pages_max; /* 0 */
extern ssize_t store_maxobjsize; /* -1 */
extern RemovalPolicy *mem_policy;
+extern hash_table *proxy_auth_username_cache; /* NULL */
/*
- * $Id: helper.cc,v 1.21 2001/01/05 09:51:38 adrian Exp $
+ * $Id: helper.cc,v 1.22 2001/01/07 23:36:39 hno Exp $
*
* DEBUG: section 29 Helper process maintenance
* AUTHOR: Harvest Derived?
#define HELPER_MAX_ARGS 64
static PF helperHandleRead;
+static PF helperStatefulHandleRead;
static PF helperServerFree;
+static PF helperStatefulServerFree;
static void Enqueue(helper * hlp, helper_request *);
static helper_request *Dequeue(helper * hlp);
+static helper_stateful_request *StatefulDequeue(statefulhelper * hlp);
static helper_server *GetFirstAvailable(helper * hlp);
+static helper_stateful_server *StatefulGetFirstAvailable(statefulhelper * hlp);
static void helperDispatch(helper_server * srv, helper_request * r);
+static void helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r);
static void helperKickQueue(helper * hlp);
+static void helperStatefulKickQueue(statefulhelper * hlp);
static void helperRequestFree(helper_request * r);
-
+static void helperStatefulRequestFree(helper_stateful_request * r);
+static void StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r);
+static helper_stateful_request *StatefulServerDequeue(helper_stateful_server * srv);
+static void StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r);
+static void helperStatefulServerKickQueue(helper_stateful_server * srv);
void
helperOpenServers(helper * hlp)
helperKickQueue(hlp);
}
+void
+helperStatefulOpenServers(statefulhelper * hlp)
+{
+ char *s;
+ char *progname;
+ char *shortname;
+ char *procname;
+ char *args[HELPER_MAX_ARGS];
+ char fd_note_buf[FD_DESC_SZ];
+ helper_stateful_server *srv;
+ int nargs = 0;
+ int k;
+ int x;
+ int rfd;
+ int wfd;
+ wordlist *w;
+ if (hlp->cmdline == NULL)
+ return;
+ progname = hlp->cmdline->key;
+ if ((s = strrchr(progname, '/')))
+ shortname = xstrdup(s + 1);
+ else
+ shortname = xstrdup(progname);
+ debug(29, 1) ("helperStatefulOpenServers: Starting %d '%s' processes\n",
+ hlp->n_to_start, shortname);
+ procname = xmalloc(strlen(shortname) + 3);
+ snprintf(procname, strlen(shortname) + 3, "(%s)", shortname);
+ args[nargs++] = procname;
+ for (w = hlp->cmdline->next; w && nargs < HELPER_MAX_ARGS; w = w->next)
+ args[nargs++] = w->key;
+ args[nargs++] = NULL;
+ assert(nargs <= HELPER_MAX_ARGS);
+ for (k = 0; k < hlp->n_to_start; k++) {
+ getCurrentTime();
+ rfd = wfd = -1;
+ x = ipcCreate(hlp->ipc_type,
+ progname,
+ args,
+ shortname,
+ &rfd,
+ &wfd);
+ if (x < 0) {
+ debug(29, 1) ("WARNING: Cannot run '%s' process.\n", progname);
+ continue;
+ }
+ hlp->n_running++;
+ srv = CBDATA_ALLOC(helper_stateful_server, NULL);
+ srv->flags.alive = 1;
+ srv->flags.reserved = S_HELPER_FREE;
+ srv->deferred_requests = 0;
+ srv->index = k;
+ srv->rfd = rfd;
+ srv->wfd = wfd;
+ srv->buf = memAllocate(MEM_8K_BUF);
+ srv->buf_sz = 8192;
+ srv->offset = 0;
+ srv->parent = hlp;
+ if (hlp->datapool != NULL)
+ srv->data = memPoolAlloc(hlp->datapool);
+ cbdataLock(hlp); /* lock because of the parent backlink */
+ dlinkAddTail(srv, &srv->link, &hlp->servers);
+ if (rfd == wfd) {
+ snprintf(fd_note_buf, FD_DESC_SZ, "%s #%d", shortname, k + 1);
+ fd_note(rfd, fd_note_buf);
+ } else {
+ snprintf(fd_note_buf, FD_DESC_SZ, "reading %s #%d", shortname, k + 1);
+ fd_note(rfd, fd_note_buf);
+ snprintf(fd_note_buf, FD_DESC_SZ, "writing %s #%d", shortname, k + 1);
+ fd_note(wfd, fd_note_buf);
+ }
+ commSetNonBlocking(rfd);
+ if (wfd != rfd)
+ commSetNonBlocking(wfd);
+ comm_add_close_handler(rfd, helperStatefulServerFree, srv);
+ }
+ safe_free(shortname);
+ safe_free(procname);
+ helperStatefulKickQueue(hlp);
+}
+
+
void
helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data)
{
helperDispatch(srv, r);
else
Enqueue(hlp, r);
+ debug(29, 9) ("helperSubmit: %s\n", buf);
+}
+
+void
+helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver)
+{
+ helper_stateful_request *r = memAllocate(MEM_HELPER_STATEFUL_REQUEST);
+ helper_stateful_server *srv;
+ if (hlp == NULL) {
+ debug(29, 3) ("helperStatefulSubmit: hlp == NULL\n");
+ callback(data, 0, NULL);
+ return;
+ }
+ r->callback = callback;
+ r->data = data;
+ if (buf != NULL)
+ r->buf = xstrdup(buf);
+ else
+ r->placeholder = 1;
+ cbdataLock(r->data);
+ if ((buf != NULL) && lastserver) {
+ debug(29, 5) ("StatefulSubmit with lastserver %d\n", lastserver);
+ if (lastserver->flags.reserved != S_HELPER_RESERVED)
+ lastserver->deferred_requests--;
+ if (!(lastserver->request)) {
+ debug(29, 5) ("StatefulSubmit dispatching\n");
+ helperStatefulDispatch(lastserver, r);
+ } else {
+ debug(29, 5) ("StatefulSubmit queuing\n");
+ StatefulServerEnqueue(lastserver, r);
+ }
+ } else {
+ if ((srv = StatefulGetFirstAvailable(hlp))) {
+ helperStatefulDispatch(srv, r);
+ } else
+ StatefulEnqueue(hlp, r);
+ }
+ debug(29, 9) ("helperStatefulSubmit: placeholder: '%d', buf '%s'.\n", r->placeholder, buf);
+}
+
+helper_stateful_server *
+helperStatefulDefer(statefulhelper * hlp)
+/* find and add a deferred request to a server */
+{
+ dlink_node *n;
+ helper_stateful_server *srv = NULL, *rv = NULL;
+ if (hlp == NULL) {
+ debug(29, 3) ("helperStatefulReserve: hlp == NULL\n");
+ return NULL;
+ }
+ debug(29, 5) ("helperStatefulDefer: Running servers %d.\n", hlp->n_running);
+ if (hlp->n_running == 0) {
+ debug(29, 1) ("helperStatefulDefer: No running servers!. \n");
+ return NULL;
+ }
+ srv = StatefulGetFirstAvailable(hlp);
+ /* all currently busy:loop through servers and find server with the shortest queue */
+ rv = srv;
+ if (rv == NULL)
+ for (n = hlp->servers.head; n != NULL; n = n->next) {
+ srv = n->data;
+ if (srv->flags.reserved == S_HELPER_RESERVED)
+ continue;
+ if (!srv->flags.alive)
+ continue;
+ if ((hlp->IsAvailable != NULL) && (srv->data != NULL) &&
+ !(hlp->IsAvailable(srv->data)))
+ continue;
+ if ((rv != NULL) && (rv->deferred_requests < srv->deferred_requests))
+ continue;
+ rv = srv;
+ }
+ if (rv == NULL) {
+ debug(29, 1) ("helperStatefulDefer: None available.\n");
+ return NULL;
+ }
+ rv->flags.reserved = S_HELPER_DEFERRED;
+ rv->deferred_requests++;
+ return rv;
+}
+
+void
+helperStatefulReset(helper_stateful_server * srv)
+/* puts this helper back in the queue. the calling app is required to
+ * manage the state in the helper.
+ */
+{
+ statefulhelper *hlp = srv->parent;
+ helper_stateful_request *r;
+ r = srv->request;
+ if (r != NULL) {
+ /* reset attempt DURING an outstaning request */
+ debug(29, 1) ("helperStatefulReset: RESET During request %s \n",
+ hlp->id_name);
+ srv->flags.busy = 0;
+ srv->offset = 0;
+ helperStatefulRequestFree(r);
+ srv->request = NULL;
+ }
+ debug(29, 1) ("helperStatefulReset reset helper %s #%d\n", hlp->id_name, srv->index + 1);
+ srv->flags.busy = 0;
+ if (srv->queue.head) {
+ srv->flags.reserved = S_HELPER_DEFERRED;
+ helperStatefulServerKickQueue(srv);
+ } else {
+ srv->flags.reserved = S_HELPER_FREE;
+ if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+ srv->parent->OnEmptyQueue(srv->data);
+ helperStatefulKickQueue(hlp);
+ }
+}
+
+void
+helperStatefulReleaseServer(helper_stateful_server * srv)
+/*decrease the number of 'waiting' clients that set the helper to be DEFERRED */
+{
+ if (srv->deferred_requests > 0)
+ srv->deferred_requests--;
+ if (!(srv->deferred_requests) && (srv->flags.reserved == S_HELPER_DEFERRED) && !(srv->queue.head)) {
+ srv->flags.reserved = S_HELPER_FREE;
+ if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+ srv->parent->OnEmptyQueue(srv->data);
+ }
+}
+
+void *
+helperStatefulServerGetData(helper_stateful_server * srv)
+/* return a pointer to the stateful routines data area */
+{
+ return srv->data;
}
void
storeAppendPrintf(sentry, " S = SHUTDOWN\n");
}
+void
+helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp)
+{
+ helper_stateful_server *srv;
+ dlink_node *link;
+ double tt;
+ storeAppendPrintf(sentry, "number running: %d of %d\n",
+ hlp->n_running, hlp->n_to_start);
+ storeAppendPrintf(sentry, "requests sent: %d\n",
+ hlp->stats.requests);
+ storeAppendPrintf(sentry, "replies received: %d\n",
+ hlp->stats.replies);
+ storeAppendPrintf(sentry, "queue length: %d\n",
+ hlp->stats.queue_size);
+ storeAppendPrintf(sentry, "avg service time: %d msec\n",
+ hlp->stats.avg_svc_time);
+ storeAppendPrintf(sentry, "\n");
+ storeAppendPrintf(sentry, "%7s\t%7s\t%11s\t%s\t%7s\t%7s\t%7s\n",
+ "#",
+ "FD",
+ "# Requests",
+ "# Deferred Requests",
+ "Flags",
+ "Time",
+ "Offset",
+ "Request");
+ for (link = hlp->servers.head; link; link = link->next) {
+ srv = link->data;
+ tt = 0.001 * tvSubMsec(srv->dispatch_time, current_time);
+ storeAppendPrintf(sentry, "%7d\t%7d\t%11d\t%11d\t%c%c%c%c%c\t%7.3f\t%7d\t%s\n",
+ srv->index + 1,
+ srv->rfd,
+ srv->stats.uses,
+ srv->deferred_requests,
+ srv->flags.alive ? 'A' : ' ',
+ srv->flags.busy ? 'B' : ' ',
+ srv->flags.closing ? 'C' : ' ',
+ srv->flags.reserved != S_HELPER_FREE ? 'R' : ' ',
+ srv->flags.shutdown ? 'S' : ' ',
+ tt < 0.0 ? 0.0 : tt,
+ (int) srv->offset,
+ srv->request ? log_quote(srv->request->buf) : "(none)");
+ }
+ storeAppendPrintf(sentry, "\nFlags key:\n\n");
+ storeAppendPrintf(sentry, " A = ALIVE\n");
+ storeAppendPrintf(sentry, " B = BUSY\n");
+ storeAppendPrintf(sentry, " C = CLOSING\n");
+ storeAppendPrintf(sentry, " R = RESERVED or DEFERRED\n");
+ storeAppendPrintf(sentry, " S = SHUTDOWN\n");
+}
+
void
helperShutdown(helper * hlp)
{
}
}
+void
+helperStatefulShutdown(statefulhelper * hlp)
+{
+ dlink_node *link = hlp->servers.head;
+ helper_stateful_server *srv;
+ while (link) {
+ srv = link->data;
+ link = link->next;
+ if (!srv->flags.alive) {
+ debug(34, 3) ("helperStatefulShutdown: %s #%d is NOT ALIVE.\n",
+ hlp->id_name, srv->index + 1);
+ continue;
+ }
+ srv->flags.shutdown = 1; /* request it to shut itself down */
+ if (srv->flags.busy) {
+ debug(34, 3) ("helperStatefulShutdown: %s #%d is BUSY.\n",
+ hlp->id_name, srv->index + 1);
+ continue;
+ }
+ if (srv->flags.closing) {
+ debug(34, 3) ("helperStatefulShutdown: %s #%d is CLOSING.\n",
+ hlp->id_name, srv->index + 1);
+ continue;
+ }
+ if (srv->flags.reserved != S_HELPER_FREE) {
+ debug(34, 3) ("helperStatefulShutdown: %s #%d is RESERVED.\n",
+ hlp->id_name, srv->index + 1);
+ continue;
+ }
+ if (srv->deferred_requests) {
+ debug(34, 3) ("helperStatefulShutdown: %s #%d has DEFERRED requests.\n",
+ hlp->id_name, srv->index + 1);
+ continue;
+ }
+ srv->flags.closing = 1;
+ comm_close(srv->wfd);
+ srv->wfd = -1;
+ }
+}
+
+
helper *
helperCreate(const char *name)
{
return hlp;
}
+statefulhelper *
+helperStatefulCreate(const char *name)
+{
+ statefulhelper *hlp;
+ hlp = CBDATA_ALLOC(statefulhelper, NULL);
+ hlp->id_name = name;
+ return hlp;
+}
+
+
void
helperFree(helper * hlp)
{
cbdataFree(hlp);
}
+void
+helperStatefulFree(statefulhelper * hlp)
+{
+ /* note, don't free hlp->name, it probably points to static memory */
+ if (hlp->queue.head)
+ debug(29, 0) ("WARNING: freeing %s helper with %d requests queued\n",
+ hlp->id_name, hlp->stats.queue_size);
+ cbdataFree(hlp);
+}
+
+
/* ====================================================================== */
/* LOCAL FUNCTIONS */
/* ====================================================================== */
cbdataFree(srv);
}
+static void
+helperStatefulServerFree(int fd, void *data)
+{
+ helper_stateful_server *srv = data;
+ statefulhelper *hlp = srv->parent;
+ helper_stateful_request *r;
+ assert(srv->rfd == fd);
+ if (srv->buf) {
+ memFree(srv->buf, MEM_8K_BUF);
+ srv->buf = NULL;
+ }
+ if ((r = srv->request)) {
+ if (cbdataValid(r->data))
+ r->callback(r->data, srv, srv->buf);
+ helperStatefulRequestFree(r);
+ srv->request = NULL;
+ }
+ if (srv->wfd != srv->rfd && srv->wfd != -1)
+ comm_close(srv->wfd);
+ dlinkDelete(&srv->link, &hlp->servers);
+ hlp->n_running--;
+ assert(hlp->n_running >= 0);
+ if (!srv->flags.shutdown) {
+ debug(34, 0) ("WARNING: %s #%d (FD %d) exited\n",
+ hlp->id_name, srv->index + 1, fd);
+ if (hlp->n_running < hlp->n_to_start / 2)
+ fatalf("Too few %s processes are running", hlp->id_name);
+ }
+ if (srv->data != NULL)
+ memPoolFree(hlp->datapool, srv->data);
+ cbdataUnlock(srv->parent);
+ cbdataFree(srv);
+}
+
+
static void
helperHandleRead(int fd, void *data)
{
}
}
+static void
+helperStatefulHandleRead(int fd, void *data)
+{
+ int len;
+ char *t = NULL;
+ helper_stateful_server *srv = data;
+ helper_stateful_request *r;
+ statefulhelper *hlp = srv->parent;
+ assert(fd == srv->rfd);
+ assert(cbdataValid(data));
+ statCounter.syscalls.sock.reads++;
+ len = read(fd, srv->buf + srv->offset, srv->buf_sz - srv->offset);
+ fd_bytes(fd, len, FD_READ);
+ debug(29, 5) ("helperStatefulHandleRead: %d bytes from %s #%d.\n",
+ len, hlp->id_name, srv->index + 1);
+ if (len <= 0) {
+ if (len < 0)
+ debug(50, 1) ("helperStatefulHandleRead: FD %d read: %s\n", fd, xstrerror());
+ comm_close(fd);
+ return;
+ }
+ srv->offset += len;
+ srv->buf[srv->offset] = '\0';
+ r = srv->request;
+ if (r == NULL) {
+ /* someone spoke without being spoken to */
+ debug(29, 1) ("helperStatefulHandleRead: unexpected read from %s #%d, %d bytes\n",
+ hlp->id_name, srv->index + 1, len);
+ srv->offset = 0;
+ } else if ((t = strchr(srv->buf, '\n'))) {
+ /* end of reply found */
+ debug(29, 3) ("helperStatefulHandleRead: end of reply found\n");
+ *t = '\0';
+ if (cbdataValid(r->data)) {
+ switch ((r->callback(r->data, srv, srv->buf))) { /*if non-zero reserve helper */
+ case S_HELPER_UNKNOWN:
+ fatal("helperStatefulHandleRead: either a non-state aware callback was give to the stateful helper routines, or an uninitialised callback response was recieved.\n");
+ break;
+ case S_HELPER_RELEASE: /* helper finished with */
+ if (!srv->queue.head) {
+ srv->flags.reserved = S_HELPER_FREE;
+ if ((srv->parent->OnEmptyQueue != NULL) && (srv->data))
+ srv->parent->OnEmptyQueue(srv->data);
+ debug(29, 5) ("StatefulHandleRead: releasing %s #%d\n", hlp->id_name, srv->index + 1);
+ } else {
+ srv->flags.reserved = S_HELPER_DEFERRED;
+ debug(29, 5) ("StatefulHandleRead: outstanding deferred requests on %s #%d. reserving for deferred requests.\n", hlp->id_name, srv->index + 1);
+ }
+ break;
+ case S_HELPER_RESERVE: /* 'pin' this helper for the caller */
+ if (!srv->queue.head) {
+ srv->flags.reserved = S_HELPER_RESERVED;
+ debug(29, 5) ("StatefulHandleRead: reserving %s #%d\n", hlp->id_name, srv->index + 1);
+ } else {
+ fatal("StatefulHandleRead: Callback routine attempted to reserve a stateful helper with deferred requests. This can lead to deadlock.\n");
+ }
+ break;
+ case S_HELPER_DEFER:
+ /* the helper is still needed, but can
+ * be used for other requests in the meantime.
+ */
+ srv->flags.reserved = S_HELPER_DEFERRED;
+ srv->deferred_requests++;
+ debug(29, 5) ("StatefulHandleRead: reserving %s #%d for deferred requests.\n", hlp->id_name, srv->index + 1);
+ break;
+ default:
+ fatal("helperStatefulHandleRead: unknown stateful helper callback result.\n");
+ }
+
+ } else {
+ debug(29, 1) ("StatefulHandleRead: no callback data registered\n");
+ }
+ srv->flags.busy = 0;
+ srv->offset = 0;
+ helperStatefulRequestFree(r);
+ srv->request = NULL;
+ hlp->stats.replies++;
+ hlp->stats.avg_svc_time =
+ intAverage(hlp->stats.avg_svc_time,
+ tvSubMsec(srv->dispatch_time, current_time),
+ hlp->stats.replies, REDIRECT_AV_FACTOR);
+ if (srv->flags.shutdown) {
+ comm_close(srv->wfd);
+ srv->wfd = -1;
+ } else {
+ if (srv->queue.head)
+ helperStatefulServerKickQueue(srv);
+ else
+ helperStatefulKickQueue(hlp);
+ }
+ } else {
+ commSetSelect(srv->rfd, COMM_SELECT_READ, helperStatefulHandleRead, srv, 0);
+ }
+}
+
static void
Enqueue(helper * hlp, helper_request * r)
{
debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
}
+static void
+StatefulEnqueue(statefulhelper * hlp, helper_stateful_request * r)
+{
+ dlink_node *link = memAllocate(MEM_DLINK_NODE);
+ dlinkAddTail(r, link, &hlp->queue);
+ hlp->stats.queue_size++;
+ if (hlp->stats.queue_size < hlp->n_running)
+ return;
+ if (squid_curtime - hlp->last_queue_warn < 600)
+ return;
+ if (shutting_down || reconfiguring)
+ return;
+ hlp->last_queue_warn = squid_curtime;
+ debug(14, 0) ("WARNING: All %s processes are busy.\n", hlp->id_name);
+ debug(14, 0) ("WARNING: %d pending requests queued\n", hlp->stats.queue_size);
+ if (hlp->stats.queue_size > hlp->n_running * 2)
+ fatalf("Too many queued %s requests", hlp->id_name);
+ debug(14, 1) ("Consider increasing the number of %s processes in your config file.\n", hlp->id_name);
+}
+
+static void
+StatefulServerEnqueue(helper_stateful_server * srv, helper_stateful_request * r)
+{
+ dlink_node *link = memAllocate(MEM_DLINK_NODE);
+ dlinkAddTail(r, link, &srv->queue);
+ /* XXX No queue length check here? */
+}
+
+
static helper_request *
Dequeue(helper * hlp)
{
return r;
}
+static helper_stateful_request *
+StatefulServerDequeue(helper_stateful_server * srv)
+{
+ dlink_node *link;
+ helper_stateful_request *r = NULL;
+ if ((link = srv->queue.head)) {
+ r = link->data;
+ dlinkDelete(link, &srv->queue);
+ memFree(link, MEM_DLINK_NODE);
+ }
+ return r;
+}
+
+static helper_stateful_request *
+StatefulDequeue(statefulhelper * hlp)
+{
+ dlink_node *link;
+ helper_stateful_request *r = NULL;
+ if ((link = hlp->queue.head)) {
+ r = link->data;
+ dlinkDelete(link, &hlp->queue);
+ memFree(link, MEM_DLINK_NODE);
+ hlp->stats.queue_size--;
+ }
+ return r;
+}
+
static helper_server *
GetFirstAvailable(helper * hlp)
{
return NULL;
}
+static helper_stateful_server *
+StatefulGetFirstAvailable(statefulhelper * hlp)
+{
+ dlink_node *n;
+ helper_stateful_server *srv = NULL;
+ debug(29, 5) ("StatefulGetFirstAvailable: Running servers %d.\n", hlp->n_running);
+ if (hlp->n_running == 0)
+ return NULL;
+ for (n = hlp->servers.head; n != NULL; n = n->next) {
+ srv = n->data;
+ if (srv->flags.busy)
+ continue;
+ if (srv->flags.reserved == S_HELPER_RESERVED)
+ continue;
+ if (!srv->flags.alive)
+ continue;
+ if ((hlp->IsAvailable != NULL) && (srv->data != NULL) && !(hlp->IsAvailable(srv->data)))
+ continue;
+ return srv;
+ }
+ debug(29, 5) ("StatefulGetFirstAvailable: None available.\n");
+ return NULL;
+}
+
+
static void
helperDispatch(helper_server * srv, helper_request * r)
{
hlp->stats.requests++;
}
+static void
+helperStatefulDispatch(helper_stateful_server * srv, helper_stateful_request * r)
+{
+ statefulhelper *hlp = srv->parent;
+ if (!cbdataValid(r->data)) {
+ debug(29, 1) ("helperStatefulDispatch: invalid callback data\n");
+ helperStatefulRequestFree(r);
+ return;
+ }
+ debug(29, 9) ("helperStatefulDispatch busying helper %s #%d\n", hlp->id_name, srv->index + 1);
+ if (r->placeholder == 1) {
+ /* a callback is needed before this request can _use_ a helper. */
+ if (cbdataValid(r->data)) {
+ /* we don't care about releasing/deferring this helper. The request NEVER
+ * gets to the helper. So we throw away the return code */
+ r->callback(r->data, srv, NULL);
+ /* throw away the placeholder */
+ helperStatefulRequestFree(r);
+ /* and push the queue. Note that the callback may have call submit again -
+ * which is why we test for the request*/
+ if (srv->request == NULL) {
+ if (srv->flags.shutdown) {
+ comm_close(srv->wfd);
+ srv->wfd = -1;
+ } else {
+ if (srv->queue.head)
+ helperStatefulServerKickQueue(srv);
+ else
+ helperStatefulKickQueue(hlp);
+ }
+ }
+ }
+ return;
+ }
+ srv->flags.busy = 1;
+ srv->request = r;
+ srv->dispatch_time = current_time;
+ comm_write(srv->wfd,
+ r->buf,
+ strlen(r->buf),
+ NULL, /* Handler */
+ NULL, /* Handler-data */
+ NULL); /* free */
+ commSetSelect(srv->rfd,
+ COMM_SELECT_READ,
+ helperStatefulHandleRead,
+ srv, 0);
+ debug(29, 5) ("helperStatefulDispatch: Request sent to %s #%d, %d bytes\n",
+ hlp->id_name, srv->index + 1, strlen(r->buf));
+ srv->stats.uses++;
+ hlp->stats.requests++;
+}
+
+
static void
helperKickQueue(helper * hlp)
{
helperDispatch(srv, r);
}
+static void
+helperStatefulKickQueue(statefulhelper * hlp)
+{
+ helper_stateful_request *r;
+ helper_stateful_server *srv;
+ while ((srv = StatefulGetFirstAvailable(hlp)) && (r = StatefulDequeue(hlp)))
+ helperStatefulDispatch(srv, r);
+}
+
+static void
+helperStatefulServerKickQueue(helper_stateful_server * srv)
+{
+ helper_stateful_request *r;
+ if ((r = StatefulServerDequeue(srv)))
+ helperStatefulDispatch(srv, r);
+}
+
static void
helperRequestFree(helper_request * r)
{
xfree(r->buf);
memFree(r, MEM_HELPER_REQUEST);
}
+
+static void
+helperStatefulRequestFree(helper_stateful_request * r)
+{
+ cbdataUnlock(r->data);
+ xfree(r->buf);
+ memFree(r, MEM_HELPER_STATEFUL_REQUEST);
+}
/*
- * $Id: http.cc,v 1.374 2001/01/05 09:51:38 adrian Exp $
+ * $Id: http.cc,v 1.375 2001/01/07 23:36:39 hno Exp $
*
* DEBUG: section 11 Hypertext Transfer Protocol (HTTP)
* AUTHOR: Harvest Derived
static CWCB httpSendComplete;
static CWCB httpSendRequestEntry;
-static CWCB httpSendRequestEntryDone;
static PF httpReadReply;
static void httpSendRequest(HttpStateData *);
}
switch (e->id) {
case HDR_PROXY_AUTHORIZATION:
- /* If we're not doing proxy auth, then it must be passed on */
- if (!request->flags.used_proxy_auth)
+ /* Only pass on proxy authentication to peers for which
+ * authentication forwarding is explicitly enabled
+ */
+ if (request->flags.proxying && orig_request->peer_login &&
+ strcmp(orig_request->peer_login, "PASS") == 0) {
httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
+ }
break;
case HDR_AUTHORIZATION:
- /* If we're not doing www auth, then it must be passed on */
- if (!request->flags.accelerated || !request->flags.used_proxy_auth)
- httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
- else
- request->flags.auth = 0; /* We have used the authentication */
+ /* Pass on WWW authentication even if used locally. If this is
+ * not wanted in an accelerator then the header can be removed
+ * using the anonymization functions
+ */
+ httpHeaderAddEntry(hdr_out, httpHeaderEntryClone(e));
+ /* XXX Some accelerators might want to strip the header
+ * and regard the reply as cacheable, but authentication
+ * is not normally enabled for accelerators without reading
+ * the code, so there is not much use in adding logics here
+ * without first defining the concept of having authentication
+ * in the accelerator...
+ */
break;
case HDR_HOST:
/*
}
/* append Proxy-Authorization if configured for peer, and proxying */
if (!httpHeaderHas(hdr_out, HDR_PROXY_AUTHORIZATION)) {
- if (request->flags.proxying && orig_request->peer_login) {
+ if (request->flags.proxying && orig_request->peer_login &&
+ strcmp(orig_request->peer_login, "PASS") != 0) {
httpHeaderPutStrf(hdr_out, HDR_PROXY_AUTHORIZATION, "Basic %s",
base64_encode(orig_request->peer_login));
}
debug(11, 5) ("httpSendRequest: FD %d: httpState %p.\n", httpState->fd, httpState);
- if (httpState->orig_request->content_length > 0)
+ if (httpState->orig_request->body_connection)
sendHeaderDone = httpSendRequestEntry;
else
sendHeaderDone = httpSendComplete;
assert(-1 == cfd || FD_SOCKET == fd_table[cfd].type);
if (p != NULL)
httpState->flags.proxying = 1;
+ else
+ httpState->flags.proxying = 0;
/*
* Is keep-alive okay for all request methods?
*/
}
static void
-httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
+httpSendRequestEntryDone(int fd, void *data)
{
HttpStateData *httpState = data;
- StoreEntry *entry = httpState->entry;
- ErrorState *err;
- debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
- fd, size, errflag);
- if (size > 0) {
- fd_bytes(fd, size, FD_WRITE);
- kb_incr(&statCounter.server.all.kbytes_out, size);
- kb_incr(&statCounter.server.http.kbytes_out, size);
- }
- if (errflag == COMM_ERR_CLOSING)
- return;
- if (errflag) {
- err = errorCon(ERR_WRITE_ERROR, HTTP_INTERNAL_SERVER_ERROR);
- err->xerrno = errno;
- err->request = requestLink(httpState->orig_request);
- errorAppendEntry(entry, err);
- comm_close(fd);
- return;
+ aclCheck_t ch;
+ debug(11, 5) ("httpSendRequestEntryDone: FD %d\n",
+ fd);
+ memset(&ch, '\0', sizeof(ch));
+ ch.request = httpState->request;
+ if (!Config.accessList.brokenPosts) {
+ debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n");
+ httpSendComplete(fd, NULL, 0, 0, data);
+ } else if (!aclCheckFast(Config.accessList.brokenPosts, &ch)) {
+ debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n");
+ httpSendComplete(fd, NULL, 0, 0, data);
+ } else {
+ debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n");
+ comm_write(fd, "\r\n", 2, httpSendComplete, data, NULL);
}
- if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
- comm_close(fd);
- return;
+}
+
+static void
+httpRequestBodyHandler(char *buf, size_t size, void *data)
+{
+ HttpStateData *httpState = (HttpStateData *) data;
+ if (size > 0) {
+ comm_write(httpState->fd, buf, size, httpSendRequestEntry, data, memFree8K);
+ } else if (size == 0) {
+ /* End of body */
+ memFree8K(buf);
+ httpSendRequestEntryDone(httpState->fd, data);
+ } else {
+ /* Failed to get whole body, probably aborted */
+ memFree8K(buf);
+ httpSendComplete(httpState->fd, NULL, 0, COMM_ERR_CLOSING, data);
}
- pumpStart(fd, httpState->fwd, httpSendRequestEntryDone, httpState);
}
static void
-httpSendRequestEntryDone(int fd, char *bufnotused, size_t size, int errflag, void *data)
+httpSendRequestEntry(int fd, char *bufnotused, size_t size, int errflag, void *data)
{
HttpStateData *httpState = data;
StoreEntry *entry = httpState->entry;
ErrorState *err;
- aclCheck_t ch;
- debug(11, 5) ("httpSendRequestEntryDone: FD %d: size %d: errflag %d.\n",
+ debug(11, 5) ("httpSendRequestEntry: FD %d: size %d: errflag %d.\n",
fd, size, errflag);
if (size > 0) {
fd_bytes(fd, size, FD_WRITE);
comm_close(fd);
return;
}
- memset(&ch, '\0', sizeof(ch));
- ch.request = httpState->request;
- if (!Config.accessList.brokenPosts) {
- debug(11, 5) ("httpSendRequestEntryDone: No brokenPosts list\n");
- httpSendComplete(fd, NULL, 0, 0, data);
- } else if (!aclCheckFast(Config.accessList.brokenPosts, &ch)) {
- debug(11, 5) ("httpSendRequestEntryDone: didn't match brokenPosts\n");
- httpSendComplete(fd, NULL, 0, 0, data);
- } else {
- debug(11, 2) ("httpSendRequestEntryDone: matched brokenPosts\n");
- comm_write(fd, "\r\n", 2, httpSendComplete, data, NULL);
+ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+ comm_close(fd);
+ return;
}
+ clientReadBody(httpState->orig_request, memAllocate(MEM_8K_BUF), 8192, httpRequestBodyHandler, httpState);
}
void
/*
- * $Id: main.cc,v 1.328 2001/01/05 09:51:39 adrian Exp $
+ * $Id: main.cc,v 1.329 2001/01/07 23:36:39 hno Exp $
*
* DEBUG: section 1 Startup and Main Loop
* AUTHOR: Harvest Derived
parseConfigFile(ConfigFile);
_db_init(Config.Log.log, Config.debugOptions);
ipcache_restart(); /* clear stuck entries */
+ authenticateUserCacheRestart(); /* clear stuck ACL entries */
fqdncache_restart(); /* sigh, fqdncache too */
parseEtcHosts();
errorInitialize(); /* reload error pages */
idnsInit();
#endif
redirectInit();
- authenticateInit();
+ authenticateInit(&Config.authConfig);
#if USE_WCCP
wccpInit();
#endif
dnsInit();
#endif
redirectInit();
- authenticateInit();
+ authenticateInit(&Config.authConfig);
}
static void
idnsInit();
#endif
redirectInit();
- authenticateInit();
+ authenticateInit(&Config.authConfig);
useragentOpenLog();
refererOpenLog();
httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */
cbdataInit();
eventInit(); /* eventInit() is required for config parsing */
storeFsInit(); /* required for config parsing */
+ authenticateSchemeInit(); /* required for config parsign */
parse_err = parseConfigFile(ConfigFile);
if (opt_parse_cfg_only)
/*
- * $Id: mem.cc,v 1.52 2001/01/07 20:11:18 hno Exp $
+ * $Id: mem.cc,v 1.53 2001/01/07 23:36:39 hno Exp $
*
* DEBUG: section 13 High Level Memory Pool Management
* AUTHOR: Harvest Derived
memDataInit(MEM_ACL_LIST, "acl_list", sizeof(acl_list), 0);
memDataInit(MEM_ACL_NAME_LIST, "acl_name_list", sizeof(acl_name_list), 0);
memDataInit(MEM_ACL_TIME_DATA, "acl_time_data", sizeof(acl_time_data), 0);
+ memDataInit(MEM_AUTH_USER_T, "auth_user_t",
+ sizeof(auth_user_t), 0);
+ memDataInit(MEM_AUTH_USER_HASH, "auth_user_hash_pointer",
+ sizeof(auth_user_hash_pointer), 0);
+ memDataInit(MEM_ACL_PROXY_AUTH_MATCH, "acl_proxy_auth_match_cache",
+ sizeof(acl_proxy_auth_match_cache), 0);
memDataInit(MEM_ACL_USER_DATA, "acl_user_data",
sizeof(acl_user_data), 0);
- memDataInit(MEM_ACL_PROXY_AUTH_USER, "acl_proxy_auth_user",
- sizeof(acl_proxy_auth_user), 0);
memDataInit(MEM_CACHEMGR_PASSWD, "cachemgr_passwd",
sizeof(cachemgr_passwd), 0);
#if USE_CACHE_DIGESTS
memDataInit(MEM_CLIENT_INFO, "ClientInfo", sizeof(ClientInfo), 0);
memDataInit(MEM_MD5_DIGEST, "MD5 digest", MD5_DIGEST_CHARS, 0);
memDataInit(MEM_HELPER, "helper", sizeof(helper), 0);
+ memDataInit(MEM_HELPER_STATEFUL, "stateful_helper", sizeof(statefulhelper), 0);
memDataInit(MEM_HELPER_REQUEST, "helper_request",
sizeof(helper_request), 0);
+ memDataInit(MEM_HELPER_STATEFUL_REQUEST, "helper_stateful_request",
+ sizeof(helper_stateful_request), 0);
memDataInit(MEM_HELPER_SERVER, "helper_server",
sizeof(helper_server), 0);
+ memDataInit(MEM_HELPER_STATEFUL_SERVER, "helper_stateful_server",
+ sizeof(helper_stateful_server), 0);
memDataInit(MEM_STORE_IO, "storeIOState", sizeof(storeIOState), 0);
memDataInit(MEM_TLV, "storeSwapTLV", sizeof(tlv), 0);
memDataInit(MEM_GEN_CBDATA, "generic_cbdata", sizeof(generic_cbdata), 0);
/*
- * $Id: protos.h,v 1.392 2001/01/05 09:51:39 adrian Exp $
+ * $Id: protos.h,v 1.393 2001/01/07 23:36:39 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
#endif
char *log_quote(const char *header);
+/* acl.c */
extern aclCheck_t *aclChecklistCreate(const struct _acl_access *,
request_t *,
const char *ident);
extern const char *aclTypeToStr(squid_acl);
extern wordlist *aclDumpGeneric(const acl *);
extern int aclPurgeMethodInUse(acl_access *);
+extern void aclCacheMatchFlush(dlink_list * cache);
/*
* cache_cf.c
extern void self_destruct(void);
extern int GetInteger(void);
+/* extra functions from cache_cf.c useful for lib modules */
+extern void parse_int(int *var);
+extern void parse_eol(char *volatile *var);
+extern void parse_wordlist(wordlist ** list);
+extern void requirePathnameExists(const char *name, const char *path);
+extern void parse_time_t(time_t * var);
/*
* cbdata.c
extern void clientHttpConnectionsClose(void);
extern StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags);
extern int isTcpHit(log_type);
+extern void clientReadBody(request_t * req, char *buf, size_t size, CBCB * callback, void *data);
+extern int clientAbortBody(request_t * req);
extern int commSetNonBlocking(int fd);
extern int commUnsetNonBlocking(int fd);
extern void redirectInit(void);
extern void redirectShutdown(void);
-extern void authenticateStart(acl_proxy_auth_user *, RH *, void *);
-extern void authenticateInit(void);
+/* auth_modules.c */
+extern void authSchemeSetup(void);
+
+/* authenticate.c */
+extern void authenticateAuthUserMerge(auth_user_t *, auth_user_t *);
+extern auth_user_t *authenticateAuthUserNew(const char *);
+extern int authenticateAuthSchemeId(const char *typestr);
+extern void authenticateStart(auth_user_request_t *, RH *, void *);
+extern void authenticateSchemeInit(void);
+extern void authenticateInit(authConfig *);
extern void authenticateShutdown(void);
+extern void authenticateFixHeader(HttpReply *, auth_user_request_t *, request_t *, int);
+extern void authenticateAddTrailer(HttpReply *, auth_user_request_t *, request_t *, int);
+extern auth_user_request_t *authenticateGetAuthUser(const char *proxy_auth);
+extern void authenticateAuthenticateUser(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type);
+extern void authenticateAuthUserUnlock(auth_user_t * auth_user);
+extern void authenticateAuthUserLock(auth_user_t * auth_user);
+extern void authenticateAuthUserRequestUnlock(auth_user_request_t *);
+extern void authenticateAuthUserRequestLock(auth_user_request_t *);
+extern char *authenticateAuthUserRequestMessage(auth_user_request_t *);
+extern int authenticateAuthUserInuse(auth_user_t * auth_user);
+extern void authenticateAuthUserRequestSetIp(auth_user_request_t *, struct in_addr);
+extern int authenticateDirection(auth_user_request_t *);
+extern FREE authenticateFreeProxyAuthUser;
+extern void authenticateFreeProxyAuthUserACLResults(void *data);
+extern void authenticateProxyUserCacheCleanup(void *);
+extern void authenticateInitUserCache();
+#if 0
+extern void authenticateProxyAuthCacheAddLink(const char *key, auth_user_t *);
+#endif
+extern int authenticateActiveSchemeCount();
+extern int authenticateSchemeCount();
+extern void authenticateUserNameCacheAdd(auth_user_t * auth_user);
+extern int authenticateCheckAuthUserIP(struct in_addr request_src_addr, auth_user_request_t * auth_user);
+extern int authenticateUserAuthenticated(auth_user_request_t *);
+extern void authenticateUserCacheRestart();
+extern char *authenticateUserUsername(auth_user_t *);
+extern char *authenticateUserRequestUsername(auth_user_request_t *);
+extern int authenticateValidateUser(auth_user_request_t *);
+extern void authenticateOnCloseConnection(ConnStateData * conn);
+extern void authSchemeAdd(char *type, AUTHSSETUP * setup);
extern void refreshAddToList(const char *, int, time_t, int, time_t);
extern int refreshIsCachable(const StoreEntry *);
extern void PrintRusage(void);
extern void dumpMallocStats(void);
-extern void pumpInit(int fd, request_t * r, char *uri);
-extern void pumpStart(int, FwdState *, CWCB * callback, void *);
-extern int pumpMethod(method_t method);
-extern int pumpRestart(request_t *);
-
#if USE_UNLINKD
extern void unlinkdInit(void);
extern void unlinkdClose(void);
extern void dlinkAdd(void *data, dlink_node *, dlink_list *);
extern void dlinkAddTail(void *data, dlink_node *, dlink_list *);
extern void dlinkDelete(dlink_node * m, dlink_list * list);
+extern void dlinkNodeDelete(dlink_node * m);
+extern dlink_node *dlinkNodeNew();
+
extern void kb_incr(kb_t *, size_t);
extern double gb_to_double(const gb_t *);
extern const char *gb_to_str(const gb_t *);
/* helper.c */
extern void helperOpenServers(helper * hlp);
+extern void helperStatefulOpenServers(statefulhelper * hlp);
extern void helperSubmit(helper * hlp, const char *buf, HLPCB * callback, void *data);
+extern void helperStatefulSubmit(statefulhelper * hlp, const char *buf, HLPSCB * callback, void *data, helper_stateful_server * lastserver);
extern void helperStats(StoreEntry * sentry, helper * hlp);
+extern void helperStatefulStats(StoreEntry * sentry, statefulhelper * hlp);
extern void helperShutdown(helper * hlp);
+extern void helperStatefulShutdown(statefulhelper * hlp);
extern helper *helperCreate(const char *);
+extern statefulhelper *helperStatefulCreate(const char *);
extern void helperFree(helper *);
+extern void helperStatefulFree(statefulhelper *);
+extern void helperStatefulReset(helper_stateful_server * srv);
+extern void helperStatefulReleaseServer(helper_stateful_server * srv);
+extern void *helperStatefulServerGetData(helper_stateful_server * srv);
+extern helper_stateful_server *helperStatefulDefer(statefulhelper *);
+
+
#if USE_LEAKFINDER
extern void leakInit(void);
/*
- * $Id: redirect.cc,v 1.85 2001/01/05 09:51:40 adrian Exp $
+ * $Id: redirect.cc,v 1.86 2001/01/07 23:36:40 hno Exp $
*
* DEBUG: section 29 Redirector
* AUTHOR: Duane Wessels
r = CBDATA_ALLOC(redirectStateData, NULL);
r->orig_url = xstrdup(http->uri);
r->client_addr = conn->log_addr;
- if (http->request->user_ident[0])
- r->client_ident = http->request->user_ident;
- else if (conn->ident == NULL || *conn->ident == '\0') {
- r->client_ident = dash_str;
+ if (http->request->auth_user_request)
+ r->client_ident = authenticateUserRequestUsername(http->request->auth_user_request);
+ else if (conn->rfc931[0]) {
+ r->client_ident = conn->rfc931;
} else {
- r->client_ident = conn->ident;
+ r->client_ident = dash_str;
}
r->method_s = RequestMethodStr[http->request->method];
r->handler = handler;
/*
- * $Id: ssl.cc,v 1.109 2001/01/05 09:51:40 adrian Exp $
+ * $Id: ssl.cc,v 1.110 2001/01/07 23:36:40 hno Exp $
*
* DEBUG: section 26 Secure Sockets Layer Proxy
* AUTHOR: Duane Wessels
sslState->request->peer_login = fs->peer->login;
sslState->request->flags.proxying = 1;
} else {
+ sslState->request->peer_login = NULL;
sslState->request->flags.proxying = 0;
}
#if DELAY_POOLS
/*
- * $Id: stat.cc,v 1.341 2001/01/05 09:51:40 adrian Exp $
+ * $Id: stat.cc,v 1.342 2001/01/07 23:36:40 hno Exp $
*
* DEBUG: section 18 Cache Manager Statistics
* AUTHOR: Harvest Derived
ntohs(conn->me.sin_port));
storeAppendPrintf(s, "\tnrequests: %d\n",
conn->nrequests);
- storeAppendPrintf(s, "\tpersistent: %d\n",
- conn->persistent);
storeAppendPrintf(s, "\tdefer: n %d, until %d\n",
conn->defer.n, conn->defer.until);
}
/*
- * $Id: structs.h,v 1.374 2001/01/07 19:55:20 hno Exp $
+ * $Id: structs.h,v 1.375 2001/01/07 23:36:40 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
acl_name_list *next;
};
-struct _acl_proxy_auth_user {
- hash_link hash; /* must be first */
+struct _acl_proxy_auth_match_cache {
+ dlink_node link;
+ int matchrv;
+ void *acl_data;
+};
+
+struct _auth_user_hash_pointer {
+ /* first two items must be same as hash_link */
+ char *key;
+ auth_user_hash_pointer *next;
+ auth_user_t *auth_user;
+ dlink_node link; /* other hash entries that point to the same auth_user */
+};
+
+struct _auth_user_t {
/* extra fields for proxy_auth */
- char *passwd;
- int passwd_ok; /* 1 = passwd checked OK */
+ /* this determines what scheme owns the user data. */
+ auth_type_t auth_type;
+ /* the index +1 in the authscheme_list to the authscheme entry */
+ int auth_module;
+ /* we only have one username associated with a given auth_user struct */
+ auth_user_hash_pointer *usernamehash;
+ /* we may have many proxy-authenticate strings that decode to the same user */
+ dlink_list proxy_auth_list;
+ dlink_list proxy_match_cache;
+ struct {
+ unsigned int credentials_ok:2; /*0=unchecked,1=ok,2=failed */
+ } flags;
long expiretime;
- struct in_addr ipaddr; /* IP addr this user authenticated from */
+ /* IP addr this user authenticated from */
+ struct in_addr ipaddr;
time_t ip_expiretime;
+ /* how many references are outstanding to this instance */
+ size_t references;
+ /* the auth scheme has it's own private data area */
+ void *scheme_data;
+ /* the auth_user_request structures that link to this. Yes it could be a splaytree
+ * but how many requests will a single username have in parallel? */
+ dlink_list requests;
+};
+
+struct _auth_user_request_t {
+ /* this is the object passed around by client_side and acl functions */
+ /* it has request specific data, and links to user specific data */
+ /* the user */
+ auth_user_t *auth_user;
+ /* return a message on the 407 error pages */
char *message;
+ /* any scheme specific request related data */
+ void *scheme_data;
+ /* how many 'processes' are working on this data */
+ size_t references;
+};
+
+
+/*
+ * This defines an auth scheme module
+ */
+
+struct _authscheme_entry {
+ char *typestr;
+ AUTHSACTIVE *Active;
+ AUTHSADDHEADER *AddHeader;
+ AUTHSADDTRAILER *AddTrailer;
+ AUTHSAUTHED *authenticated;
+ AUTHSAUTHUSER *authAuthenticate;
+ AUTHSDUMP *dump;
+ AUTHSFIXERR *authFixHeader;
+ AUTHSFREE *FreeUser;
+ AUTHSFREECONFIG *freeconfig;
+ AUTHSUSERNAME *authUserUsername;
+ AUTHSONCLOSEC *oncloseconnection; /*optional */
+ AUTHSDECODE *decodeauth;
+ AUTHSDIRECTION *getdirection;
+ AUTHSPARSE *parse;
+ AUTHSINIT *init;
+ AUTHSREQFREE *requestFree;
+ AUTHSSHUTDOWN *donefunc;
+ AUTHSSTART *authStart;
+ AUTHSSTATS *authStats;
+};
+
+/*
+ * This is a configured auth scheme
+ */
+
+/* private data types */
+struct _authScheme {
+ /* pointer to the authscheme_list's string entry */
+ char *typestr;
+ /* the scheme id in the authscheme_list */
+ int Id;
+ /* the scheme's configuration details. */
+ void *scheme_data;
};
struct _acl_deny_info_list {
struct in_addr my_addr;
unsigned short my_port;
request_t *request;
-#if USE_IDENT
- ConnStateData *conn; /* hack for ident */
- char ident[USER_IDENT_SZ];
-#endif
- acl_proxy_auth_user *auth_user;
+ ConnStateData *conn; /* hack for ident and NTLM */
+ char rfc931[USER_IDENT_SZ];
+ auth_user_request_t *auth_user_request;
acl_lookup_state state[ACL_ENUM_MAX];
#if SQUID_SNMP
char *snmp_community;
sockaddr_in_list *next;
};
+
#if DELAY_POOLS
struct _delaySpec {
int restore_bps;
char *dnsserver;
#endif
wordlist *redirect;
- wordlist *authenticate;
#if USE_ICMP
char *pinger;
#endif
int dnsChildren;
#endif
int redirectChildren;
- int authenticateChildren;
+ time_t authenticateGCInterval;
time_t authenticateTTL;
time_t authenticateIpTTL;
struct {
acl_access *redirector;
} accessList;
acl_deny_info_list *denyInfoList;
- char *proxyAuthRealm;
+ struct _authConfig {
+ authScheme *schemes;
+ int n_allocated;
+ int n_configured;
+ } authConfig;
struct {
size_t list_width;
int list_wrap;
size_t size;
log_type code;
int msec;
- const char *ident;
+ const char *rfc931;
+ const char *authuser;
} cache;
struct {
char *request;
off_t offset;
size_t size;
} in;
+ struct {
+ size_t size_left; /* How much body left to process */
+ request_t *request; /* Parameters passed to clientReadBody */
+ char *buf;
+ size_t bufsize;
+ CBCB *callback;
+ void *cbdata;
+ } body;
+ auth_type_t auth_type; /* Is this connection based authentication ? if so
+ * what type it is. */
+ /* note this is ONLY connection based because NTLM is against HTTP spec */
+ /* the user details for connection based authentication */
+ auth_user_request_t *auth_user_request;
clientHttpRequest *chr;
struct sockaddr_in peer;
struct sockaddr_in me;
struct in_addr log_addr;
- char ident[USER_IDENT_SZ];
+ char rfc931[USER_IDENT_SZ];
int nrequests;
- int persistent;
struct {
int n;
time_t until;
unsigned int proxy_keepalive:1;
unsigned int proxying:1;
unsigned int refresh:1;
- unsigned int used_proxy_auth:1;
unsigned int redirected:1;
unsigned int need_validation:1;
#if HTTP_VIOLATIONS
#endif
unsigned int accelerated:1;
unsigned int internal:1;
+ unsigned int body_sent:1;
};
struct _link_list {
protocol_t protocol;
char login[MAX_LOGIN_SZ];
char host[SQUIDHOSTNAMELEN + 1];
- char user_ident[USER_IDENT_SZ]; /* from proxy auth or ident server */
+ auth_user_request_t *auth_user_request;
u_short port;
String urlpath;
char *canonical;
struct in_addr my_addr;
unsigned short my_port;
HttpHeader header;
- char *body;
- size_t body_sz;
+ ConnStateData *body_connection; /* used by clientReadBody() */
int content_length;
HierarchyLogEntry hier;
err_type err_type;
err_type type;
int page_id;
http_status http_status;
+ auth_user_request_t *auth_user_request;
request_t *request;
char *url;
int xerrno;
char *host;
u_short port;
char *dnsserver_msg;
- char *proxy_auth_msg;
time_t ttl;
struct in_addr src_addr;
char *redirect_url;
void *data;
};
+struct _helper_stateful_request {
+ char *buf;
+ HLPSCB *callback;
+ int placeholder; /* if 1, this is a dummy request waiting for a stateful helper
+ * to become available for deferred requests.*/
+ void *data;
+};
+
+
struct _helper {
wordlist *cmdline;
dlink_list servers;
} stats;
};
+struct _helper_stateful {
+ wordlist *cmdline;
+ dlink_list servers;
+ dlink_list queue;
+ const char *id_name;
+ int n_to_start;
+ int n_running;
+ int ipc_type;
+ MemPool *datapool;
+ HLPSAVAIL *IsAvailable;
+ HLPSONEQ *OnEmptyQueue;
+ time_t last_queue_warn;
+ struct {
+ int requests;
+ int replies;
+ int queue_size;
+ int avg_svc_time;
+ } stats;
+};
+
struct _helper_server {
int index;
int rfd;
} stats;
};
+
+struct _helper_stateful_server {
+ int index;
+ int rfd;
+ int wfd;
+ char *buf;
+ size_t buf_sz;
+ off_t offset;
+ struct timeval dispatch_time;
+ struct timeval answer_time;
+ dlink_node link;
+ dlink_list queue;
+ statefulhelper *parent;
+ helper_stateful_request *request;
+ struct _helper_stateful_flags {
+ unsigned int alive:1;
+ unsigned int busy:1;
+ unsigned int closing:1;
+ unsigned int shutdown:1;
+ stateful_helper_reserve_t reserved:2;
+ } flags;
+ struct {
+ int uses;
+ } stats;
+ size_t deferred_requests; /* current number of deferred requests */
+ void *data; /* State data used by the calling routines */
+};
+
/*
* use this when you need to pass callback data to a blocking
* operation, but you don't want to add that pointer to cbdata
/*
- * $Id: tools.cc,v 1.202 2001/01/04 03:42:35 wessels Exp $
+ * $Id: tools.cc,v 1.203 2001/01/07 23:36:40 hno Exp $
*
* DEBUG: section 21 Misc Functions
* AUTHOR: Harvest Derived
extern void (*failure_notify) (const char *);
+MemPool *dlink_node_pool = NULL;
+
void
releaseServerSockets(void)
{
return p ? p : "(NULL)";
}
+dlink_node *
+dlinkNodeNew()
+{
+ if (dlink_node_pool == NULL)
+ dlink_node_pool = memPoolCreate("Dlink list nodes", sizeof(dlink_node));
+ /* where should we call memPoolDestroy(dlink_node_pool); */
+ return memPoolAlloc(dlink_node_pool);
+}
+
+/* the node needs to be unlinked FIRST */
+void
+dlinkNodeDelete(dlink_node * m)
+{
+ if (m == NULL)
+ return;
+ memPoolFree(dlink_node_pool, m);
+}
+
void
dlinkAdd(void *data, dlink_node * m, dlink_list * list)
{
/*
- * $Id: tunnel.cc,v 1.109 2001/01/05 09:51:40 adrian Exp $
+ * $Id: tunnel.cc,v 1.110 2001/01/07 23:36:40 hno Exp $
*
* DEBUG: section 26 Secure Sockets Layer Proxy
* AUTHOR: Duane Wessels
sslState->request->peer_login = fs->peer->login;
sslState->request->flags.proxying = 1;
} else {
+ sslState->request->peer_login = NULL;
sslState->request->flags.proxying = 0;
}
#if DELAY_POOLS
/*
- * $Id: typedefs.h,v 1.115 2001/01/07 19:55:20 hno Exp $
+ * $Id: typedefs.h,v 1.116 2001/01/07 23:36:41 hno Exp $
*
*
* SQUID Internet Object Cache http://squid.nlanr.net/Squid/
typedef struct _acl_time_data acl_time_data;
typedef struct _acl_name_list acl_name_list;
typedef struct _acl_deny_info_list acl_deny_info_list;
+typedef struct _acl_proxy_auth acl_proxy_auth;
+typedef struct _auth_user_t auth_user_t;
+typedef struct _auth_user_request_t auth_user_request_t;
+typedef struct _auth_user_hash_pointer auth_user_hash_pointer;
+typedef struct _acl_proxy_auth_match_cache acl_proxy_auth_match_cache;
+typedef struct _authscheme_entry authscheme_entry_t;
+typedef struct _authScheme authScheme;
typedef struct _acl_user_data acl_user_data;
-typedef struct _acl_proxy_auth_user acl_proxy_auth_user;
typedef struct _acl_arp_data acl_arp_data;
typedef struct _acl acl;
typedef struct _acl_snmp_comm acl_snmp_comm;
typedef struct _icpUdpData icpUdpData;
typedef struct _clientHttpRequest clientHttpRequest;
typedef struct _ConnStateData ConnStateData;
+typedef struct _ConnCloseHelperData ConnCloseHelperData;
typedef struct _ipcache_addrs ipcache_addrs;
typedef struct _domain_ping domain_ping;
typedef struct _domain_type domain_type;
typedef struct _SwapDir SwapDir;
typedef struct _request_flags request_flags;
typedef struct _helper_flags helper_flags;
+typedef struct _helper_stateful_flags helper_stateful_flags;
typedef struct _http_state_flags http_state_flags;
typedef struct _header_mangler header_mangler;
typedef struct _request_t request_t;
typedef struct _StatCounters StatCounters;
typedef struct _tlv tlv;
typedef struct _storeSwapLogData storeSwapLogData;
+typedef struct _authConfig authConfig;
typedef struct _cacheSwap cacheSwap;
typedef struct _StatHist StatHist;
typedef struct _String String;
typedef struct _FwdState FwdState;
typedef struct _FwdServer FwdServer;
typedef struct _helper helper;
+typedef struct _helper_stateful statefulhelper;
typedef struct _helper_server helper_server;
+typedef struct _helper_stateful_server helper_stateful_server;
typedef struct _helper_request helper_request;
+typedef struct _helper_stateful_request helper_stateful_request;
typedef struct _generic_cbdata generic_cbdata;
typedef struct _storeIOState storeIOState;
typedef struct _queued_read queued_read;
typedef void RH(void *data, char *);
typedef void UH(void *data, wordlist *);
typedef int DEFER(int fd, void *data);
+typedef void CBCB(char *buf, size_t size, void *data);
typedef void STIOCB(void *their_data, int errflag, storeIOState *);
typedef void STFNCB(void *their_data, int errflag, storeIOState *);
typedef void SIGHDLR(int sig);
typedef void STVLDCB(void *, int, int);
typedef void HLPCB(void *, char *buf);
+typedef stateful_helper_callback_t HLPSCB(void *, void *lastserver, char *buf);
+typedef int HLPSAVAIL(void *);
+typedef void HLPSONEQ(void *);
typedef void HLPCMDOPTS(int *argc, char **argv);
typedef void IDNSCB(void *, rfc1035_rr *, int);
typedef double hbase_f(double);
typedef void StatHistBinDumper(StoreEntry *, int idx, double val, double size, int count);
+/* authenticate.c authenticate scheme routines typedefs */
+typedef int AUTHSACTIVE();
+typedef int AUTHSAUTHED(auth_user_request_t *);
+typedef void AUTHSAUTHUSER(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type);
+typedef void AUTHSDECODE(auth_user_request_t *, const char *);
+typedef int AUTHSDIRECTION(auth_user_request_t *);
+typedef void AUTHSDUMP(StoreEntry *, const char *, authScheme *);
+typedef void AUTHSFIXERR(auth_user_request_t *, HttpReply *, http_hdr_type, request_t *);
+typedef void AUTHSADDHEADER(auth_user_request_t *, HttpReply *, int);
+typedef void AUTHSADDTRAILER(auth_user_request_t *, HttpReply *, int);
+typedef void AUTHSFREE(auth_user_t *);
+typedef void AUTHSFREECONFIG(authScheme *);
+typedef char *AUTHSUSERNAME(auth_user_t *);
+typedef void AUTHSONCLOSEC(ConnStateData *);
+typedef void AUTHSPARSE(authScheme *, int, char *);
+typedef void AUTHSINIT(authScheme *);
+typedef void AUTHSREQFREE(auth_user_request_t *);
+typedef void AUTHSSETUP(authscheme_entry_t *);
+typedef void AUTHSSHUTDOWN(void);
+typedef void AUTHSSTART(auth_user_request_t *, RH *, void *);
+typedef void AUTHSSTATS(StoreEntry *);
+
/* append/vprintf's for Packer */
typedef void (*append_f) (void *, const char *buf, int size);
#if STDC_HEADERS