From: hno <> Date: Mon, 8 Jan 2001 06:32:04 +0000 (+0000) Subject: Major rewrite of proxy authentication to support other schemes than X-Git-Tag: SQUID_3_0_PRE1~1673 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=94439e4e1d1a662f14357ee1e16c4a9af95db94b;p=thirdparty%2Fsquid.git Major rewrite of proxy authentication to support other schemes than 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 --- diff --git a/ChangeLog b/ChangeLog index 659fe13ed4..7023a4279c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 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) diff --git a/configure.in b/configure.in index 5589768aa3..885b32fda5 100644 --- a/configure.in +++ b/configure.in @@ -3,13 +3,13 @@ dnl Configuration input file for Squid 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) @@ -615,7 +615,6 @@ AC_ARG_ENABLE(leakfinder, ]) AC_SUBST(LEAKFINDER_OBJS) -dnl Disable HTTP violations AC_ARG_ENABLE(ident-lookups, [ --disable-ident-lookups This allows you to remove code that performs @@ -676,32 +675,117 @@ AC_ARG_ENABLE(underscores, 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, @@ -1710,13 +1794,33 @@ for repl in $REPL_POLICIES none; do 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 \ @@ -1729,10 +1833,14 @@ AC_OUTPUT(\ $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 \ ) diff --git a/doc/Programming-Guide/prog-guide.sgml b/doc/Programming-Guide/prog-guide.sgml index 5c824505d1..b5e3ce89b8 100644 --- a/doc/Programming-Guide/prog-guide.sgml +++ b/doc/Programming-Guide/prog-guide.sgml @@ -2,7 +2,7 @@
Squid Programmers Guide Duane Wessels, Squid Developers -$Id: prog-guide.sgml,v 1.33 2001/01/06 11:39:43 hno Exp $ +$Id: prog-guide.sgml,v 1.34 2001/01/07 23:36:35 hno Exp $ Squid is a WWW Cache application developed by the National Laboratory @@ -153,6 +153,14 @@ Squid consists of the following major components information and continues the access control checks when the information is available. +Authentication Framework + +

+ 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. + Network Communication

@@ -2011,6 +2019,343 @@ coupling between the storage layer and the replacement policy.

To be written... + +Authentication Framework + +

+ + Definition of an auth scheme. + Data types + How to add a new auth scheme + How to 'hook in' new functions to the API. + +

+ Definition of an auth scheme + +

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. + +

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. + +

Data types + +

The data types are presented in C for the simple reason that squid is + currently written exclusively in C. + +

Function typedefs. + +

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: + +

typedef int AUTHSACTIVE(); + +

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. + +

typedef void AUTHSSETUP(authscheme_entry_t *); + +

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. + +

typedef void AUTHSSHUTDOWN(void); + +

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. + +

typedef void AUTHSINIT(authScheme *); + +

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. + +

typedef void AUTHSPARSE(authScheme *, int, char *); + +

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. + +

typedef void AUTHSFREECONFIG(authScheme *); + +

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). + +

typedef void AUTHSDUMP(StoreEntry *, const char *, authScheme *); + +

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. + +

typedef void AUTHSSTATS(StoreEntry *); +

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. +

The next set of functions work on the data structures used by the + authentication schemes. + +

typedef void AUTHSREQFREE(auth_user_request_t *); + +

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. + +

typedef char *AUTHSUSERNAME(auth_user_t *); + +

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. + +

typedef int AUTHSAUTHED(auth_user_request_t *); + +

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.

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. + +

typedef void AUTHSAUTHUSER(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type); +

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. + +

typedef void AUTHSDECODE(auth_user_request_t *, const char *); + +

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. + +

typedef int AUTHSDIRECTION(auth_user_request_t *); + +

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. + +

typedef void AUTHSFIXERR(auth_user_request_t *, HttpReply *, http_hdr_type, request_t *); + +

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. + +

typedef void AUTHSFREE(auth_user_t *); + +

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. + +

typedef void AUTHSADDHEADER(auth_user_request_t *, HttpReply *, int); + typedef void AUTHSADDTRAILER(auth_user_request_t *, HttpReply *, int); + +

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. + +

typedef void AUTHSONCLOSEC(ConnStateData *); + +

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. + +

typedef void AUTHSSTART(auth_user_request_t * , RH * , void *); + +

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. + +

Structures + +

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. + +

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 */ + }; + +

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. + +

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; + }; + +

This is a short lived structure is the visible aspect of the + authentication framework. + +

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. + +

+ 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; + }; + +

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. + +

How to add a new auth scheme + +

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. + +

How to 'hook in' new functions to the API. + +

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. + + ICP diff --git a/doc/debug-sections.txt b/doc/debug-sections.txt index 1b35dbc6bc..b192ac07d2 100644 --- a/doc/debug-sections.txt +++ b/doc/debug-sections.txt @@ -33,7 +33,7 @@ section 25 MIME Parsing 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 diff --git a/helpers/basic_auth/LDAP/Makefile.in b/helpers/basic_auth/LDAP/Makefile.in new file mode 100644 index 0000000000..c697fa7568 --- /dev/null +++ b/helpers/basic_auth/LDAP/Makefile.in @@ -0,0 +1,76 @@ + +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 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 diff --git a/helpers/basic_auth/LDAP/README b/helpers/basic_auth/LDAP/README new file mode 100644 index 0000000000..2af85db4b4 --- /dev/null +++ b/helpers/basic_auth/LDAP/README @@ -0,0 +1,8 @@ +This LDAP Authentication code is written by Glen Newton +. + +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. diff --git a/helpers/basic_auth/LDAP/squid_ldap_auth.c b/helpers/basic_auth/LDAP/squid_ldap_auth.c new file mode 100644 index 0000000000..7f204d3317 --- /dev/null +++ b/helpers/basic_auth/LDAP/squid_ldap_auth.c @@ -0,0 +1,97 @@ +/* + * + * 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 + * + * 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 +#include +#include +#include +#include + +/* 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; +} diff --git a/helpers/basic_auth/MSNT/COPYING-2.0 b/helpers/basic_auth/MSNT/COPYING-2.0 new file mode 100644 index 0000000000..d684351da8 --- /dev/null +++ b/helpers/basic_auth/MSNT/COPYING-2.0 @@ -0,0 +1,341 @@ + + 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 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. + diff --git a/helpers/basic_auth/MSNT/Makefile.in b/helpers/basic_auth/MSNT/Makefile.in new file mode 100644 index 0000000000..1252ecd244 --- /dev/null +++ b/helpers/basic_auth/MSNT/Makefile.in @@ -0,0 +1,98 @@ +# +# 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 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 diff --git a/helpers/basic_auth/MSNT/README.html b/helpers/basic_auth/MSNT/README.html new file mode 100644 index 0000000000..6ac18ed3cc --- /dev/null +++ b/helpers/basic_auth/MSNT/README.html @@ -0,0 +1,317 @@ + + +MSNTAUTH readme + + + + + +

+MSNT Auth v2.0.1
+Squid web proxy Authentication module
+Antonino Iannella, Stellar-X Pty Ltd
+Fri Sep 29 15:53:33 CST 2000 +

+ +

Contents

+ + + +

Introduction

+ +

+This is an authentication module for the Squid proxy server +to authenticate users on an NT domain. + +

+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). + +

+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. + +

+Msntauth is released under the GNU General Public License and +is available from http://stellarx.tripod.com. + +

Installation

+ +

+Make any changes to the source code you need. + +

+Type 'make', then 'make install', then 'make clean'. + +

+To avoid using the makefile, it may compile with + + gcc -O2 -s -o msntauth *.c + +

+'Make install' will put 'msntauth' into +/usr/local/squid/bin by default. + +

+Hopefully nobody has problems compiling msntauth. +In the future I plan to use GNU automake. + +

Other compiling issues

+ +

+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 ' line commented out. +Remove the comment for S5R4 systems, like Solaris. + +

+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. + +

Configuration file

+ +

+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. + +

+An example configuration file is provided. It looks like + +

+# 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
+
+ +

+All comments start with '#'. + +

+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. + +

+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! + +

+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. + +

Denying users

+ +

+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. + +

+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. + +

+Msntauth will send syslog messages if a user was denied, +at LOG_USER facility. + +

Allowing users

+ +

+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. + +

+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. + +

+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. + +

+Some other rules - + +

    +
  1. The operation of the denied user file is independent of the +allowed user file. The former file is checked first. +
  2. You can use none, one, or both files. +
  3. If a username appears in the denied user file, they will +be denied, even if they are in the allowed user file. +
  4. If a username is not in either file, they will be denied, +because they have not been allowed. +
  5. If the allowed user file is in use and is empty, all +users will be allowed. +
+ +

+Hopefully this wasn't too confusing. + +

Squid.conf changes

+ +

+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 - + +

+  acl  proxy_auth REQUIRED
+  http_access allow password
+  http_access allow 
+  http_access deny all
+
+
+ +

+You will also need to review the following directives - + +

+  proxy_auth_realm enterprise web gateway
+  authenticate_program /usr/local/squid/bin/msntauth
+  authenticate_ttl 5
+  authenticate_children 20
+
+ +

Testing

+ +

+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. + +

+It should behave in the following way - +

+ - 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.
+
+ +

+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. + +

Contact details

+ +

+To contact the maintainer of this package, email Antonino Iannella +at antonino@usa.net, antonino.iannella@usa.net, or +antonino.iannella@camtech.com.au. + +

+The latest version may be found on http://members.tripod.com/stellarx. +It is also distributed as part of Squid. + +

Reported problem

+ +

+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. + +

+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 + +

+  patch smblib.c < smblib.c.patch
+
+ +

Known limitation

+ +

+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. + +

+As of version 2.0.1, this problem has been fixed. + +

Changes since last revision

+ +

+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. + +

    +
  • Added many patches from Duane Wessels to stop compilation errors (?) +
  • Improved the main() function yet again +
  • Created a more informative Makefile +
  • Added an 'allowed users' feature to complement the 'denied users' feature +
  • Stopped the use of alarm() which was causing problems under Solaris +
  • Added more syslog messages for authentication problems +
  • Added the use of a configuration file, instead of hard-coding NT server details +
  • Allowed for querying multiple NT servers and domains (this was a hot issue) +
  • Changed README into an HTML document to improve readability +
  • Didn't make use of GNU autoconf. I will in future, I promise. +
  • Removed denied/allowed username substring search limitation. +
+ +

+Hopefully msntauth and Squid is now a more valuable product. +Feel free to send me success or problem stories. + + + diff --git a/helpers/basic_auth/MSNT/allowusers.c b/helpers/basic_auth/MSNT/allowusers.c new file mode 100644 index 0000000000..42ebfe3f9c --- /dev/null +++ b/helpers/basic_auth/MSNT/allowusers.c @@ -0,0 +1,192 @@ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); + } +} diff --git a/helpers/basic_auth/MSNT/byteorder.h b/helpers/basic_auth/MSNT/byteorder.h new file mode 100644 index 0000000000..1856df9392 --- /dev/null +++ b/helpers/basic_auth/MSNT/byteorder.h @@ -0,0 +1,87 @@ +/* + * 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_ */ diff --git a/helpers/basic_auth/MSNT/confload.c b/helpers/basic_auth/MSNT/confload.c new file mode 100644 index 0000000000..89018af5e5 --- /dev/null +++ b/helpers/basic_auth/MSNT/confload.c @@ -0,0 +1,235 @@ + +/* + * 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 +#include +#include +#include +#include +#include + +#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. + */ diff --git a/helpers/basic_auth/MSNT/denyusers.c b/helpers/basic_auth/MSNT/denyusers.c new file mode 100644 index 0000000000..a339679fec --- /dev/null +++ b/helpers/basic_auth/MSNT/denyusers.c @@ -0,0 +1,249 @@ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + } +} diff --git a/helpers/basic_auth/MSNT/md4.c b/helpers/basic_auth/MSNT/md4.c new file mode 100644 index 0000000000..3943254220 --- /dev/null +++ b/helpers/basic_auth/MSNT/md4.c @@ -0,0 +1,209 @@ +/* + * 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; +} diff --git a/helpers/basic_auth/MSNT/msntauth-v2.0.lsm b/helpers/basic_auth/MSNT/msntauth-v2.0.lsm new file mode 100644 index 0000000000..0ad7d98da7 --- /dev/null +++ b/helpers/basic_auth/MSNT/msntauth-v2.0.lsm @@ -0,0 +1,13 @@ +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 diff --git a/helpers/basic_auth/MSNT/msntauth.c b/helpers/basic_auth/MSNT/msntauth.c new file mode 100644 index 0000000000..4f9da6a9ca --- /dev/null +++ b/helpers/basic_auth/MSNT/msntauth.c @@ -0,0 +1,114 @@ + +/* + * 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 +#include +#include +#include + +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; +} diff --git a/helpers/basic_auth/MSNT/rfcnb-common.h b/helpers/basic_auth/MSNT/rfcnb-common.h new file mode 100644 index 0000000000..fab20c4f6e --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-common.h @@ -0,0 +1,40 @@ +/* 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_ */ diff --git a/helpers/basic_auth/MSNT/rfcnb-error.h b/helpers/basic_auth/MSNT/rfcnb-error.h new file mode 100644 index 0000000000..6dd46820e6 --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-error.h @@ -0,0 +1,57 @@ +/* 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_ */ diff --git a/helpers/basic_auth/MSNT/rfcnb-io.c b/helpers/basic_auth/MSNT/rfcnb-io.c new file mode 100644 index 0000000000..a60de07986 --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-io.c @@ -0,0 +1,415 @@ +/* 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 */ +#include "std-includes.h" +#include "rfcnb-priv.h" +#include "rfcnb-util.h" +#include "rfcnb-io.h" +#include +#include + +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)); +} diff --git a/helpers/basic_auth/MSNT/rfcnb-io.h b/helpers/basic_auth/MSNT/rfcnb-io.h new file mode 100644 index 0000000000..267d12f854 --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-io.h @@ -0,0 +1,28 @@ +/* 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); diff --git a/helpers/basic_auth/MSNT/rfcnb-priv.h b/helpers/basic_auth/MSNT/rfcnb-priv.h new file mode 100644 index 0000000000..1e906b3bdd --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-priv.h @@ -0,0 +1,150 @@ +/* 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 diff --git a/helpers/basic_auth/MSNT/rfcnb-util.c b/helpers/basic_auth/MSNT/rfcnb-util.c new file mode 100644 index 0000000000..55928dfc69 --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-util.c @@ -0,0 +1,555 @@ +/* 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 +#include +#include +#include +#include +#include +#include + +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; + } +} diff --git a/helpers/basic_auth/MSNT/rfcnb-util.h b/helpers/basic_auth/MSNT/rfcnb-util.h new file mode 100644 index 0000000000..e19330d4be --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb-util.h @@ -0,0 +1,51 @@ +/* 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); diff --git a/helpers/basic_auth/MSNT/rfcnb.h b/helpers/basic_auth/MSNT/rfcnb.h new file mode 100644 index 0000000000..d1ebde427d --- /dev/null +++ b/helpers/basic_auth/MSNT/rfcnb.h @@ -0,0 +1,48 @@ +/* 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); diff --git a/helpers/basic_auth/MSNT/session.c b/helpers/basic_auth/MSNT/session.c new file mode 100644 index 0000000000..76eb81a568 --- /dev/null +++ b/helpers/basic_auth/MSNT/session.c @@ -0,0 +1,363 @@ +/* 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 +#include "rfcnb-priv.h" +#include "rfcnb-util.h" +#include "rfcnb-io.h" + +#include +#include +#include + +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; + +} diff --git a/helpers/basic_auth/MSNT/smbdes.c b/helpers/basic_auth/MSNT/smbdes.c new file mode 100644 index 0000000000..a9ac145c1e --- /dev/null +++ b/helpers/basic_auth/MSNT/smbdes.c @@ -0,0 +1,364 @@ +/* + * 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); +} diff --git a/helpers/basic_auth/MSNT/smbencrypt.c b/helpers/basic_auth/MSNT/smbencrypt.c new file mode 100644 index 0000000000..0bd847f9cb --- /dev/null +++ b/helpers/basic_auth/MSNT/smbencrypt.c @@ -0,0 +1,205 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +/* Antonino #include */ + +#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++; + } + } +} diff --git a/helpers/basic_auth/MSNT/smblib-common.h b/helpers/basic_auth/MSNT/smblib-common.h new file mode 100644 index 0000000000..b1ab26cb7f --- /dev/null +++ b/helpers/basic_auth/MSNT/smblib-common.h @@ -0,0 +1,189 @@ +/* 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_ */ diff --git a/helpers/basic_auth/MSNT/smblib-priv.h b/helpers/basic_auth/MSNT/smblib-priv.h new file mode 100644 index 0000000000..7caff4f4b0 --- /dev/null +++ b/helpers/basic_auth/MSNT/smblib-priv.h @@ -0,0 +1,604 @@ +/* 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 +#include + +#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__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_ */ diff --git a/helpers/basic_auth/MSNT/smblib-util.c b/helpers/basic_auth/MSNT/smblib-util.c new file mode 100644 index 0000000000..f4f8026a4b --- /dev/null +++ b/helpers/basic_auth/MSNT/smblib-util.c @@ -0,0 +1,803 @@ +/* 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 +#include +#include + +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)); + + } + } + +} diff --git a/helpers/basic_auth/MSNT/smblib.c b/helpers/basic_auth/MSNT/smblib.c new file mode 100644 index 0000000000..725b026846 --- /dev/null +++ b/helpers/basic_auth/MSNT/smblib.c @@ -0,0 +1,555 @@ +/* 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 +#include +#include +#include + +#include + +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); + +} diff --git a/helpers/basic_auth/MSNT/smblib.h b/helpers/basic_auth/MSNT/smblib.h new file mode 100644 index 0000000000..b08444dfec --- /dev/null +++ b/helpers/basic_auth/MSNT/smblib.h @@ -0,0 +1,98 @@ +/* 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); diff --git a/helpers/basic_auth/MSNT/std-defines.h b/helpers/basic_auth/MSNT/std-defines.h new file mode 100644 index 0000000000..c3959c64f0 --- /dev/null +++ b/helpers/basic_auth/MSNT/std-defines.h @@ -0,0 +1,45 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#endif /* _STD_DEFINES_H_ */ diff --git a/helpers/basic_auth/MSNT/std-includes.h b/helpers/basic_auth/MSNT/std-includes.h new file mode 100644 index 0000000000..95bc508c97 --- /dev/null +++ b/helpers/basic_auth/MSNT/std-includes.h @@ -0,0 +1,45 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +/* Pick up define for INADDR_NONE */ + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif diff --git a/helpers/basic_auth/MSNT/valid.c b/helpers/basic_auth/MSNT/valid.c new file mode 100644 index 0000000000..72a9aee40a --- /dev/null +++ b/helpers/basic_auth/MSNT/valid.c @@ -0,0 +1,45 @@ +#include +#include +#include +#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); +} diff --git a/helpers/basic_auth/MSNT/valid.h b/helpers/basic_auth/MSNT/valid.h new file mode 100644 index 0000000000..1d14e6bb8d --- /dev/null +++ b/helpers/basic_auth/MSNT/valid.h @@ -0,0 +1,12 @@ +#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 diff --git a/helpers/basic_auth/Makefile.in b/helpers/basic_auth/Makefile.in new file mode 100644 index 0000000000..b7b3fe6b60 --- /dev/null +++ b/helpers/basic_auth/Makefile.in @@ -0,0 +1,38 @@ +# 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; diff --git a/helpers/basic_auth/NCSA/Makefile.in b/helpers/basic_auth/NCSA/Makefile.in new file mode 100644 index 0000000000..3e7c373547 --- /dev/null +++ b/helpers/basic_auth/NCSA/Makefile.in @@ -0,0 +1,100 @@ +# +# 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 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 diff --git a/helpers/basic_auth/NCSA/ncsa_auth.c b/helpers/basic_auth/NCSA/ncsa_auth.c new file mode 100644 index 0000000000..ceb81572c0 --- /dev/null +++ b/helpers/basic_auth/NCSA/ncsa_auth.c @@ -0,0 +1,143 @@ +/* + * ncsa_auth.c + * + * AUTHOR: Arjan de Vet + * + * Example authentication program for Squid, based on the original + * proxy_auth code from client_side.c, written by + * Jon Thackray . + * + * 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 +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_CRYPT_H +#include +#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 \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); +} diff --git a/helpers/basic_auth/PAM/Makefile.in b/helpers/basic_auth/PAM/Makefile.in new file mode 100644 index 0000000000..f8a8c749cf --- /dev/null +++ b/helpers/basic_auth/PAM/Makefile.in @@ -0,0 +1,96 @@ +# +# 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 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 diff --git a/helpers/basic_auth/PAM/pam_auth.c b/helpers/basic_auth/PAM/pam_auth.c new file mode 100644 index 0000000000..36c03b28ff --- /dev/null +++ b/helpers/basic_auth/PAM/pam_auth.c @@ -0,0 +1,190 @@ +/* + * $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 + * + * 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 +#include +#include +#include +#include +#include + +#include + +#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 */ +} diff --git a/helpers/basic_auth/SMB/COPYING-2.0 b/helpers/basic_auth/SMB/COPYING-2.0 new file mode 100644 index 0000000000..d684351da8 --- /dev/null +++ b/helpers/basic_auth/SMB/COPYING-2.0 @@ -0,0 +1,341 @@ + + 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 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. + diff --git a/helpers/basic_auth/SMB/Makefile.in b/helpers/basic_auth/SMB/Makefile.in new file mode 100644 index 0000000000..8227978f8e --- /dev/null +++ b/helpers/basic_auth/SMB/Makefile.in @@ -0,0 +1,118 @@ +# +# 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 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 diff --git a/helpers/basic_auth/SMB/README b/helpers/basic_auth/SMB/README new file mode 100644 index 0000000000..f550dc386a --- /dev/null +++ b/helpers/basic_auth/SMB/README @@ -0,0 +1,3 @@ +For documentation, please refer to + + http://www.hacom.nl/~richard/software/smb_auth.html diff --git a/helpers/basic_auth/SMB/smb_auth.c b/helpers/basic_auth/SMB/smb_auth.c new file mode 100644 index 0000000000..5a5ef102eb --- /dev/null +++ b/helpers/basic_auth/SMB/smb_auth.c @@ -0,0 +1,232 @@ +/* + * smb_auth - SMB proxy authentication module + * Copyright (C) 1998 Richard Huveneers + * + * 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 +#include +#include + +#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; +} diff --git a/helpers/basic_auth/SMB/smb_auth.sh b/helpers/basic_auth/SMB/smb_auth.sh new file mode 100644 index 0000000000..99f86150b3 --- /dev/null +++ b/helpers/basic_auth/SMB/smb_auth.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# +# smb_auth - SMB proxy authentication module +# Copyright (C) 1998 Richard Huveneers +# +# 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 " ] || exit 1 +exit 0 diff --git a/helpers/basic_auth/YP/Makefile.in b/helpers/basic_auth/YP/Makefile.in new file mode 100644 index 0000000000..bdb3d7e1d4 --- /dev/null +++ b/helpers/basic_auth/YP/Makefile.in @@ -0,0 +1,100 @@ +# +# 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 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 diff --git a/helpers/basic_auth/YP/nis_support.c b/helpers/basic_auth/YP/nis_support.c new file mode 100644 index 0000000000..48a82c07fb --- /dev/null +++ b/helpers/basic_auth/YP/nis_support.c @@ -0,0 +1,46 @@ +/* + * Written By Rabellino Sergio (rabellino@di.unito.it) For Solaris 2.x + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/helpers/basic_auth/YP/yp_auth.c b/helpers/basic_auth/YP/yp_auth.c new file mode 100644 index 0000000000..2b54e051e0 --- /dev/null +++ b/helpers/basic_auth/YP/yp_auth.c @@ -0,0 +1,81 @@ +/* + * Adapted By Rabellino Sergio (rabellino@di.unito.it) For Solaris 2.x + * From NCSA Authentication module + */ + +#include "config.h" +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_CRYPT_H +#include +#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 \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); +} diff --git a/helpers/basic_auth/getpwnam/Makefile.in b/helpers/basic_auth/getpwnam/Makefile.in new file mode 100644 index 0000000000..5345559fb8 --- /dev/null +++ b/helpers/basic_auth/getpwnam/Makefile.in @@ -0,0 +1,80 @@ +# +# 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 diff --git a/helpers/basic_auth/getpwnam/getpwnam_auth.c b/helpers/basic_auth/getpwnam/getpwnam_auth.c new file mode 100644 index 0000000000..e71c3656fb --- /dev/null +++ b/helpers/basic_auth/getpwnam/getpwnam_auth.c @@ -0,0 +1,80 @@ +/* + * getpwnam_auth.c + * + * AUTHOR: Erik Hofman + * Robin Elfrink + * + * Example authentication program for Squid, based on the + * original proxy_auth code from client_side.c, written by + * Jon Thackray . + * + * 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 +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_CRYPT_H +#include +#endif +#if HAVE_PWD_H +#include +#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); +} diff --git a/helpers/basic_auth/multi-domain-NTLM/README.txt b/helpers/basic_auth/multi-domain-NTLM/README.txt new file mode 100644 index 0000000000..5ea1f409ca --- /dev/null +++ b/helpers/basic_auth/multi-domain-NTLM/README.txt @@ -0,0 +1,17 @@ + +From: "Chemolli Francesco (USI)" +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 + diff --git a/helpers/basic_auth/multi-domain-NTLM/smb_auth.pl b/helpers/basic_auth/multi-domain-NTLM/smb_auth.pl new file mode 100644 index 0000000000..34972a77f8 --- /dev/null +++ b/helpers/basic_auth/multi-domain-NTLM/smb_auth.pl @@ -0,0 +1,132 @@ +#!/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 () { + 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; +} + diff --git a/helpers/ntlm_auth/Makefile.in b/helpers/ntlm_auth/Makefile.in new file mode 100644 index 0000000000..0520eeb788 --- /dev/null +++ b/helpers/ntlm_auth/Makefile.in @@ -0,0 +1,38 @@ +# 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; diff --git a/helpers/ntlm_auth/SMB/Makefile.in b/helpers/ntlm_auth/SMB/Makefile.in new file mode 100644 index 0000000000..6382f47170 --- /dev/null +++ b/helpers/ntlm_auth/SMB/Makefile.in @@ -0,0 +1,86 @@ +# +# 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 diff --git a/helpers/ntlm_auth/SMB/libntlmssp.c b/helpers/ntlm_auth/SMB/libntlmssp.c new file mode 100644 index 0000000000..6c9ef0859f --- /dev/null +++ b/helpers/ntlm_auth/SMB/libntlmssp.c @@ -0,0 +1,221 @@ +/* + * (C) 2000 Francesco Chemolli + * 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 +#endif /* HAVE_STRING_H */ +#if HAVE_STDLIB_H +#include +#endif /* HAVE_STDLIB_H */ +#ifdef HAVE_UNISTD_H +#include +#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) (Almresponse); + 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; +} diff --git a/helpers/ntlm_auth/SMB/ntlm.h b/helpers/ntlm_auth/SMB/ntlm.h new file mode 100644 index 0000000000..65b65915a5 --- /dev/null +++ b/helpers/ntlm_auth/SMB/ntlm.h @@ -0,0 +1,100 @@ +/* + * (C) 2000 Francesco Chemolli , + * 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 +#endif +#if HAVE_SYS_TIME_H +#include +#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 + + +/* Debugging stuff */ + +#ifdef __GNUC__ /* this is really a gcc-ism */ +#ifdef DEBUG +#include +#include +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_ */ diff --git a/helpers/ntlm_auth/SMB/ntlm_auth.c b/helpers/ntlm_auth/SMB/ntlm_auth.c new file mode 100644 index 0000000000..c965eede6b --- /dev/null +++ b/helpers/ntlm_auth/SMB/ntlm_auth.c @@ -0,0 +1,336 @@ +/* + * (C) 2000 Francesco Chemolli + * 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 +#endif + + +#if HAVE_GETOPT_H +#include +#endif + + + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#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; +} diff --git a/helpers/ntlm_auth/SMB/smbval/Makefile.in b/helpers/ntlm_auth/SMB/smbval/Makefile.in new file mode 100644 index 0000000000..491a664b62 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/Makefile.in @@ -0,0 +1,52 @@ +# 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 *~ + diff --git a/helpers/ntlm_auth/SMB/smbval/byteorder.h b/helpers/ntlm_auth/SMB/smbval/byteorder.h new file mode 100644 index 0000000000..ab2578ce4c --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/byteorder.h @@ -0,0 +1,80 @@ +/* + * 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)) diff --git a/helpers/ntlm_auth/SMB/smbval/md4.c b/helpers/ntlm_auth/SMB/smbval/md4.c new file mode 100644 index 0000000000..4586c70248 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/md4.c @@ -0,0 +1,210 @@ +/* + * 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 + +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; +} diff --git a/helpers/ntlm_auth/SMB/smbval/md4.h b/helpers/ntlm_auth/SMB/smbval/md4.h new file mode 100644 index 0000000000..015b84997c --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/md4.h @@ -0,0 +1 @@ +void mdfour(unsigned char *out, unsigned char *in, int n); diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-common.h b/helpers/ntlm_auth/SMB/smbval/rfcnb-common.h new file mode 100644 index 0000000000..a56a539523 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-common.h @@ -0,0 +1,34 @@ +/* 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; diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-error.h b/helpers/ntlm_auth/SMB/smbval/rfcnb-error.h new file mode 100644 index 0000000000..14eb6c614a --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-error.h @@ -0,0 +1,74 @@ +/* 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 ..." + * + * }; + */ diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-io.c b/helpers/ntlm_auth/SMB/smbval/rfcnb-io.c new file mode 100644 index 0000000000..06ed1ee398 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-io.c @@ -0,0 +1,400 @@ +/* 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 */ +#include "config.h" +#include "std-includes.h" +#include "rfcnb-priv.h" +#include "rfcnb-util.h" +#include "rfcnb-io.h" +#include +#include +#include + +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)); +} diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-io.h b/helpers/ntlm_auth/SMB/smbval/rfcnb-io.h new file mode 100644 index 0000000000..40a96b3629 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-io.h @@ -0,0 +1,30 @@ +/* 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); diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-priv.h b/helpers/ntlm_auth/SMB/smbval/rfcnb-priv.h new file mode 100644 index 0000000000..766a66fb92 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-priv.h @@ -0,0 +1,156 @@ +#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__ */ diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-util.c b/helpers/ntlm_auth/SMB/smbval/rfcnb-util.c new file mode 100644 index 0000000000..5df94e37fe --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-util.c @@ -0,0 +1,529 @@ +/* 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 +#include + +#include "std-includes.h" +#include "rfcnb-priv.h" +#include "rfcnb-util.h" +#include "rfcnb-io.h" +#include + + +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; + } +} diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb-util.h b/helpers/ntlm_auth/SMB/smbval/rfcnb-util.h new file mode 100644 index 0000000000..66e93aec57 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb-util.h @@ -0,0 +1,50 @@ +/* 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); diff --git a/helpers/ntlm_auth/SMB/smbval/rfcnb.h b/helpers/ntlm_auth/SMB/smbval/rfcnb.h new file mode 100644 index 0000000000..cd071f62e4 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/rfcnb.h @@ -0,0 +1,55 @@ +/* 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); diff --git a/helpers/ntlm_auth/SMB/smbval/session.c b/helpers/ntlm_auth/SMB/smbval/session.c new file mode 100644 index 0000000000..c69ced899d --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/session.c @@ -0,0 +1,388 @@ +/* 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 +#include + +int RFCNB_errno = 0; +int RFCNB_saved_errno = 0; +#define RFCNB_ERRNO + +#include "std-includes.h" +#include +#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; + +} diff --git a/helpers/ntlm_auth/SMB/smbval/smbdes.c b/helpers/ntlm_auth/SMB/smbval/smbdes.c new file mode 100644 index 0000000000..a9ac145c1e --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smbdes.c @@ -0,0 +1,364 @@ +/* + * 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); +} diff --git a/helpers/ntlm_auth/SMB/smbval/smbdes.h b/helpers/ntlm_auth/SMB/smbval/smbdes.h new file mode 100644 index 0000000000..5a431140dd --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smbdes.h @@ -0,0 +1,2 @@ +void E_P16(unsigned char *p14, unsigned char *p16); +void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24); diff --git a/helpers/ntlm_auth/SMB/smbval/smbencrypt.c b/helpers/ntlm_auth/SMB/smbval/smbencrypt.c new file mode 100644 index 0000000000..4524b7ff30 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smbencrypt.c @@ -0,0 +1,208 @@ +/* + * 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 +#include +//#include +#include +#include +//#include +#include +#include + +#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++; + } + } +} diff --git a/helpers/ntlm_auth/SMB/smbval/smbencrypt.h b/helpers/ntlm_auth/SMB/smbval/smbencrypt.h new file mode 100644 index 0000000000..9dd72a9178 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smbencrypt.h @@ -0,0 +1 @@ +void SMBencrypt(uchar * passwd, uchar * c8, uchar * p24); diff --git a/helpers/ntlm_auth/SMB/smbval/smblib-common.h b/helpers/ntlm_auth/SMB/smbval/smblib-common.h new file mode 100644 index 0000000000..f2a4bb234f --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smblib-common.h @@ -0,0 +1,189 @@ +#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__ */ diff --git a/helpers/ntlm_auth/SMB/smbval/smblib-priv.h b/helpers/ntlm_auth/SMB/smbval/smblib-priv.h new file mode 100644 index 0000000000..412dcca1ef --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smblib-priv.h @@ -0,0 +1,655 @@ +#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 +#include + +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__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__ */ diff --git a/helpers/ntlm_auth/SMB/smbval/smblib-util.c b/helpers/ntlm_auth/SMB/smbval/smblib-util.c new file mode 100644 index 0000000000..ffb1fccb8e --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smblib-util.c @@ -0,0 +1,801 @@ +/* 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 +#include + +#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; +} diff --git a/helpers/ntlm_auth/SMB/smbval/smblib.c b/helpers/ntlm_auth/SMB/smbval/smblib.c new file mode 100644 index 0000000000..36d5c6d555 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smblib.c @@ -0,0 +1,574 @@ +/* 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 +#include +#include + +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 + +#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); + +} diff --git a/helpers/ntlm_auth/SMB/smbval/smblib.h b/helpers/ntlm_auth/SMB/smbval/smblib.h new file mode 100644 index 0000000000..92467a60b7 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/smblib.h @@ -0,0 +1,98 @@ +/* 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" diff --git a/helpers/ntlm_auth/SMB/smbval/std-defines.h b/helpers/ntlm_auth/SMB/smbval/std-defines.h new file mode 100644 index 0000000000..a9968c55e3 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/std-defines.h @@ -0,0 +1,45 @@ +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#endif /* __STD_DEFINES__ */ diff --git a/helpers/ntlm_auth/SMB/smbval/std-includes.h b/helpers/ntlm_auth/SMB/smbval/std-includes.h new file mode 100644 index 0000000000..2426899a91 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/std-includes.h @@ -0,0 +1,45 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +/* Pick up define for INADDR_NONE */ + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif diff --git a/helpers/ntlm_auth/SMB/smbval/valid.c b/helpers/ntlm_auth/SMB/smbval/valid.c new file mode 100644 index 0000000000..cdefa99352 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/valid.c @@ -0,0 +1,105 @@ +#include +#include +#include +#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); +} diff --git a/helpers/ntlm_auth/SMB/smbval/valid.h b/helpers/ntlm_auth/SMB/smbval/valid.h new file mode 100644 index 0000000000..4c2f6d2128 --- /dev/null +++ b/helpers/ntlm_auth/SMB/smbval/valid.h @@ -0,0 +1,15 @@ +#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 diff --git a/helpers/ntlm_auth/fakeauth/Makefile.in b/helpers/ntlm_auth/fakeauth/Makefile.in new file mode 100644 index 0000000000..e318416ddd --- /dev/null +++ b/helpers/ntlm_auth/fakeauth/Makefile.in @@ -0,0 +1,80 @@ +# +# 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 diff --git a/helpers/ntlm_auth/fakeauth/fakeauth_auth.c b/helpers/ntlm_auth/fakeauth/fakeauth_auth.c new file mode 100644 index 0000000000..e7603a241f --- /dev/null +++ b/helpers/ntlm_auth/fakeauth/fakeauth_auth.c @@ -0,0 +1,310 @@ +/* + * + * AUTHOR: Robert Collins + * + * Example ntlm authentication program for Squid, based on the + * original proxy_auth code from client_side.c, written by + * Jon Thackray . 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 + +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_CRYPT_H +#include +#endif +#if HAVE_PWD_H +#include +#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); +} diff --git a/helpers/ntlm_auth/fakeauth/ntlm.h b/helpers/ntlm_auth/fakeauth/ntlm.h new file mode 100644 index 0000000000..f14250f213 --- /dev/null +++ b/helpers/ntlm_auth/fakeauth/ntlm.h @@ -0,0 +1,105 @@ +/* + * $Id: ntlm.h,v 1.1 2001/01/07 23:36:50 hno Exp $ + * + * AUTHOR: Andy Doran + * + * 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 + +/* 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_ */ diff --git a/helpers/ntlm_auth/no_check/Makefile.in b/helpers/ntlm_auth/no_check/Makefile.in new file mode 100644 index 0000000000..30c2182d34 --- /dev/null +++ b/helpers/ntlm_auth/no_check/Makefile.in @@ -0,0 +1,80 @@ +# +# 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 diff --git a/helpers/ntlm_auth/no_check/README.no_check_ntlm_auth b/helpers/ntlm_auth/no_check/README.no_check_ntlm_auth new file mode 100644 index 0000000000..19d895a954 --- /dev/null +++ b/helpers/ntlm_auth/no_check/README.no_check_ntlm_auth @@ -0,0 +1,10 @@ +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 +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. diff --git a/include/ntlmauth.h b/include/ntlmauth.h new file mode 100644 index 0000000000..babec47619 --- /dev/null +++ b/include/ntlmauth.h @@ -0,0 +1,147 @@ +/* + * $Id: ntlmauth.h,v 1.1 2001/01/07 23:36:35 hno Exp $ + * + * * * * * * * * Legal stuff * * * * * * * + * + * (C) 2000 Francesco Chemolli , + * 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 /* 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_ */ diff --git a/include/util.h b/include/util.h index 8e2bfebd2f..c5633ac2ea 100644 --- a/include/util.h +++ b/include/util.h @@ -1,5 +1,5 @@ /* - * $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 * @@ -111,6 +111,7 @@ extern int safe_inet_addr(const char *, SIA *); 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); diff --git a/lib/Makefile.in b/lib/Makefile.in index 43b13e6d7f..5ae7ab5cd0 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -1,5 +1,5 @@ # -# $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@ @@ -43,7 +43,8 @@ UTILOBJS = rfc1123.o \ $(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) @@ -66,6 +67,11 @@ libdlmalloc.a: $(DLMALLOCOBJS) $(AR_R) $@ $(DLMALLOCOBJS) $(RANLIB) $@ +libntlmauth.a: $(NTLMAUTHOBJS) + $(RM) -f $@ + $(AR_R) $@ $(NTLMAUTHOBJS) + $(RANLIB) $@ + clean: -rm -f *.o $(LIBS) core diff --git a/lib/base64.c b/lib/base64.c index 60f797b27e..75c46d458f 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -1,5 +1,5 @@ /* - * $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" @@ -111,3 +111,50 @@ base64_encode(const char *decoded_str) 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; +} diff --git a/lib/ntlmauth.c b/lib/ntlmauth.c new file mode 100644 index 0000000000..5158798a4a --- /dev/null +++ b/lib/ntlmauth.c @@ -0,0 +1,138 @@ +/* + * $Id: ntlmauth.c,v 1.1 2001/01/07 23:36:36 hno Exp $ + * + * * * * * * * * Legal stuff * * * * * * * + * + * (C) 2000 Francesco Chemolli , + * 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; +} diff --git a/lib/util.c b/lib/util.c index 8d0db31b68..d2b290d4c2 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,6 +1,6 @@ /* - * $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 @@ -749,7 +749,7 @@ xitoa(int num) } /* 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)); diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc index cac8bf47af..50e7f11336 100644 --- a/src/HttpRequest.cc +++ b/src/HttpRequest.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -55,7 +55,10 @@ void 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); diff --git a/src/Makefile.in b/src/Makefile.in index e227537531..f6cf97439f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ # # 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: # @@ -18,7 +18,7 @@ localstatedir = @localstatedir@ srcdir = @srcdir@ VPATH = @srcdir@ -SUBDIRS = fs repl +SUBDIRS = fs repl auth # Gotta love the DOS legacy # @@ -47,6 +47,8 @@ DEFAULT_ICON_DIR = $(sysconfdir)/icons 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@ @@ -90,6 +92,7 @@ OBJS = \ access_log.o \ acl.o \ asn.o \ + auth_modules.o \ authenticate.o \ cache_cf.o \ CacheDigest.o \ @@ -148,7 +151,6 @@ OBJS = \ pconn.o \ peer_digest.o \ peer_select.o \ - pump.o \ redirect.o \ referer.o \ refresh.o \ @@ -208,8 +210,8 @@ $(OBJS): $(top_srcdir)/include/version.h ../include/autoconf.h $(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) @@ -298,6 +300,15 @@ $(REPL_OBJS): 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)"; \ @@ -406,7 +417,7 @@ install-pinger: 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; \ diff --git a/src/access_log.cc b/src/access_log.cc index d583bc4f67..a1585248c9 100644 --- a/src/access_log.cc +++ b/src/access_log.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -182,10 +182,60 @@ log_quote(const char *header) 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) @@ -200,24 +250,28 @@ accessLogSquid(AccessLogEntry * al) 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, @@ -226,25 +280,18 @@ accessLogCommon(AccessLogEntry * al) 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 diff --git a/src/acl.cc b/src/acl.cc index 9685b9c4fa..9f7b317bc6 100644 --- a/src/acl.cc +++ b/src/acl.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -38,7 +38,6 @@ static int aclFromFile = 0; static FILE *aclFile; -static hash_table *proxy_auth_cache = NULL; static void aclParseDomainList(void *curlist); static void aclParseUserList(void **current); @@ -53,7 +52,8 @@ static char *strtokFile(void); 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); @@ -76,8 +76,6 @@ static IPH aclLookupDstIPDone; 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 *); @@ -101,6 +99,7 @@ static wordlist *aclDumpArpList(void *); static SPLAYCMP aclArpCompare; static SPLAYWALKEE aclDumpArpListWalkee; #endif +static int aclCacheMatchAcl(dlink_list * cache, squid_acl acltype, void *data, char *MatchParam); static char * strtokFile(void) @@ -788,19 +787,25 @@ aclParseAclLine(acl ** head) 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 @@ -1081,205 +1086,229 @@ aclMatchUser(void *proxyauth_acl, char *user) 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 - */ - 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 @@ -1378,6 +1407,7 @@ aclMatchAcl(acl * ae, aclCheck_t * checklist) const char *header; const char *browser; int k; + http_hdr_type headertype; if (!ae) return 0; switch (ae->type) { @@ -1505,16 +1535,16 @@ aclMatchAcl(acl * ae, aclCheck_t * checklist) /* 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; @@ -1539,48 +1569,39 @@ aclMatchAcl(acl * ae, aclCheck_t * checklist) 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 */ @@ -1713,14 +1734,17 @@ aclCheck(aclCheck_t * checklist) 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; } @@ -1787,6 +1811,9 @@ aclCheckCallback(aclCheck_t * checklist, allow_t answer) 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); } @@ -1796,17 +1823,19 @@ aclLookupIdentDone(const char *ident, void *data) { 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 @@ -1847,17 +1876,22 @@ static void 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); } @@ -1886,9 +1920,9 @@ aclChecklistCreate(const acl_access * A, 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; } @@ -1933,15 +1967,6 @@ aclDestroyRegexList(relist * data) } } -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) { diff --git a/src/auth/Makefile.in b/src/auth/Makefile.in new file mode 100644 index 0000000000..0d11351e24 --- /dev/null +++ b/src/auth/Makefile.in @@ -0,0 +1,36 @@ +# 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 diff --git a/src/auth/basic/Makefile.in b/src/auth/basic/Makefile.in new file mode 100644 index 0000000000..60c2e2c716 --- /dev/null +++ b/src/auth/basic/Makefile.in @@ -0,0 +1,71 @@ +# +# 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 diff --git a/src/auth/basic/auth_basic.cc b/src/auth/basic/auth_basic.cc new file mode 100644 index 0000000000..31820dbf31 --- /dev/null +++ b/src/auth/basic/auth_basic.cc @@ -0,0 +1,640 @@ + +/* + * $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 : ""); + 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 + */ + 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); + } +} diff --git a/src/auth/basic/auth_basic.h b/src/auth/basic/auth_basic.h new file mode 100644 index 0000000000..9c49b59436 --- /dev/null +++ b/src/auth/basic/auth_basic.h @@ -0,0 +1,49 @@ +/* + * 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 diff --git a/src/auth/ntlm/Makefile.in b/src/auth/ntlm/Makefile.in new file mode 100644 index 0000000000..ac491cf448 --- /dev/null +++ b/src/auth/ntlm/Makefile.in @@ -0,0 +1,70 @@ +# +# 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 diff --git a/src/auth/ntlm/auth_ntlm.cc b/src/auth/ntlm/auth_ntlm.cc new file mode 100644 index 0000000000..ff439ef3d2 --- /dev/null +++ b/src/auth/ntlm/auth_ntlm.cc @@ -0,0 +1,1015 @@ + +/* + * $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 : ""); + 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; +} diff --git a/src/auth/ntlm/auth_ntlm.h b/src/auth/ntlm/auth_ntlm.h new file mode 100644 index 0000000000..354edad8e3 --- /dev/null +++ b/src/auth/ntlm/auth_ntlm.h @@ -0,0 +1,64 @@ +/* + * 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 diff --git a/src/auth_modules.sh b/src/auth_modules.sh new file mode 100644 index 0000000000..956e7be3a7 --- /dev/null +++ b/src/auth_modules.sh @@ -0,0 +1,15 @@ +#!/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 "}" diff --git a/src/authenticate.cc b/src/authenticate.cc index ce67ad3111..d03079626a 100644 --- a/src/authenticate.cc +++ b/src/authenticate.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -33,108 +33,673 @@ * */ -#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 : ""); - 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; + } } diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 41aa3ed43c..dab27b16ff 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -63,14 +63,14 @@ static int parseTimeUnits(const char *unit); 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[]); @@ -303,17 +303,6 @@ configDoConfigure(void) 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); @@ -369,8 +358,6 @@ configDoConfigure(void) #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 @@ -923,6 +910,86 @@ check_null_string(char *s) 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) { @@ -1423,7 +1490,7 @@ dump_int(StoreEntry * entry, const char *name, int var) storeAppendPrintf(entry, "%s %d\n", name, var); } -static void +void parse_int(int *var) { int i; @@ -1620,7 +1687,7 @@ free_string(char **var) safe_free(*var); } -static void +void parse_eol(char *volatile *var) { char *token = strtok(NULL, null_string); @@ -1636,7 +1703,7 @@ dump_time_t(StoreEntry * entry, const char *name, time_t var) storeAppendPrintf(entry, "%s %d seconds\n", name, (int) var); } -static void +void parse_time_t(time_t * var) { parseTimeLine(var, T_SECOND_STR); @@ -1729,7 +1796,7 @@ dump_wordlist(StoreEntry * entry, const char *name, wordlist * list) } } -static void +void parse_wordlist(wordlist ** list) { char *token; @@ -1919,7 +1986,7 @@ configFreeMemory(void) free_all(); } -static void +void requirePathnameExists(const char *name, const char *path) { struct stat sb; diff --git a/src/cbdata.cc b/src/cbdata.cc index f6ea50eed2..81df59870e 100644 --- a/src/cbdata.cc +++ b/src/cbdata.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -137,6 +137,8 @@ cbdataInit(void) 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); diff --git a/src/cf.data.pre b/src/cf.data.pre index 322ba34fa3..ff40101b14 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $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/ @@ -228,7 +228,7 @@ DOC_START no-digest no-netdb-exchange no-delay - login=user:password + login=user:password|PASS connect-timeout=nn digest-url=url allow-miss @@ -280,6 +280,13 @@ DOC_START 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) @@ -1104,17 +1111,35 @@ DOC_START 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 @@ -1124,19 +1149,87 @@ DOC_START 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 +auth_param ntlm children 5 +auth_param ntlm max_challenge_reuses 0 +auth_param ntlm max_challenge_lifetime 2 minutes +#auth_param basic program +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 @@ -1144,9 +1237,9 @@ TYPE: time_t 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 @@ -1176,7 +1269,7 @@ TYPE: onoff 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. @@ -1824,17 +1917,6 @@ DOC_START 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 diff --git a/src/client_side.cc b/src/client_side.cc index 97d067586a..c9db0e5eb1 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -116,6 +116,7 @@ static DEFER httpAcceptDefer; 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) @@ -138,11 +139,9 @@ static void 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 * @@ -152,15 +151,16 @@ clientAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http) 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; } @@ -223,8 +223,7 @@ clientAccessCheckDone(int answer, void *data) 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); @@ -266,7 +265,13 @@ clientAccessCheckDone(int answer, void *data) 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); } @@ -305,13 +310,10 @@ clientRedirectDone(void *data, char *result) 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; @@ -706,6 +708,8 @@ httpRequestFree(void *data) 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 @@ -746,10 +750,13 @@ httpRequestFree(void *data) 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); } @@ -784,6 +791,7 @@ httpRequestFree(void *data) 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) @@ -805,6 +813,7 @@ connStateFree(int fd, void *data) 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); @@ -986,14 +995,18 @@ clientCachable(clientHttpRequest * http) 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; @@ -1272,6 +1285,9 @@ clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep) 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()); @@ -1853,7 +1869,7 @@ clientKeepaliveNextRequest(clientHttpRequest * http) 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(). */ @@ -2135,13 +2151,6 @@ clientProcessRequest(clientHttpRequest * http) } /* 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); } @@ -2509,7 +2518,10 @@ static int 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 @@ -2542,14 +2554,18 @@ clientReadRequest(int fd, void *data) * 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; } @@ -2559,7 +2575,11 @@ clientReadRequest(int fd, void *data) 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()); @@ -2570,13 +2590,16 @@ clientReadRequest(int fd, void *data) 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--; @@ -2592,6 +2615,9 @@ clientReadRequest(int fd, void *data) 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, @@ -2686,7 +2712,7 @@ clientReadRequest(int fd, void *data) 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); @@ -2696,38 +2722,13 @@ clientReadRequest(int fd, void *data) 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, @@ -2737,7 +2738,7 @@ clientReadRequest(int fd, void *data) } } 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() @@ -2776,7 +2777,132 @@ clientReadRequest(int fd, void *data) } 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 */ diff --git a/src/defines.h b/src/defines.h index 0abde80070..70b066e6fc 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -53,7 +53,6 @@ #define DefaultDnsChildrenMax 32 /* 32 processes */ #define DefaultRedirectChildrenMax 32 /* 32 processes */ -#define DefaultAuthenticateChildrenMax 32 /* 32 processes */ #define MAXHTTPPORTS 12 #define COMM_OK (0) @@ -133,10 +132,9 @@ #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 diff --git a/src/enums.h b/src/enums.h index cbb452877a..c1fa715889 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -130,7 +130,7 @@ typedef enum { ACL_LOOKUP_NEEDED, ACL_LOOKUP_PENDING, ACL_LOOKUP_DONE, - ACL_PROXY_AUTH_NEEDED + ACL_PROXY_AUTH_NEEDED, } acl_lookup_state; enum { @@ -492,6 +492,37 @@ typedef 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, @@ -517,8 +548,10 @@ typedef enum { 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 @@ -551,8 +584,11 @@ typedef enum { 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, @@ -609,6 +645,9 @@ typedef enum { MEM_PUMP_STATE_DATA, MEM_CLIENT_REQ_BUF, MEM_MAX +#ifdef NTLM_CACHING + ,MEM_NTLM_AUTH_CACHE +#endif } mem_type; /* @@ -696,6 +735,8 @@ typedef enum { CBDATA_generic_cbdata, CBDATA_helper, CBDATA_helper_server, + CBDATA_statefulhelper, + CBDATA_helper_stateful_server, CBDATA_HttpStateData, CBDATA_peer, CBDATA_ps_state, diff --git a/src/errorpage.cc b/src/errorpage.cc index 365df2c3c5..e63b7aea28 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -52,8 +52,6 @@ typedef struct { /* 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 @@ -280,25 +278,13 @@ errorAppendEntry(StoreEntry * entry, ErrorState * err) 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); @@ -361,10 +347,13 @@ errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data 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); } @@ -377,11 +366,12 @@ errorStateFree(ErrorState * 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); } @@ -483,7 +473,7 @@ errorConvert(char token, ErrorState * 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]"; diff --git a/src/forward.cc b/src/forward.cc index 887dcb105f..aec80d1af5 100644 --- a/src/forward.cc +++ b/src/forward.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -138,9 +138,8 @@ fwdCheckRetry(FwdState * fwdState) 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; } @@ -392,6 +391,7 @@ fwdDispatch(FwdState * fwdState) fwdState->request->peer_login = p->login; httpStart(fwdState); } else { + fwdState->request->peer_login = NULL; switch (request->protocol) { case PROTO_HTTP: httpStart(fwdState); @@ -459,9 +459,8 @@ fwdReforward(FwdState * 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); diff --git a/src/ftp.cc b/src/ftp.cc index 917ce5b805..19d4d8612c 100644 --- a/src/ftp.cc +++ b/src/ftp.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -148,8 +148,9 @@ typedef void (FTPSM) (FtpStateData *); /* 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; @@ -159,8 +160,6 @@ static int ftpRestartable(FtpStateData * ftpState); 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 *); @@ -194,6 +193,7 @@ static FTPSM ftpListDir; static FTPSM ftpGetFile; static FTPSM ftpSendCwd; static FTPSM ftpReadCwd; +static FTPSM ftpRestOrList; static FTPSM ftpSendList; static FTPSM ftpSendNlst; static FTPSM ftpReadList; @@ -202,16 +202,15 @@ static FTPSM ftpReadRest; 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) ** ************************************************* @@ -222,17 +221,21 @@ User Pass 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 - ************************************************/ @@ -255,7 +258,8 @@ FTPSM *FTP_SM_FUNCS[] = 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 */ }; @@ -838,10 +842,10 @@ ftpParseListing(FtpStateData * ftpState) } 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 @@ -862,9 +866,9 @@ ftpDataRead(int fd, void *data) 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); @@ -913,7 +917,7 @@ ftpDataRead(int fd, void *data) return; } } else if (len == 0) { - ftpReadComplete(ftpState); + ftpDataComplete(ftpState); } else { if (ftpState->flags.isdir) { ftpParseListing(ftpState); @@ -921,14 +925,11 @@ ftpDataRead(int fd, void *data) 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); } } @@ -1134,7 +1135,7 @@ ftpWriteCommandCallback(int fd, char *bufnotused, size_t size, int errflag, void 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; @@ -1528,15 +1529,15 @@ ftpReadCwd(FtpStateData * ftpState) 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; @@ -1910,14 +1911,17 @@ static void 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)) @@ -1953,14 +1957,20 @@ ftpReadStor(FtpStateData * 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"); @@ -2157,11 +2167,7 @@ ftpReadTransferDone(FtpStateData * ftpState) /* 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); @@ -2171,15 +2177,62 @@ ftpReadTransferDone(FtpStateData * ftpState) } } +/* 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 @@ -2370,48 +2423,6 @@ ftpFailedErrorMessage(FtpStateData * ftpState, err_type error) 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) { diff --git a/src/globals.h b/src/globals.h index 81414376de..7f4d419dc2 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -145,6 +145,7 @@ extern int refresh_nocache_hack; /* 0 */ 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 */ @@ -152,3 +153,4 @@ extern int store_swap_high; /* 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 */ diff --git a/src/helper.cc b/src/helper.cc index dd5b0e3d15..0201482d15 100644 --- a/src/helper.cc +++ b/src/helper.cc @@ -1,6 +1,6 @@ /* - * $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? @@ -38,14 +38,24 @@ #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) @@ -123,6 +133,87 @@ 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) { @@ -141,6 +232,136 @@ 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 @@ -190,6 +411,57 @@ helperStats(StoreEntry * sentry, helper * hlp) 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) { @@ -220,6 +492,47 @@ 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) { @@ -229,6 +542,16 @@ 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) { @@ -239,6 +562,17 @@ 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 */ /* ====================================================================== */ @@ -275,6 +609,41 @@ helperServerFree(int fd, void *data) 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) { @@ -329,6 +698,101 @@ 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) { @@ -349,6 +813,35 @@ 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) { @@ -363,6 +856,33 @@ 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) { @@ -381,6 +901,31 @@ 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) { @@ -410,6 +955,60 @@ 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) { @@ -419,6 +1018,23 @@ 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) { @@ -426,3 +1042,11 @@ 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); +} diff --git a/src/http.cc b/src/http.cc index 34439d9170..a8e63eb305 100644 --- a/src/http.cc +++ b/src/http.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -44,7 +44,6 @@ static const char *const crlf = "\r\n"; static CWCB httpSendComplete; static CWCB httpSendRequestEntry; -static CWCB httpSendRequestEntryDone; static PF httpReadReply; static void httpSendRequest(HttpStateData *); @@ -690,16 +689,27 @@ httpBuildRequestHeader(request_t * request, } 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: /* @@ -780,7 +790,8 @@ httpBuildRequestHeader(request_t * request, } /* 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)); } @@ -855,7 +866,7 @@ httpSendRequest(HttpStateData * httpState) 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; @@ -869,6 +880,8 @@ httpSendRequest(HttpStateData * httpState) 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? */ @@ -953,43 +966,50 @@ httpStart(FwdState * fwd) } 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); @@ -1006,18 +1026,11 @@ httpSendRequestEntryDone(int fd, char *bufnotused, size_t size, int errflag, voi 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 diff --git a/src/main.cc b/src/main.cc index 556516d963..e5c05b2c65 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -349,6 +349,7 @@ mainReconfigure(void) 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 */ @@ -358,7 +359,7 @@ mainReconfigure(void) idnsInit(); #endif redirectInit(); - authenticateInit(); + authenticateInit(&Config.authConfig); #if USE_WCCP wccpInit(); #endif @@ -399,7 +400,7 @@ mainRotate(void) dnsInit(); #endif redirectInit(); - authenticateInit(); + authenticateInit(&Config.authConfig); } static void @@ -489,7 +490,7 @@ mainInitialize(void) idnsInit(); #endif redirectInit(); - authenticateInit(); + authenticateInit(&Config.authConfig); useragentOpenLog(); refererOpenLog(); httpHeaderInitModule(); /* must go before any header processing (e.g. the one in errorInitialize) */ @@ -632,6 +633,7 @@ main(int argc, char **argv) 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) diff --git a/src/mem.cc b/src/mem.cc index 52692e6018..24fad0f5b7 100644 --- a/src/mem.cc +++ b/src/mem.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -204,10 +204,14 @@ memInit(void) 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 @@ -286,10 +290,15 @@ memInit(void) 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); diff --git a/src/protos.h b/src/protos.h index d85bec4da1..c393371026 100644 --- a/src/protos.h +++ b/src/protos.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -46,6 +46,7 @@ extern void headersLog(int cs, int pq, method_t m, void *data); #endif char *log_quote(const char *header); +/* acl.c */ extern aclCheck_t *aclChecklistCreate(const struct _acl_access *, request_t *, const char *ident); @@ -67,6 +68,7 @@ extern void aclParseRegexList(void *curlist); 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 @@ -85,6 +87,12 @@ extern void allocate_new_swapdir(cacheSwap *); 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 @@ -123,6 +131,8 @@ extern void clientHttpConnectionsOpen(void); 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); @@ -707,9 +717,47 @@ extern void redirectStart(clientHttpRequest *, RH *, void *); 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 *); @@ -1028,11 +1076,6 @@ extern void releaseServerSockets(void); 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); @@ -1084,6 +1127,9 @@ extern void asnFreeMemory(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 *); @@ -1187,11 +1233,23 @@ extern void delayUnregisterDelayIdPtr(delay_id * loc); /* 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); diff --git a/src/redirect.cc b/src/redirect.cc index e7391726fb..9d47053290 100644 --- a/src/redirect.cc +++ b/src/redirect.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -126,12 +126,12 @@ redirectStart(clientHttpRequest * http, RH * handler, void *data) 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; diff --git a/src/ssl.cc b/src/ssl.cc index f7f56bc41d..4d11b7f6e2 100644 --- a/src/ssl.cc +++ b/src/ssl.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -587,6 +587,7 @@ sslPeerSelectComplete(FwdServer * fs, void *data) 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 diff --git a/src/stat.cc b/src/stat.cc index 428c665905..74219a0803 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -1415,8 +1415,6 @@ statClientRequests(StoreEntry * s) 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); } diff --git a/src/structs.h b/src/structs.h index 7e3e74caee..0e5d77caef 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -73,15 +73,100 @@ struct _acl_name_list { 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 { @@ -161,11 +246,9 @@ struct _aclCheck_t { 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; @@ -206,6 +289,7 @@ struct _sockaddr_in_list { sockaddr_in_list *next; }; + #if DELAY_POOLS struct _delaySpec { int restore_bps; @@ -334,7 +418,6 @@ struct _SquidConfig { char *dnsserver; #endif wordlist *redirect; - wordlist *authenticate; #if USE_ICMP char *pinger; #endif @@ -346,7 +429,7 @@ struct _SquidConfig { int dnsChildren; #endif int redirectChildren; - int authenticateChildren; + time_t authenticateGCInterval; time_t authenticateTTL; time_t authenticateIpTTL; struct { @@ -470,7 +553,11 @@ struct _SquidConfig { 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; @@ -876,7 +963,8 @@ struct _AccessLogEntry { size_t size; log_type code; int msec; - const char *ident; + const char *rfc931; + const char *authuser; } cache; struct { char *request; @@ -932,13 +1020,25 @@ struct _ConnStateData { 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; @@ -1421,7 +1521,6 @@ struct _request_flags { 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 @@ -1429,6 +1528,7 @@ struct _request_flags { #endif unsigned int accelerated:1; unsigned int internal:1; + unsigned int body_sent:1; }; struct _link_list { @@ -1461,7 +1561,7 @@ struct _request_t { 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; @@ -1478,8 +1578,7 @@ struct _request_t { 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; @@ -1524,13 +1623,13 @@ struct _ErrorState { 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; @@ -1816,6 +1915,15 @@ struct _helper_request { 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; @@ -1833,6 +1941,26 @@ struct _helper { } 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; @@ -1856,6 +1984,34 @@ struct _helper_server { } 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 diff --git a/src/tools.cc b/src/tools.cc index f08689b333..645163dd7d 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -62,6 +62,8 @@ extern int setresuid(uid_t, uid_t, uid_t); extern void (*failure_notify) (const char *); +MemPool *dlink_node_pool = NULL; + void releaseServerSockets(void) { @@ -762,6 +764,24 @@ checkNullString(char *p) 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) { diff --git a/src/tunnel.cc b/src/tunnel.cc index 581f7d6c34..55f6f83511 100644 --- a/src/tunnel.cc +++ b/src/tunnel.cc @@ -1,6 +1,6 @@ /* - * $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 @@ -587,6 +587,7 @@ sslPeerSelectComplete(FwdServer * fs, void *data) 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 diff --git a/src/typedefs.h b/src/typedefs.h index 8eed27c0ca..f28297ed7d 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -1,6 +1,6 @@ /* - * $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/ @@ -61,8 +61,14 @@ typedef struct _acl_ip_data acl_ip_data; 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; @@ -105,6 +111,7 @@ typedef struct _HttpStateData HttpStateData; 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; @@ -134,6 +141,7 @@ typedef struct _StoreEntry StoreEntry; 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; @@ -147,6 +155,7 @@ typedef struct _dlink_list dlink_list; 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; @@ -160,8 +169,11 @@ typedef struct _Version Version; 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; @@ -217,6 +229,7 @@ typedef void PSC(FwdServer *, void *); 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 *); @@ -231,6 +244,9 @@ typedef void OBJH(StoreEntry *); 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); @@ -274,6 +290,28 @@ typedef void STFSSHUTDOWN(void); 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