From ff76efc3e5e1b0e4ca3b10b7402406f619509bba Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 21 Apr 2004 17:37:06 +0000 Subject: Initial Import --- .cvsignore | 20 + AUTHORS | 1 + COPYING | 31 + ChangeLog | 0 INSTALL | 8 + Makefile.am | 7 + NEWS | 1 + README | 3 + common/buffer.c | 429 ++++++++++++++ common/compat.c | 30 + common/compat.h | 40 ++ common/hash.c | 462 +++++++++++++++ common/hash.h | 171 ++++++ common/md5.c | 228 +++++++ common/md5.h | 49 ++ configure.in | 83 +++ daemon/.cvsignore | 4 + daemon/Makefile.am | 13 + daemon/basic.c | 243 ++++++++ daemon/defaults.h | 9 + daemon/httpauthd.c | 1031 ++++++++++++++++++++++++++++++++ daemon/httpauthd.h | 328 +++++++++++ daemon/ldap.c | 1114 +++++++++++++++++++++++++++++++++++ daemon/misc.c | 621 ++++++++++++++++++++ daemon/ntlm.c | 703 ++++++++++++++++++++++ daemon/ntlmssp.c | 398 +++++++++++++ daemon/ntlmssp.h | 140 +++++ daemon/rfcnb/byteorder.h | 80 +++ daemon/rfcnb/rfcnb-common.h | 36 ++ daemon/rfcnb/rfcnb-error.h | 75 +++ daemon/rfcnb/rfcnb-io.c | 407 +++++++++++++ daemon/rfcnb/rfcnb-io.h | 28 + daemon/rfcnb/rfcnb-priv.h | 151 +++++ daemon/rfcnb/rfcnb-util.c | 532 +++++++++++++++++ daemon/rfcnb/rfcnb-util.h | 50 ++ daemon/rfcnb/rfcnb.h | 48 ++ daemon/rfcnb/session.c | 364 ++++++++++++ daemon/rfcnb/std-includes.h | 45 ++ daemon/rfcnb/x_Makefile | 38 ++ daemon/smblib/exper.c | 748 +++++++++++++++++++++++ daemon/smblib/file.c | 1306 +++++++++++++++++++++++++++++++++++++++++ daemon/smblib/find_password.c | 281 +++++++++ daemon/smblib/smb-errors.c | 220 +++++++ daemon/smblib/smbencrypt.c | 202 +++++++ daemon/smblib/smblib-api.c | 379 ++++++++++++ daemon/smblib/smblib-common.h | 184 ++++++ daemon/smblib/smblib-priv.h | 624 ++++++++++++++++++++ daemon/smblib/smblib-util.c | 783 ++++++++++++++++++++++++ daemon/smblib/smblib.c | 549 +++++++++++++++++ daemon/smblib/smblib.h | 95 +++ daemon/smblib/std-defines.h | 45 ++ daemon/usuals.h | 31 + doc/protocol.txt | 46 ++ sample/httpauthd.conf | 8 + 54 files changed, 13522 insertions(+) create mode 100644 .cvsignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 common/buffer.c create mode 100644 common/compat.c create mode 100644 common/compat.h create mode 100644 common/hash.c create mode 100644 common/hash.h create mode 100644 common/md5.c create mode 100644 common/md5.h create mode 100644 configure.in create mode 100644 daemon/.cvsignore create mode 100644 daemon/Makefile.am create mode 100644 daemon/basic.c create mode 100644 daemon/defaults.h create mode 100644 daemon/httpauthd.c create mode 100644 daemon/httpauthd.h create mode 100644 daemon/ldap.c create mode 100644 daemon/misc.c create mode 100644 daemon/ntlm.c create mode 100644 daemon/ntlmssp.c create mode 100644 daemon/ntlmssp.h create mode 100644 daemon/rfcnb/byteorder.h create mode 100644 daemon/rfcnb/rfcnb-common.h create mode 100644 daemon/rfcnb/rfcnb-error.h create mode 100644 daemon/rfcnb/rfcnb-io.c create mode 100644 daemon/rfcnb/rfcnb-io.h create mode 100644 daemon/rfcnb/rfcnb-priv.h create mode 100644 daemon/rfcnb/rfcnb-util.c create mode 100644 daemon/rfcnb/rfcnb-util.h create mode 100644 daemon/rfcnb/rfcnb.h create mode 100644 daemon/rfcnb/session.c create mode 100644 daemon/rfcnb/std-includes.h create mode 100644 daemon/rfcnb/x_Makefile create mode 100644 daemon/smblib/exper.c create mode 100644 daemon/smblib/file.c create mode 100644 daemon/smblib/find_password.c create mode 100644 daemon/smblib/smb-errors.c create mode 100644 daemon/smblib/smbencrypt.c create mode 100644 daemon/smblib/smblib-api.c create mode 100644 daemon/smblib/smblib-common.h create mode 100644 daemon/smblib/smblib-priv.h create mode 100644 daemon/smblib/smblib-util.c create mode 100644 daemon/smblib/smblib.c create mode 100644 daemon/smblib/smblib.h create mode 100644 daemon/smblib/std-defines.h create mode 100644 daemon/usuals.h create mode 100644 doc/protocol.txt create mode 100644 sample/httpauthd.conf diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..c3101e3 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,20 @@ +.cdtproject +.project +junk +config.h +autom4te.cache +Makefile +Makefile.in +aclocal.m4 +config.h +config.log +config.status +configure +depcomp +install-sh +missing +mkinstalldirs +stamp-* +*~ +compile +config.h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d2e76d6 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +nielsen@memberwebs.com diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..763af15 --- /dev/null +++ b/COPYING @@ -0,0 +1,31 @@ + +Copyright (c) 2004, Nate Nielsen +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + * Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or + other materials provided with the distribution. + * The names of contributors to this software may not be + used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..5c551d0 --- /dev/null +++ b/INSTALL @@ -0,0 +1,8 @@ +====================================================================== + HTTPAUTH INSTALL + +QUICK INSTALLATION: + # tar -zxvf httpauth-0.1.tar.gz + # ./configure + # make && make install + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d22b079 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ + +EXTRA_DIST = +SUBDIRS = daemon + +dist-hook: + rm -rf `find $(distdir)/ -name CVS` + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..c7ab92a --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +See ChangeLog \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..f8cfa13 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +================================================================= + HTTPAUTH README + diff --git a/common/buffer.c b/common/buffer.c new file mode 100644 index 0000000..28cab24 --- /dev/null +++ b/common/buffer.c @@ -0,0 +1,429 @@ + +#include "usuals.h" +#include "httpauthd.h" + +#include + +/* ----------------------------------------------------------------------- + * Memory Buffer + */ + +/* + * LEGEND: + * _al: allocated + * _dt: data + * _rp: read/write point + * _pp: parse/begin point + */ + +void buffer_bump(ha_buffer_t* buf, int count) +{ + if(ha_buferr(buf)) + return; + + if(buf->_rp + count >= buf->_dt + buf->_al) + { + char* old = buf->_dt; + int alloc = buf->_al; + int diff; + + while((buf->_al + count) > alloc) + alloc *= 2; + + buf->_dt = (char*)reallocf(buf->_dt, alloc * sizeof(char)); + if(!buf->_dt) + { + buf->_al = 0; + buf->_rp = buf->_pp = NULL; + ha_messagex(LOG_CRIT, "out of memory"); + return; + } + + buf->_al = alloc; + + diff = buf->_dt - old; + buf->_rp += diff; + buf->_pp += diff; + } +} + +void ha_bufreset(ha_buffer_t* buf) +{ + if(!buf->_dt || buf->_al == 0) + { + buf->_dt = (char*)reallocf(buf->_dt, 256 * sizeof(char)); + if(!buf->_dt) + { + buf->_al = 0; + buf->_rp = buf->_pp = NULL; + ha_messagex(LOG_CRIT, "out of memory"); + return; + } + + buf->_al = 256; + } + + buf->_rp = buf->_dt; + buf->_pp = buf->_dt; +} + +void ha_bufinit(ha_buffer_t* buf) +{ + memset(buf, 0, sizeof(*buf)); + ha_bufreset(buf); +} + +void ha_buffree(ha_buffer_t* buf) +{ + if(buf->_dt) + free(buf->_dt); + + buf->_al = 0; + buf->_rp = buf->_pp = NULL; +} + +int ha_readline(int fd, ha_buffer_t* buf) +{ + int l; + + if(ha_buferr(buf)) + return 0; + + for(;;) + { + buffer_bump(buf, 1); + l = read(fd, (void*)buf->_rp, sizeof(char)); + + /* We got a character */ + if(l == 1) + { + /* Skip junky CRLFs */ + if(*(buf->_rp) == '\r') + { + *(buf->_rp) = ' '; + continue; + } + + /* End of line */ + else if(*(buf->_rp) == '\n') + { + buf->_rp++; + break; + } + + /* All other characters */ + else + { + buf->_rp++; + } + } + + /* If it's the end of file then return that */ + else if(l == 0) + return 0; + + /* Transient errors */ + else if(l == -1 && (errno == EINTR || errno == EAGAIN)) + continue; + + /* Fatal errors */ + else if(l == -1) + { + ha_message(LOG_ERR, "couldn't read data"); + return 0; + } + } + + return 1; +} + +char* ha_parseword(ha_buffer_t* buf, const char* delims) +{ + char* word = NULL; + + if(ha_buferr(buf)) + return NULL; + + /* Knock out any previous delims */ + while(buf->_pp < buf->_rp && strchr(delims, *(buf->_pp))) + buf->_pp++; + + /* If at end of buffer or end of line return null */ + if(buf->_pp == buf->_rp || *(buf->_pp) == '\n') + return NULL; + + /* We do this before we stash away a pointer */ + buffer_bump(buf, 1); + + word = buf->_pp; + + while(!strchr(delims, *(buf->_pp))) + { + buf->_pp++; + + /* At the end of the buffer */ + if(buf->_pp == buf->_rp) + { + *(buf->_rp) = 0; + buf->_rp++; + break; + } + + /* At the end of a line */ + else if(*(buf->_pp) == '\n') + break; + } + + /* Now null terminate what we found */ + *(buf->_pp) = 0; + buf->_pp++; + + /* We don't return empty strings */ + if(word[0] == 0) + return NULL; + + return word; +} + +char* ha_parseline(ha_buffer_t* buf, int trim) +{ + char* t; + char* line = NULL; + + if(ha_buferr(buf)) + return NULL; + + if(trim) + { + /* Knock out any previous whitespace */ + while(buf->_pp < buf->_rp && isblank(*(buf->_pp))) + buf->_pp++; + } + + if(buf->_pp == buf->_rp) + return NULL; + + /* We do this before we stash away a pointer */ + buffer_bump(buf, 1); + + line = buf->_pp; + + t = (char*)memchr(buf->_pp, '\n', ha_buflen(buf)); + if(t == NULL) + { + t = (buf->_rp); + buf->_rp++; + } + + *t = 0; + buf->_pp = t + 1; + + if(trim) + { + while(t > line && isspace(*(--t))) + *t = 0; + } + + /* We don't return empty strings */ + if(line[0] == 0) + return NULL; + + return line; +} + +void ha_bufnext(ha_buffer_t* buf) +{ + buffer_bump(buf, 1); + + if(!ha_buferr(buf)) + { + buf->_rp++; + buf->_pp = buf->_rp; + *(buf->_rp) = 0; + } +} + +void ha_bufcat(ha_buffer_t* buf, ...) +{ + const char* str; + va_list ap; + + va_start(ap, buf); + + while((str = va_arg(ap, char*)) != NULL) + { + int len = strlen(str); + + buffer_bump(buf, len); + + if(ha_buferr(buf)) + return; + + /* _rpoint points to teh null */ + strcpy(buf->_rp, str); + buf->_rp += len; + } +} + +void* ha_bufmalloc(ha_buffer_t* buf, size_t sz) +{ + void* ret; + + ha_bufnext(buf); + buffer_bump(buf, sz); + buf->_rp += sz; + + if(ha_buferr(buf)) + return NULL; + + ret = (void*)ha_bufdata(buf); + ha_bufnext(buf); + + return ret; +} + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +static const char BASE64C[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char PAD64C = '='; + +void ha_bufenc64(ha_buffer_t* buf, const char* src, int len) +{ + unsigned char input[3]; + unsigned char output[4]; + size_t i; + + if(len == -1) + len = strlen(src); + + while(2 < len) + { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + len -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + buffer_bump(buf, 5); + + if(ha_buferr(buf)) + return; + + *(buf->_rp++) = BASE64C[output[0]]; + *(buf->_rp++) = BASE64C[output[1]]; + *(buf->_rp++) = BASE64C[output[2]]; + *(buf->_rp++) = BASE64C[output[3]]; + } + + /* Now we worry about padding. */ + if(0 != len) + { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for(i = 0; i < len; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + buffer_bump(buf, 5); + + if(ha_buferr(buf)) + return; + + *(buf->_rp++) = BASE64C[output[0]]; + *(buf->_rp++) = BASE64C[output[1]]; + if(len == 1) + *(buf->_rp++) = PAD64C; + else + *(buf->_rp++) = BASE64C[output[2]]; + *(buf->_rp++) = PAD64C; + } + + *(buf->_rp++) = '\0'; +} + +void ha_bufdec64(ha_buffer_t* buf, const char* src) +{ + int state; + int ch; + char* pos; + + state = 0; + + while((ch = *src++) != '\0') + { + if(isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if(ch == PAD64C) + break; + + pos = strchr(BASE64C, ch); + if(pos == 0) /* A non-base64 character. */ + break; + + buffer_bump(buf, 3); + + if(ha_buferr(buf)) + return; + + switch(state) + { + case 0: + *(buf->_rp++) = (pos - BASE64C) << 2; + state = 1; + break; + + case 1: + *(buf->_rp++) |= (pos - BASE64C) >> 4; + *(buf->_rp) = ((pos - BASE64C) & 0x0f) << 4; + state = 2; + break; + + case 2: + *(buf->_rp++) |= (pos - BASE64C) >> 2; + *(buf->_rp) = ((pos - BASE64C) & 0x03) << 6; + state = 3; + break; + + case 3: + *(buf->_rp++) |= (pos - BASE64C); + state = 0; + break; + }; + + /* TODO: Validate ending and return error if invalid somehow */ + } + + *(buf->_rp++) = '\0'; +} diff --git a/common/compat.c b/common/compat.c new file mode 100644 index 0000000..6a4b914 --- /dev/null +++ b/common/compat.c @@ -0,0 +1,30 @@ + +#include "usuals.h" +#include "compat.h" + +#ifndef HAVE_REALLOCF + +void* reallocf(void* ptr, size_t size) +{ + void* ret = realloc(ptr, size); + + if(!ret && size) + free(ptr); + + return ret; +} + +#endif + +#ifndef HAVE_STRLWR +char* strlwr(char* s) +{ + char* t = s; + while(*t) + { + *t = tolower(*t); + t++; + } + return s; +} +#endif diff --git a/common/compat.h b/common/compat.h new file mode 100644 index 0000000..c250db1 --- /dev/null +++ b/common/compat.h @@ -0,0 +1,40 @@ + + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifndef HAVE_STDARG_H +#error ERROR: Must have a working stdarg.h header +#else +#include +#endif + +#ifndef HAVE_REALLOCF +void* reallocf(void* p, size_t sz); +#endif + +#include + +/* TODO: Move this logic to configure */ +#if HAVE_ERR_MUTEX == 1 +# define HA_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK_NP +#else +# if HAVE_ERR_MUTEX == 2 +# define HA_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK +# else +# error "Need error checking mutex functionality" +# endif +#endif + +#ifndef HAVE_STRLWR +char* strlwr(char* s); +#endif + + +#endif /* _COMPAT_H_ */ diff --git a/common/hash.c b/common/hash.c new file mode 100644 index 0000000..7d67e53 --- /dev/null +++ b/common/hash.c @@ -0,0 +1,462 @@ +/* + * Originally from apache 2.0 + * Modifications for general use by + */ + +/* Copyright 2000-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "hash.h" + +#ifdef HASH_TIMESTAMP +#include +#endif + +#ifdef HASH_COPYKEYS + #define KEY_DATA(he) (void*)(((unsigned char*)(he)) + sizeof(*(he))) +#else + #define KEY_DATA(he) ((he)->key) +#endif + +/* + * The internal form of a hash table. + * + * The table is an array indexed by the hash of the key; collisions + * are resolved by hanging a linked list of hash entries off each + * element of the array. Although this is a really simple design it + * isn't too bad given that pools have a low allocation overhead. + */ + +typedef struct hash_entry_t hash_entry_t; + +struct hash_entry_t +{ + hash_entry_t* next; + unsigned int hash; +#ifndef HASH_COPYKEYS + const void* key; + size_t klen; +#endif + const void* val; +#ifdef HASH_TIMESTAMP + time_t stamp; +#endif +}; + +/* + * Data structure for iterating through a hash table. + * + * We keep a pointer to the next hash entry here to allow the current + * hash entry to be freed or otherwise mangled between calls to + * hash_next(). + */ +struct hash_index_t +{ + hash_t* ht; + hash_entry_t* ths; + hash_entry_t* next; + unsigned int index; +}; + +/* + * The size of the array is always a power of two. We use the maximum + * index rather than the size so that we can use bitwise-AND for + * modular arithmetic. + * The count of hash entries may be greater depending on the chosen + * collision rate. + */ +struct hash_t +{ + hash_entry_t** array; + hash_index_t iterator; /* For hash_first(...) */ + unsigned int count; + unsigned int max; +#ifdef HASH_COPYKEYS + unsigned int klen; +#endif +}; + +#define INITIAL_MAX 15 /* tunable == 2^n - 1 */ + + +/* + * Hash creation functions. + */ + +static hash_entry_t** alloc_array(hash_t* ht, unsigned int max) +{ + return malloc(sizeof(*(ht->array)) * (max + 1)); +} + +#ifdef HASH_COPYKEYS +hash_t* hash_create(size_t klen) +#else +hash_t* hash_create() +#endif +{ + hash_t* ht = malloc(sizeof(hash_t)); + if(ht) + { + ht->count = 0; + ht->max = INITIAL_MAX; + ht->array = alloc_array(ht, ht->max); +#ifdef HASH_COPYKEYS + ht->klen = klen; +#endif + + if(!ht->array) + { + free(ht); + return NULL; + } + } + return ht; +} + +void hash_free(hash_t* ht) +{ + hash_index_t* hi; + + for(hi = hash_first(ht); hi; hi = hash_next(hi)) + free(hi->ths); + + if(ht->array) + free(ht->array); + + free(ht); +} + +/* + * Hash iteration functions. + */ + +hash_index_t* hash_next(hash_index_t* hi) +{ + hi->ths = hi->next; + while(!hi->ths) + { + if(hi->index > hi->ht->max) + return NULL; + + hi->ths = hi->ht->array[hi->index++]; + } + hi->next = hi->ths->next; + return hi; +} + +hash_index_t* hash_first(hash_t* ht) +{ + hash_index_t* hi = &ht->iterator; + + hi->ht = ht; + hi->index = 0; + hi->ths = NULL; + hi->next = NULL; + return hash_next(hi); +} + +#ifdef HASH_COPYKEYS +void* hash_this(hash_index_t* hi, const void** key) +#else +void* hash_this(hash_index_t* hi, const void** key, size_t* klen) +#endif +{ + if(key) + *key = KEY_DATA(hi->ths); + +#ifndef HASH_COPYKEYS + if(klen) + *klen = hi->ths->klen; +#endif + + return (void*)hi->ths->val; +} + + +/* + * Expanding a hash table + */ + +static int expand_array(hash_t* ht) +{ + hash_index_t* hi; + hash_entry_t** new_array; + unsigned int new_max; + + new_max = ht->max * 2 + 1; + new_array = alloc_array(ht, new_max); + + if(!new_array) + return 0; + + for(hi = hash_first(ht); hi; hi = hash_next(hi)) + { + unsigned int i = hi->ths->hash & new_max; + hi->ths->next = new_array[i]; + new_array[i] = hi->ths; + } + + if(ht->array) + free(ht->array); + + ht->array = new_array; + ht->max = new_max; + return 1; +} + +/* + * This is where we keep the details of the hash function and control + * the maximum collision rate. + * + * If val is non-NULL it creates and initializes a new hash entry if + * there isn't already one there; it returns an updatable pointer so + * that hash entries can be removed. + */ + +#ifdef HASH_COPYKEYS +static hash_entry_t** find_entry(hash_t* ht, const void* key, const void* val) +#else +static hash_entry_t** find_entry(hash_t* ht, const void* key, size_t klen, const void* val) +#endif +{ + hash_entry_t** hep; + hash_entry_t* he; + const unsigned char* p; + unsigned int hash; + size_t i; + +#ifdef HASH_COPYKEYS + size_t klen = ht->klen; +#endif + + /* + * This is the popular `times 33' hash algorithm which is used by + * perl and also appears in Berkeley DB. This is one of the best + * known hash functions for strings because it is both computed + * very fast and distributes very well. + * + * The originator may be Dan Bernstein but the code in Berkeley DB + * cites Chris Torek as the source. The best citation I have found + * is "Chris Torek, Hash function for text in C, Usenet message + * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich + * Salz's USENIX 1992 paper about INN which can be found at + * . + * + * The magic of number 33, i.e. why it works better than many other + * constants, prime or not, has never been adequately explained by + * anyone. So I try an explanation: if one experimentally tests all + * multipliers between 1 and 256 (as I did while writing a low-level + * data structure library some time ago) one detects that even + * numbers are not useable at all. The remaining 128 odd numbers + * (except for the number 1) work more or less all equally well. + * They all distribute in an acceptable way and this way fill a hash + * table with an average percent of approx. 86%. + * + * If one compares the chi^2 values of the variants (see + * Bob Jenkins ``Hashing Frequently Asked Questions'' at + * http://burtleburtle.net/bob/hash/hashfaq.html for a description + * of chi^2), the number 33 not even has the best value. But the + * number 33 and a few other equally good numbers like 17, 31, 63, + * 127 and 129 have nevertheless a great advantage to the remaining + * numbers in the large set of possible multipliers: their multiply + * operation can be replaced by a faster operation based on just one + * shift plus either a single addition or subtraction operation. And + * because a hash function has to both distribute good _and_ has to + * be very fast to compute, those few numbers should be preferred. + * + * -- Ralf S. Engelschall + */ + hash = 0; + +#ifndef HASH_COPYKEYS + if(klen == HASH_KEY_STRING) + { + for(p = key; *p; p++) + hash = hash * 33 + *p; + + klen = p - (const unsigned char *)key; + } + else +#endif + { + for(p = key, i = klen; i; i--, p++) + hash = hash * 33 + *p; + } + + /* scan linked list */ + for(hep = &ht->array[hash & ht->max], he = *hep; + he; hep = &he->next, he = *hep) + { + if(he->hash == hash && +#ifndef HASH_COPYKEYS + he->klen == klen && +#endif + memcmp(KEY_DATA(he), key, klen) == 0) + break; + } + + if(he || !val) + return hep; + + /* add a new entry for non-NULL val */ +#ifdef HASH_COPYKEYS + he = malloc(sizeof(*he) + klen); +#else + he = malloc(sizeof(*he)); +#endif + + if(he) + { +#ifdef HASH_COPYKEYS + /* Key data points past end of entry */ + memcpy(KEY_DATA(he), key, klen); +#else + /* Key points to external data */ + he->key = key; + he->klen = klen; +#endif + + he->next = NULL; + he->hash = hash; + he->val = val; + +#ifdef HASH_TIMESTAMP + he->stamp = 0; +#endif + + *hep = he; + ht->count++; + } + + return hep; +} + +#ifdef HASH_COPYKEYS +void* hash_get(hash_t* ht, const void *key) +{ + hash_entry_t** he = find_entry(ht, key, NULL); +#else +void* hash_get(hash_t* ht, const void *key, size_t klen) +{ + hash_entry_t** he = find_entry(ht, key, klen, NULL); +#endif + + if(he && *he) + return (void*)((*he)->val); + else + return NULL; +} + +#ifdef HASH_COPYKEYS +int hash_set(hash_t* ht, const void* key, const void* val) +{ + hash_entry_t** hep = find_entry(ht, key, val); +#else +int hash_set(hash_t* ht, const void* key, size_t klen, const void* val) +{ + hash_entry_t** hep = find_entry(ht, key, klen, val); +#endif + + if(hep && *hep) + { + if(val) + { + /* replace entry */ + (*hep)->val = val; + +#ifdef HASH_TIMESTAMP + /* Update or set the timestamp */ + (*hep)->stamp = time(NULL); +#endif + + /* check that the collision rate isn't too high */ + if(ht->count > ht->max) + { + if(!expand_array(ht)) + return 0; + } + + return 1; + } + } + + return 0; +} + +#ifdef HASH_COPYKEYS +void hash_rem(hash_t* ht, const void* key) +{ + hash_entry_t** hep = find_entry(ht, key, NULL); +#else +void hash_rem(hash_t* ht, const void* key, size_t klen) +{ + hash_entry_t** hep = find_entry(ht, key, klen, NULL); +#endif + + if(hep && *hep) + { + hash_entry_t* old = *hep; + *hep = (*hep)->next; + --ht->count; + free(old); + } +} + +unsigned int hash_count(hash_t* ht) +{ + return ht->count; +} + +#ifdef HASH_TIMESTAMP +int hash_purge(hash_t* ht, time_t stamp) +{ + hash_index_t* hi; + int r = 0; + + for(hi = hash_first(ht); hi; hi = hash_next(hi)) + { + if(hi->ths->stamp < stamp) + { + /* No need to check for errors as we're deleting */ +#ifdef HASH_COPYKEYS + hash_rem(ht, KEY_DATA(hi->ths)); +#else + hash_rem(ht, hi->ths->key, hi->ths->klen); +#endif + + r++; + } + } + + return r; +} + +#ifdef HASH_COPYKEYS +void* hash_touch(hash_index_t* hi, const void** key); +{ + hash_entry_t** hep = find_entry(ht, key, NULL); +#else +void* hash_touch(hash_index_t* hi, const void** key, size_t* klen); +{ + hash_entry_t** hep = find_entry(ht, key, klen, NULL); +#endif + + if(he && *he) + ((*he)->stamp) = time(NULL); +} + +#endif /* HASH_TIMESTAMP */ diff --git a/common/hash.h b/common/hash.h new file mode 100644 index 0000000..9e8174c --- /dev/null +++ b/common/hash.h @@ -0,0 +1,171 @@ +/* + * Originally from apache 2.0 + * Modifications for general use by + */ + +/* Copyright 2000-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HASH_H__ +#define __HASH_H__ + +/* + * Options to define. You need to build both this file and + * the corresponding hash.c file with whatever options you set here + */ + +/* Keep timestamps for the entries */ +#define HASH_TIMESTAMP 1 + +/* Keep key values internally */ +#define HASH_COPYKEYS 1 + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When passing a key to hash_set or hash_get, this value can be passed to + * indicate a string-valued key, and have hash compute the length automatically. + */ +#define HASH_KEY_STRING (-1) + +/* Abstract type for hash tables. */ +typedef struct hash_t hash_t; + +/* Abstract type for scanning hash tables. */ +typedef struct hash_index_t hash_index_t; + +/* Create a hash table */ +#ifdef HASH_COPYKEYS + hash_t* hash_create(size_t klen); +#else + hash_t* hash_create(); +#endif + +/* To release all resources for a hash table */ +void hash_free(hash_t* ht); + +/** + * Associate a value with a key in a hash table. + * + * ht The hash table + * key Pointer to the key + * klen Length of the key. Can be HASH_KEY_STRING to use the string length. + * val Value to associate with the key + * + * val must not be null + */ +#ifdef HASH_COPYKEYS + int hash_set(hash_t* ht, const void* key, const void* val); +#else + int hash_set(hash_t* ht, const void* key, size_t klen, const void* val); +#endif + +/** + * Remove a value and key form the hash table + * + * ht The hash table + * key Pointer to the key + * klen Length of the key. Can be HASH_KEY_STRING to use the string length + */ +#ifdef HASH_COPYKEYS + void hash_rem(hash_t* ht, const void* key); +#else + void hash_rem(hash_t* ht, const void* key, size_t klen); +#endif + +/** + * Look up the value associated with a key in a hash table. + * + * ht The hash table + * key Pointer to the key + * klen Length of the key. Can be APR_HASH_KEY_STRING to use the string length. + * + * Returns NULL if the key is not present. + */ +#ifdef HASH_COPYKEYS + void* hash_get(hash_t* ht, const void* key); +#else + void* hash_get(hash_t* ht, const void* key, size_t klen); +#endif + +/** + * Start iterating over the entries in a hash table. + * + * ht The hash table + * + * There is no restriction on adding or deleting hash entries during + * an iteration (although the results may be unpredictable unless all you do + * is delete the current entry). Only one iteration can be in progress at once. + */ +hash_index_t* hash_first(hash_t* ht); + +/** + * Continue iterating over the entries in a hash table. + * + * hi The iteration state + * + * Returns a pointer to the updated iteration state. + * NULL if there are no more entries. + */ +hash_index_t* hash_next(hash_index_t* hi); + +/** + * Get the current entry's details from the iteration state. + * + * hi The iteration state + * key Return pointer for the pointer to the key. + * klen Return pointer for the key length. + * val Return pointer for the associated value. + * + * The return pointers should point to a variable that will be set to the + * corresponding data, or they may be NULL if the data isn't interesting. + */ +#ifdef HASH_COPYKEYS + void* hash_this(hash_index_t* hi, const void** key); +#else + void* hash_this(hash_index_t* hi, const void** key, size_t* klen); +#endif + +/** + * Purge entries before a certain timestamp + */ +#ifdef HASH_TIMESTAMP +int hash_purge(hash_t* ht, time_t stamp); + +#ifdef HASH_COPYKEYS + void hash_touch(hash_index_t* hi, const void** key); +#else + void hash_touch(hash_index_t* hi, const void** key, size_t* klen); +#endif +#endif + +/** + * Get the number of key/value pairs in the hash table. + * + * ht The hash table + * + * The number of key/value pairs in the hash table. + */ +unsigned int hash_count(hash_t* ht); + + +#ifdef __cplusplus +} +#endif + +#endif /* __HASH_H__ */ diff --git a/common/md5.c b/common/md5.c new file mode 100644 index 0000000..c909bb3 --- /dev/null +++ b/common/md5.c @@ -0,0 +1,228 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include +#include "md5.h" + +#ifdef BIG_ENDIAN +void +byteSwap(unsigned int *buf, unsigned words) +{ + unsigned char *p = (unsigned char *)buf; + + do { + *buf++ = (unsigned int)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, const unsigned char* buf, unsigned len) +{ + unsigned int t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((unsigned char *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((unsigned char *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + unsigned char *p = (unsigned char *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, (unsigned int)count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (unsigned char *)ctx->in; + count = 56; + } + memset(p, 0, (unsigned int)count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(unsigned int buf[4], unsigned int const in[16]) +{ + register unsigned int a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/common/md5.h b/common/md5.h new file mode 100644 index 0000000..62cbf2b --- /dev/null +++ b/common/md5.h @@ -0,0 +1,49 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MD5_H +#define MD5_H + +#define md5byte unsigned char + +struct MD5Context { + unsigned int buf[4]; + unsigned int bytes[2]; + unsigned int in[16]; +}; + +typedef struct MD5Context MD5_CTX; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, const md5byte* buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(unsigned int buf[4], unsigned int const in[16]); + +#ifdef __cplusplus +} +#endif + +#endif /* !MD5_H */ diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..fbce603 --- /dev/null +++ b/configure.in @@ -0,0 +1,83 @@ +dnl +dnl Copyright (c) 2004, Nate Nielsen +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions +dnl are met: +dnl +dnl * Redistributions of source code must retain the above +dnl copyright notice, this list of conditions and the +dnl following disclaimer. +dnl * Redistributions in binary form must reproduce the +dnl above copyright notice, this list of conditions and +dnl the following disclaimer in the documentation and/or +dnl other materials provided with the distribution. +dnl * The names of contributors to this software may not be +dnl used to endorse or promote products derived from this +dnl software without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +dnl FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +dnl COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +dnl INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +dnl BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +dnl OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +dnl AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +dnl OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +dnl THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +dnl DAMAGE. +dnl +dnl +dnl CONTRIBUTORS +dnl Nate Nielsen +dnl + +dnl Process this file with autoconf to produce a configure script. +AC_INIT(httpauth, 0.1, nielsen@memberwebs.com) +AM_INIT_AUTOMAKE(httpauth, 0.8.1) + +LDFLAGS="$LDFLAGS -L/usr/local/lib" +CFLAGS="$CFLAGS -I/usr/local/include -g -O0" + +AC_CONFIG_SRCDIR([daemon/httpauthd.c]) +AM_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET + +# Check for libraries +AC_CHECK_LIB([pthread], [pthread_create], , + [ echo "ERROR: Pthread libraries required."; exit 1] ) + +# Check for libraries +AC_CHECK_LIB([crypt], [crypt], , + [ echo "ERROR: Crypt library required."; exit 1] ) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([unistd.h stdio.h stddef.h fcntl.h stdlib.h assert.h errno.h stdarg.h err.h string.h], , + [echo "ERROR: Required C header missing"; exit 1]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK_NP, [AC_DEFINE(HAVE_ERR_MUTEX, 1, "Error Mutex Type")], + [AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK, [AC_DEFINE(HAVE_ERR_MUTEX, 2)], + [echo "ERROR: Missing error checking mutex functionality in pthread.h"], + [ #include ])], [ #include ]) + + +# Required Functions +AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower], , + [echo "ERROR: Required function missing"; exit 1]) +AC_CHECK_FUNCS([strlwr]) + +AC_CONFIG_FILES([Makefile daemon/Makefile]) +AC_OUTPUT diff --git a/daemon/.cvsignore b/daemon/.cvsignore new file mode 100644 index 0000000..b89d4b4 --- /dev/null +++ b/daemon/.cvsignore @@ -0,0 +1,4 @@ +.deps +httpauthd +Makefile +Makefile.in diff --git a/daemon/Makefile.am b/daemon/Makefile.am new file mode 100644 index 0000000..4525edf --- /dev/null +++ b/daemon/Makefile.am @@ -0,0 +1,13 @@ + +bin_PROGRAMS = httpauthd + +httpauthd_SOURCES = httpauthd.c httpauthd.h usuals.h compat.h compat.c \ + buffer.c misc.c basic.c ntlm.c hash.c hash.h ntlmssp.h ntlmssp.c \ + md5.c md5.h \ + smblib/smblib.c smblib/smblib-util.c smblib/file.c smblib/smb-errors.c smblib/exper.c smblib/smblib-api.c \ + rfcnb/rfcnb-io.c rfcnb/rfcnb-util.c rfcnb/session.c + +httpauthd_CFLAGS = -DLinux + +# man_MANS = rtfm.1 +EXTRA_DIST = protocol.txt smblib rfcnb diff --git a/daemon/basic.c b/daemon/basic.c new file mode 100644 index 0000000..a8325af --- /dev/null +++ b/daemon/basic.c @@ -0,0 +1,243 @@ + +/* On some linux this is required to get crypt to show itself */ +#define _XOPEN_SOURCE + +#include "usuals.h" +#include "httpauthd.h" +#include "defaults.h" + +#include +#include +#include + +#define BASIC_MAXLINE 128 + +/* This needs to be the same as an MD5 hash length */ +#define BASIC_HASH_KEY_LEN 16 +#define BASIC_ESTABLISHED (void*)1 + +/* ------------------------------------------------------------------------------- + * Structures + */ + +typedef struct basic_context +{ + const char* filename; /* The file name with the user names */ + const char* realm; /* The realm for basic authentication */ + + /* Context ----------------------------------------------------------- */ + hash_t* established; /* Established connections */ +} +basic_context_t; + +/* ------------------------------------------------------------------------------- + * Handler Functions + */ + +int basic_config(ha_context_t* context, const char* name, const char* value) +{ + basic_context_t* ctx = (basic_context_t*)(context.data); + + if(strcmp(name, "basicfile") == 0) + { + ctx->filename = value; + return HA_OK; + } + + else if(strcmp(name, "realm") == 0) + { + ctx->realm = value; + return HA_OK; + } + + return HA_FALSE; +} + +int basic_init(ha_context_t* context) +{ + /* Don't do global init */ + if(!context) + return HA_OK; + + basic_context_t* ctx = (basic_context_t*)(context.data); + int fd; + + /* Make sure there are some types of authentication we can do */ + if(!(context->types & HA_TYPE_BASIC)) + { + ha_messagex(LOG_ERR, "Basic module configured, but does not implement any " + "configured authentication type."); + return HA_ERROR; + } + + /* Check to make sure the file exists */ + if(!ctx->filename) + { + ha_messagex(LOG_ERR, "Basic configuration incomplete. " + "Must have a BasicFile configured."); + return HA_ERROR; + } + + fd = open(ctx->filename, O_RDONLY); + if(fd == -1) + { + ha_message(LOG_ERR, "can't open file for basic authentication: %s", ctx->filename); + return HA_ERROR; + } + + close(fd); + + /* Initialize our cache table */ + if(!(ctx->established = hash_create(BASIC_HASH_KEY_LEN))) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + return HA_OK; +} + +int basic_process(ha_context_t* context, ha_request_t* req, + ha_response_t* resp, ha_buffer_t* buf) +{ + basic_context_t* ctx = (basic_context_t*)(context.data); + const char* header; + char* t; + int found = 0; + ha_basic_header_t basic; + + memset(&basic, 0, sizeof(basic)); + + ha_lock(NULL); + + /* Purge the cache */ + hash_purge(ctx->established, time(NULL) - context->timeout); + + ha_unlock(NULL); + + + /* + * Init checked and made sure basic auth is enabled, so we + * can take that for granted here. + */ + + header = ha_getheader(req, "Authorization", BASIC_PREFIX); + if(header) + { + if(ha_parsebasic(header, buf, &basic) == HA_ERROR) + return HA_ERROR; + + /* Check and see if this connection is in the cache */ + ha_lock(NULL); + + if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED) + { + hash_touch(ctx->established, basic.key); + found = 1; + } + + ha_unlock(NULL); + } + + + /* If we have a user name and password that wasn't in the cache */ + if(!found && basic.user && basic.user[0] && + basic.password && basic.password[0]) + { + FILE* f; + char line[BASIC_MAXLINE]; + + f = fopen(ctx->filename, "r"); + if(!f) + { + ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename); + resp->code = HA_SERVER_ERROR; + return HA_FALSE; + } + + /* + * Note: There should be no returns or jumps between + * this point and the closing of the file below. + */ + + /* Now look through the whole file */ + while(!feof(f)) + { + fgets(line, BASIC_MAXLINE, f); + + if(ferror(f)) + ha_message(LOG_ERR, "error reading basic password file"); + + t = strchr(line, ':'); + if(t) + { + /* Split the line */ + *t = 0; + t++; + + /* Check the user */ + if(strcmp(line, basic.user) == 0) + { + /* Not sure if crypt is thread safe so we lock */ + ha_lock(); + + /* Check the password */ + if(strcmp(crypt(basic.password, t), t) == 0) + found = 1; + + ha_unlock(); + + if(found) + break; + } + } + } + + fclose(f); + } + + /* If we found a user then tell them what it was */ + if(found) + { + resp->code = HA_SERVER_ACCEPT; + resp->detail = basic.user; + + /* We put this connection into the successful connections */ + if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED)) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + } + + /* Otherwise make the browser authenticate */ + else + { + resp->code = HA_SERVER_DECLINE; + + ha_bufnext(buf); + ha_bufcat(buf, "BASIC realm=\"", ctx->realm ? ctx->realm : "", + "\"", NULL); + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); + } + + return HA_OK; +} + + +/* ------------------------------------------------------------------------------- + * Handler Definition + */ + +ha_handler_t basic_handler = +{ + "Basic", /* The type */ + basic_init, /* Initialization function */ + basic_destroy, /* Uninitialization routine */ + basic_config, /* Config routine */ + basic_process, /* Processing routine */ + NULL, /* A default context */ + sizeof(basic_context_t) /* Size of the context */ +}; + diff --git a/daemon/defaults.h b/daemon/defaults.h new file mode 100644 index 0000000..bfcd9f4 --- /dev/null +++ b/daemon/defaults.h @@ -0,0 +1,9 @@ + +#ifndef __DEFAULTS_H__ +#define __DEFAULTS_H__ + +#define DEFAULT_PENDING_MAX 16 +#define DEFAULT_PENDING_TIMEOUT 60 +#define DEFAULT_TIMEOUT 900 + +#endif /* __DEFAULTS_H__ */ \ No newline at end of file diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c new file mode 100644 index 0000000..8af5d7f --- /dev/null +++ b/daemon/httpauthd.c @@ -0,0 +1,1031 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usuals.h" +#include "httpauthd.h" + +/* ----------------------------------------------------------------------- + * Handlers Registered Here + */ + +extern ha_handler_t basic_handler; + +/* This is the list of all available handlers */ +const ha_handler_t* g_handlerlist[] = +{ + &basic_handler +}; + +typedef struct httpauth_loaded +{ + ha_context_t ctx; + struct httpauth_loaded* next; +} +httpauth_loaded_t; + +/* The list of handlers in use */ +httpauth_loaded_t* g_handlers = NULL; + +/* ----------------------------------------------------------------------- + * Structures and Constants + */ + +/* A command definition. Used in parsing */ +typedef struct httpauth_command +{ + const char* name; + int code; + int args; + const char** headers; +} +httpauth_command_t; + +/* The various valid headers for the auth command */ +const char* kAuthHeaders[] = +{ + "Authorization", + "Proxy-Authorization", + NULL +}; + +/* The command definitions */ +const httpauth_command_t kCommands[] = +{ + { "auth", REQTYPE_AUTH, 2, kAuthHeaders }, + { "quit", REQTYPE_QUIT, 0, 0 }, + { NULL, -1, -1 } +}; + +typedef struct httpauth_thread +{ + pthread_t tid; + int fd; +} +httpauth_thread_t; + +/* ----------------------------------------------------------------------- + * Default Settings + */ + +#define DEFAULT_CONFIG "/usr/local/etc/httpauthd.conf" +#define DEFAULT_SOCKET "/var/run/httpauthd.sock" +#define DEFAULT_MAXTHREADS 32 + + +/* ----------------------------------------------------------------------- + * Globals + */ + +int g_daemonized = 0; /* Currently running as a daemon */ +int g_debugging = 0; /* In debug mode */ +const char* g_socket = DEFAULT_SOCKET; /* The socket to communicate on */ +int g_maxthreads = DEFAULT_MAXTHREADS; /* The maximum number of threads */ + +/* For main loop and signal handlers */ +int g_quit = 0; + +/* The main thread */ +pthread_t g_mainthread; + +/* The main mutex */ +pthread_mutex_t g_mutex; +pthread_mutexattr_t g_mutexattr; + +/* ----------------------------------------------------------------------- + * Forward Declarations + */ + +int usage(); +void* httpauth_thread(void* arg); +int httpauth_processor(int ifd, int ofd); +int process_auth(ha_request_t* req, ha_response_t* resp, + ha_buffer_t* outb); +int config_parse(const char* file, ha_buffer_t* buf); + + +/* ----------------------------------------------------------------------- + * Main Program + */ + +int main(int argc, char* argv[]) +{ + const char* conf = DEFAULT_CONFIG; + httpauth_thread_t* threads = NULL; + const httpauth_loaded_t* h; + int daemonize = 1; + ha_buffer_t cbuf; + int r, i, sock; + int ch = 0; + + /* Keep note of the main thread */ + g_mainthread = pthread_self(); + + /* Create the main mutex */ + if(pthread_mutexattr_init(&g_mutexattr) != 0 || + pthread_mutexattr_settype(&g_mutexattr, HA_MUTEX_TYPE) || + pthread_mutex_init(&g_mutex, &g_mutexattr) != 0) + errx(1, "threading problem. can't create mutex"); + + /* Parse the arguments nicely */ + while((ch = getopt(argc, argv, "df:X")) != -1) + { + switch(ch) + { + /* Don't daemonize */ + case 'd': + daemonize = 0; + break; + + /* The configuration file */ + case 'f': + conf = optarg; + break; + + /* Process console input instead */ + case 'X': + g_debugging = 1; + break; + + /* Usage information */ + case '?': + default: + return usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if(argc != 0) + return usage(); + + + /* From here on out we need to quit in an orderly fashion */ + + /* Run global initialization on all handlers */ + for(i = 0; i < countof(g_handlerlist); i++) + { + if(g_handlerlist[i]->f_init) + { + if((r = (g_handlerlist[i]->f_init)(NULL)) == -1) + goto finally; + } + } + + /* Initialize our configuration buffer */ + ha_bufinit(&cbuf); + + + /* Parse the configuration */ + config_parse(conf, &cbuf); + + + if(!g_debugging) + { + struct sockaddr_un sau; + + /* Create the thread buffers */ + threads = (httpauth_thread_t*)malloc(sizeof(httpauth_thread_t) * g_maxthreads); + if(!threads) + errx(1, "out of memory"); + + /* Create the socket */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if(sock < 0) + err(1, "couldn't open socket"); + + + /* Unlink the socket file if it exists */ + /* TODO: Is this safe? */ + unlink(g_socket); + + /* Setup the socket */ + strncmp(sau.sun_path, g_socket, sizeof(sau.sun_path)); + sau.sun_path[sizeof(sau.sun_path) - 1] = 0; + + /* Bind to the socket */ + if(bind(sock, (struct sockaddr*)&sau, + sizeof(sau) - (sizeof(sau.sun_path) - strlen(sau.sun_path)))) + err(1, "couldn't bind to socket: %s", sau.sun_path); + + + /* Let 5 connections queue up */ + if(listen(sock, 5) != 0) + err(1, "couldn't listen on socket"); + } + + + /* TODO: Enable signal processing here */ + + /* Initialize all the handlers */ + for(h = g_handlers; h; h = h->next) + { + if(h->ctx.handler->f_init) + { + if((r = (h->ctx.handler->f_init)(&(h->ctx))) == -1) + goto finally; + } + } + + + /* This is for debugging the internal processes */ + if(g_debugging) + { + r = httpauth_processor(0, 1); + goto finally; + } + + + /* This is the daemon section of the code */ + else + { + /* TODO: Daemonize here, and disconnect from terminal */ + + /* Open the system log */ + openlog("httpauthd", 0, LOG_AUTHPRIV); + g_daemonized = 1; + + + /* Now loop and accept the connections */ + while(!g_quit) + { + int fd; + + /* TODO: A nice way to break out of the loop here */ + + fd = accept(sock, 0, 0); + if(fd == -1) + { + switch(errno) + { + case EAGAIN: + case EINTR: + break; + + case ECONNABORTED: + case EPROTO: + ha_message(LOG_ERR, "couldn't accept a connection"); + break; + + default: + ha_message(LOG_ERR, "couldn't accept a connection"); + g_quit = 1; + break; + }; + + continue; + } + + /* Look for thread and also clean up others */ + for(i = 0; i < g_maxthreads; i++) + { + /* Clean up quit threads */ + if(threads[i].tid != 0) + { + if(threads[i].fd == 0) + { + pthread_join(threads[i].tid, NULL); + threads[i].tid = 0; + } + } + + /* Start a new thread if neccessary */ + if(fd != 0 && threads[i].tid == 0) + { + threads[i].fd = fd; + r = pthread_create(&(threads[i].tid), NULL, httpauth_thread, + (void*)(threads + i)); + if(r != 0) + { + errno = r; + ha_message(LOG_ERR, "couldn't create thread"); + g_quit = 1; + break; + } + + fd = 0; + } + } + + /* Check to make sure we have a thread */ + if(fd != 0) + { + ha_messagex(LOG_ERR, "too many connections open (max %d)", g_maxthreads); + httpauth_respond(fd, HA_SERVER_ERROR, "too many connections"); + shutdown(fd, SHUT_RDWR); + } + } + + /* TODO: Quit all threads here */ + + r = 0; + } + +finally: + + /* Uninitialize all the handlers */ + for(h = g_handlers; h; h = h->next) + { + if(h->ctx.handler->f_destroy) + (h->ctx.handler->f_destroy)(&(h->ctx)); + } + + /* Run global destroy for all handlers */ + for(i = 0; i < countof(g_handlerlist); i++) + { + if(g_handlerlist[i]->f_destroy) + (g_handlerlist[i]->f_destroy)(NULL); + } + + /* Clean up memory and stuff */ + ha_buffree(&cbuf); + + + /* Close the mutex */ + pthread_mutex_destroy(&g_mutex); + pthread_mutexattr_destroy(&g_mutexattr); + return r == -1 ? 1 : 0; +} + +int usage() +{ + fprintf(stderr, "usage: httpauthd [-dX] [-f conffile]\n"); + return 2; +} + +void* httpauth_thread(void* arg) +{ + int fd = (int)arg; + int r = httpauth_processor(fd, fd); + return (void*)r; +} + +/* ----------------------------------------------------------------------- + * Command Parsing and Handling + */ + +int httpauth_read(int ifd, ha_request_t* req, + ha_buffer_t* buf) +{ + const httpauth_command_t* cmd; + char* t; + int i, r; + int more = 1; + + /* Clean up the request header */ + memset(req, 0, sizeof(*req)); + req->type = -1; + + /* This guarantees a bit of memory allocated, and resets buffer */ + ha_bufreset(buf); + + r = ha_readline(ifd, buf); + if(r == -1) + return -1; + + /* Check if this is the last line */ + if(r == 0) + more = 0; + + /* Check to see if we got anything */ + if(ha_buflen(buf) == 0) + { + req->type = REQTYPE_IGNORE; + return more; + } + + /* Find the first space in the line */ + t = ha_parseword(buf, " \t"); + + if(t) + { + /* Figure out which command it is */ + for(cmd = kCommands; cmd->name; cmd++) + { + if(strcasecmp(t, cmd->name) == 0) + { + req->type = cmd->code; + break; + } + } + } + + /* Check for invalid command */ + if(req->type == -1) + return more; + + /* Now parse the arguments if any */ + for(i = 0; i < cmd->args; i++) + req->args[i] = ha_parseword(buf, " \t"); + + + /* Now skip anything else we have in the buffer */ + ha_bufskip(buf); + + + /* If we need headers, then read them now */ + if(cmd->headers) + { + const char** head; /* For iterating through valid headers */ + int valid = 0; /* The last header was valid */ + i = 0; /* The header we're working with */ + + for(;;) + { + /* Make sure we have more data */ + if(!more) + break; + + r = ha_readline(ifd, buf); + if(r == -1) + return -1; + + /* Check if this is the last line */ + if(r == 0) + more = 0; + + /* An empty line is the end of the headers */ + if(ha_buflen(buf) == 0) + break; + + /* Check if the header starts with a space */ + if(isspace(ha_bufchar(buf))) + { + /* Skip all the spaces */ + while(ha_buflen(buf) > 0 && isspace(ha_bufchar(buf))) + ha_bufeat(buf); + + /* An empty line is the end of the headers + even if that line has spaces on it */ + if(ha_buflen(buf) == 0) + break; + + /* A header that has data on it but started + with a space continues the previous header */ + if(valid && i > 0) + { + t = ha_parseline(buf, 0); + if(t) + { + char* t2 = (char*)req->headers[i - 1].data + strlen(req->headers[i - 1].data); + + /* Fill the area between the end of the last + valid header and this with spaces */ + memset(t2, ' ', t - t2); + } + } + } + else + { + if(i < MAX_HEADERS) + { + t = ha_parseword(buf, ":"); + + if(t) + { + for(head = cmd->headers; ; head++) + { + if(!(*head)) + { + t = NULL; + break; + } + + if(strcasecmp(t, *head) == 0) + break; + } + } + + if(t) + { + req->headers[i].name = t; + req->headers[i].data = ha_parseline(buf, 1); + i++; + } + + valid = (t != NULL) ? 1 : 0; + } + } + + ha_bufskip(buf); + } + } + + return more; +} + +int write_data(int ofd, const char* data) +{ + int r; + + while(*data != 0) + { + r = write(ofd, data, strlen(data)); + + if(r > 0) + data += r; + + else if(r == -1) + { + if(errno == EAGAIN || errno == EINTR) + continue; + + /* The other end closed. no message */ + if(errno != EPIPE) + ha_message(LOG_ERR, "couldn't write data"); + + return -1; + } + } + + return 0; +} + +int httpauth_respond(int ofd, int code, const char* msg) +{ + const char* t; + char num[16]; + + sprintf(num, "%d", code); + + if(write_data(ofd, num) == -1 || + write_data(ofd, " ") == -1) + return -1; + + switch(code) + { + case HA_SERVER_ERROR: + t = "Internal Error "; + break; + case HA_SERVER_BADREQ: + t = "Bad Request "; + break; + case HA_SERVER_DECLINE: + t = "Unauthorized "; + break; + default: + t = NULL; + break; + }; + + if(t && write_data(ofd, t) == -1) + return -1; + + if(msg) + { + if(write_data(ofd, "[") == -1 || + write_data(ofd, msg) == -1 || + write_data(ofd, "]") == -1) + return -1; + } + + return write_data(ofd, "\n"); +} + +const char kHeaderDelimiter[] = ": "; + +int httpauth_write(int ofd, ha_response_t* resp) +{ + int i; + int wrote = 0; + + if(httpauth_respond(ofd, resp->code, resp->detail) == -1) + return -1; + + for(i = 0; i < MAX_HEADERS; i++) + { + if(resp->headers[i].name) + { + if(write_data(ofd, resp->headers[i].name) == -1 || + write_data(ofd, kHeaderDelimiter) == -1 || + write_data(ofd, resp->headers[i].data) == -1 || + write_data(ofd, "\n") == -1) + return -1; + + wrote = 1; + } + } + + if(wrote && write_data(ofd, "\n") == -1) + return -1; + + return 0; +} + +int httpauth_ready(int ofd, ha_buffer_t* buf) +{ + const char* t; + httpauth_loaded_t* h; + + /* We send a ready banner to our client */ + ha_bufnext(buf); + + for(h = g_handlers; h; h = h->next) + ha_bufcat(buf, (h == g_handlers) ? "" : " ", + h->ctx.name, NULL); + + if(ha_buferr(buf)) + return httpauth_respond(ofd, HA_SERVER_ERROR, NULL); + else + return httpauth_respond(ofd, HA_SERVER_READY, ha_bufdata(buf)); +} + +int httpauth_processor(int ifd, int ofd) +{ + ha_buffer_t inb; + ha_buffer_t outb; + ha_request_t req; + ha_response_t resp; + int result = -1; + int r; + + /* Initialize the memory buffers */ + ha_bufinit(&inb); + ha_bufinit(&outb); + + if(httpauth_ready(ofd, &outb) == -1) + { + result = 1; + goto finally; + } + + /* Now loop and handle the commands */ + while(result == -1) + { + ha_bufreset(&outb); + ha_bufreset(&inb); + + r = httpauth_read(ifd, &req, &inb); + if(r == -1 || ha_buferr(&inb)) + { + httpauth_respond(ofd, HA_SERVER_ERROR, NULL); + result = 1; + continue; + } + + if(r == 0) + result = 0; + + switch(req.type) + { + case REQTYPE_AUTH: + + r = process_auth(&req, &resp, &outb); + if(r == -1 || ha_buferr(&outb)) + { + httpauth_respond(ofd, HA_SERVER_ERROR, NULL); + result = 1; + continue; + } + + if(httpauth_write(ofd, &resp) == -1) + { + result = 1; + continue; + } + + break; + + + case REQTYPE_QUIT: + result = 0; + break; + + + default: + if(httpauth_respond(ofd, HA_SERVER_BADREQ, "Unknown command") == -1) + { + result = -1; + continue; + } + + break; + }; + } + + if(ifd == ofd) + shutdown(ofd, SHUT_RDWR); + else + close(ofd); + +finally: + ha_buffree(&outb); + ha_buffree(&inb); + + return result; +} + +int process_auth(ha_request_t* req, ha_response_t* resp, + ha_buffer_t* outb) +{ + const httpauth_loaded_t* h; + + /* Clear out our response */ + memset(resp, 0, sizeof(*resp)); + + /* Check our connection argument */ + if(!req->args[1] || !(req->args[1][0])) + { + ha_messagex(LOG_ERR, "Missing connection ID in request"); + resp->detail = "Missing connection ID"; + resp->code = HA_SERVER_BADREQ; + return 0; + } + + /* Find a handler for this type */ + for(h = g_handlers; h; h = h->next) + { + if(strcasecmp(h->ctx.name, req->args[0]) == 0) + { + /* Now let the handler handle it */ + if(h->ctx.handler->f_process) + return (h->ctx.handler->f_process)(&(h->ctx), req, resp, outb); + + return 0; + } + } + + ha_messagex(LOG_ERR, "Unknown authentication type: %s", req->args[0]); + resp->detail = "Unknown authentication type"; + resp->code = HA_SERVER_BADREQ; + return -1; +} + +/* ----------------------------------------------------------------------- + * Configuration + */ + +ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias, + const ha_handler_t* handler, const ha_context_t* defaults) +{ + httpauth_loaded_t* loaded; + int len = sizeof(httpauth_loaded_t) + handler->context_size; + + loaded = (httpauth_loaded_t*)ha_bufmalloc(buf, len); + if(!loaded) + errx(1, "out of memory"); + + memset(loaded, 0, len); + memcpy(&(loaded->ctx), defaults, sizeof(ha_context_t)); + + if(handler->context_size) + { + void* mem = ((unsigned char*)loaded) + sizeof(httpauth_loaded_t); + + /* Initialize the defaults properly */ + if(handler->context_default) + memcpy(mem, handler->context_default, handler->context_size); + + loaded->ctx.data = mem; + } + + else + { + loaded->ctx.data = NULL; + } + + loaded->ctx.name = (char*)alias; + loaded->ctx.handler = handler; + + if(!g_handlers) + { + g_handlers = loaded; + } + else + { + httpauth_loaded_t* l = g_handlers; + + for(;;) + { + if(strcasecmp(alias, l->ctx.name) == 0) + errx(1, "duplicate handler section for '%s'", alias); + + if(!(l->next)) + break; + + l = l->next; + } + + l->next = loaded; + } + + return &(loaded->ctx); +} + + +int config_parse(const char* file, ha_buffer_t* buf) +{ + ha_context_t defaults; + ha_context_t* ctx = NULL; + int line = 0; + int fd; + const char** t; + char* name; + const char* value; + int more = 1; + int recog; + int r, i; + + /* Open the configuration file */ + fd = open(file, O_RDONLY); + if(fd == -1) + err(1, "couldn't open configuration file: %s", file); + + /* These are the default options for the contexts */ + memset(&defaults, 0, sizeof(defaults)); + defaults.types = 0xFFFFFFFF; /* All types by default */ + defaults.timeout = DEFAULT_TIMEOUT; /* Timeout for cache */ + + ha_bufreset(buf); + + /* Read each line and process */ + while(more) + { + ha_bufskip(buf); + + if((more = ha_readline(fd, buf)) == -1) + return -1; + + line++; + + /* Eat all white space at beginning of line */ + while(ha_buflen(buf) && isspace(ha_bufchar(buf))) + ha_bufeat(buf); + + /* Skip blank lines */ + if(ha_buflen(buf) == 0) + continue; + + /* Skip comment lines */ + if(ha_bufchar(buf) == '#') + continue; + + /* Check for a handler */ + if(ha_bufchar(buf) == '[') + { + const ha_handler_t* handler = NULL; + const char* x; + + ha_bufeat(buf); + name = ha_parseline(buf, 1); + + if(!name || name[strlen(name) - 1] != ']') + errx(1, "configuration section invalid (line %d)", line); + + + /* remove the bracket */ + name[strlen(name) - 1] = 0; + + + /* Look for a handler with this type */ + for(i = 0; i < countof(g_handlerlist); i++) + { + if(g_handlerlist[i] && g_handlerlist[i]->type && + strcasecmp(name, g_handlerlist[i]->type) == 0) + { + handler = g_handlerlist[i]; + break; + } + } + + if(handler == NULL) + errx(1, "unknown handler type '%s' (line %d)", name, line); + + /* If we had a last handler then add it to handlers */ + loaded = config_addhandler(buf, name, handler, defaults); + + /* Rest doesn't apply to handler headers */ + continue; + } + + + /* Parse out the name */ + name = ha_parseword(buf, ":"); + if(!name || !name[0]) + errx(1, "configuration file invalid (line %d)", line); + + strlwr(name); + + /* And get the rest of the line */ + value = ha_parseline(buf, 1); + if(value == NULL) + errx(1, "configuration missing value at (line %d)", line); + + + recog = 0; + + /* Is this the global section? */ + if(!ctx) + { + /* Look and see if that's a name we want */ + if(strcmp("socket", name) == 0) + { + g_socket = value; + recog = 1; + } + + else if(strcmp("maxthreads", name) == 0) + { + if(ha_confint(value, 1, 256, &g_maxthreads) == -1) + errx(1, "invalid value for '%s'. must be a number between 1 and 256", name); + recog = 1; + } + + } + + /* Otherwise we're in a handler */ + else + { + if(strcmp("alias", name) == 0) + { + loaded->alias = value; + recog = 1; + } + + if(loaded->ctx.handler->f_config) + { + r = (loaded->ctx.handler->f_config)(&(loaded->ctx), name, value); + if(r == -1) + return -1; + + if(!recog && r) + recog = 1; + } + } + + /* Options that are legal in both global and internal sections */ + if(!recog) + { + if(strcmp(name, "cachetimeout") == 0) + { + int v; + if(ha_confint(name, value, 0, 86400, &v) == HA_ERROR) + exit(1); /* Message already printed */ + + (ctx ? ctx : &defaults)->timeout = v; + recog = 1; + } + + else if(strcmp(name, "authtypes") == 0) + { + int types = 0; + char* t; + + strlwr(value); + + /* Split the line into tokens at the spaces */ + while(*value) + { + while(*value && isspace(*value)) + value++; + + t = value; + + while(*t && !isspace(*t)) + t++; + + if(strncmp(value, "basic", 5) == 0) + types |= HA_TYPE_BASIC; + + else if(strncmp(value, "digest", 6) == 0) + types |= HA_TYPE_DIGEST; + + else if(strncmp(value, "ntlm", 4) == 0) + types |= HA_TYPE_NTLM; + + else + errx(1, "invalid type for '%s': %s (line %d)", name, value, line); + + value = t; + } + + if(types == 0) + errx(1, "no authentication types for '%s' (line %d)", name, line); + + (ctx ? ctx : &defaults)->types = types; + recog = 1; + } + } + + if(!recog) + errx(1, "unrecognized configuration setting '%s' (line %d)", name, line); + } + + if(!g_handlers) + ha_messagex(LOG_INFO, "no handlers found in configuration file"); + + return 0; +} diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h new file mode 100644 index 0000000..536dfdc --- /dev/null +++ b/daemon/httpauthd.h @@ -0,0 +1,328 @@ + +#ifndef __HTTPAUTHD_H__ +#define __HTTPAUTHD_H__ + +/* ----------------------------------------------------------------------- + * Memory Buffers + */ + +/* A buffer which owns memory */ +typedef struct ha_buffer +{ + int _al; + char* _dt; + char* _pp; + char* _rp; +} +ha_buffer_t; + +void ha_bufinit(ha_buffer_t* buf); +void ha_buffree(ha_buffer_t* buf); +void ha_bufreset(ha_buffer_t* buf); + +/* Buffer input functions */ +int ha_readline(int fd, ha_buffer_t* buf); +char* ha_parseline(ha_buffer_t* buf, int trim); +char* ha_parseword(ha_buffer_t* buf, const char* delims); + +/* Buffer output functions */ +void ha_bufnext(ha_buffer_t* buf); +void ha_bufcat(ha_buffer_t* buf, ...); + +/* Buffer encoding functions */ +void ha_bufenc64(ha_buffer_t* buf, const const char* src, size_t len); +void ha_bufdec64(ha_buffer_t* buf, const char* src, size_t len); + +void ha_bufenchex(ha_buffer_t* buf, const unsigned char* src, size_t len); +void ha_bufdechex(ha_buffer_t* buf, const char* src, size_t len); + +/* Memory allocation functions */ +void* ha_bufmalloc(ha_buffer_t* buf, size_t sz); + +#define ha_bufskip(buf) \ + ((buf)->_pp = (buf)->_rp) + +#define ha_buflen(buf) \ + ((buf)->_rp - (buf)->_pp) + +#define ha_bufchar(buf) \ + ((!ha_buferr(buf) && ha_buflen(buf) > 0) ? *((buf)->_pp) : '\0' ) + +#define ha_bufdata(buf) \ + ((buf)->_pp) + +#define ha_bufeat(buf) \ + ((!ha_buferr(buf) && ha_buflen(buf) > 0) ? ++((buf)->_pp) : (buf)->_pp) + +#define ha_buferr(buf) \ + ((buf)->_dt == NULL) + + +/* ----------------------------------------------------------------------- + * HTTP Auth Handlers + */ + +typedef struct ha_context_t; + +/* + * This function initializes the handler. It gets called + * after the configuration gets loaded so if a config func + * is registered it'll get called before this. + */ +typedef int (*auth_init_t)(ha_context_t* ctx); + +/* + * This function is called when the app exits. All threads + * should have completed at this point, so it's not necessary + * to be thread safe in here + */ +typedef void (*auth_destroy_t)(ha_context_t* ctx); + +/* + * Called once for each configuration parameter. This is + * called before the initialization function. 'name' will + * always be lower case. White space will always be trimmed + * from the value. + */ +typedef int (*auth_config_t)(ha_context_t* ctx, const char* name, const char* value); + +/* + * Called for each authentication request that is designated + * for this handler. Note that all data access in this + * function must be thread-safe. + */ +typedef int (*auth_process_t)(ha_context_t* ctx, ha_request_t* req, + ha_response_t* resp, ha_buffer_t* mem); + +/* An authentication handler */ +typedef struct ha_handler +{ + const char* type; + auth_init_t f_init; /* #1 Called to initialize handler */ + auth_destroy_t f_destroy; /* #3 Called when exiting */ + auth_config_t f_config; /* #0 Called for each config param */ + auth_process_t f_process; /* #2 Called for each auth request */ + const void* context_default; /* The default context */ + size_t context_size; /* Bytes of extra context needed */ +} +ha_handler_t; + +/* + * OK signifies that things went according to plan. Return + * this even if authentication fails (send auth to user) + * unless something unexpected happens. + */ +#define HA_OK 1 + +/* + * FALSE signifies that we couldn't process but it wasn't + * an error. + */ +#define HA_FALSE 0 + +/* + * ERROR means a bad error happened which will kill the + * current processing thread. Examples are out of memory + * errors or the like. + */ +#define HA_ERROR -1 + + +struct ha_options; + +/* Context passed to the handler functions below */ +typdef struct ha_context +{ + const char* name; /* A name assigned by the configuration file */ + ha_handler_t* handler; /* The original handler structure */ + unsigned int types; /* The types of authentication allowed */ + int timeout; /* Timeout for cached connections */ + void* data; /* Handler specific data */ +} +ha_context_t; + + +/* ----------------------------------------------------------------------- + * HTTP Auth Structures and Constants + */ + +/* + * The maximum number of commands in any httpauth + * command. This is defined by the protocol. There + * should be no need to change it unless we're + * adding or removing commands + */ +#define MAX_ARGS 2 + +/* + * The maximum number of pertinent headers to read + * from the client. If you need to add valid headers + * make sure to update this number *and* the list + * of valid headers in httpauthd.c + */ + +#define MAX_HEADERS 2 + +/* + * The maximum number of handlers. If you add + * handlers make sure to update this number. + */ +#define MAX_HANDLERS 4 + + +/* A single header in memory */ +typedef struct ha_header +{ + const char* name; + const char* data; +} +ha_header_t; + +/* The various command codes */ +#define REQTYPE_IGNORE 0 +#define REQTYPE_QUIT 1 +#define REQTYPE_AUTH 2 + +/* A single request from client */ +typedef struct ha_request +{ + int type; + const char* args[MAX_ARGS]; + ha_header_t headers[MAX_HEADERS]; +} +ha_request_t; + +/* The various response codes */ +#define HA_SERVER_READY 100 +#define HA_SERVER_ACCEPT 200 +#define HA_SERVER_DECLINE 401 +#define HA_SERVER_BADREQ 402 +#define HA_SERVER_ERROR 500 +#define HA_SERVER_BUSY 500 + +/* A response to the client */ +typedef struct ha_response +{ + int code; + const char* detail; + ha_header_t headers[MAX_HEADERS]; +} +ha_response_t; + +/* Request functions */ +ha_header_t* ha_findheader(ha_request_t* req, const char* name); +const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix); + +/* Response functions */ +void ha_addheader(ha_response_t* resp, const char* name, const char* data); + +/* Configuration functions */ +int ha_confbool(const char* name, const char* conf, int* value); +int ha_confint(const char* name, const char* conf, int min, int max, int* value); + +/* A little hashing */ +#ifndef MD5_LEN + #define MD5_LEN 16 +#endif + +void ha_md5string(const char* data, unsigned char* hash); + +/* ----------------------------------------------------------------------- + * Error Handling + */ + +void ha_message(int level, const char* msg, ...); +void ha_messagex(int level, const char* msg, ...); + + +/* ----------------------------------------------------------------------- + * Authentication types + */ + + +/* The various types of authentication */ +#define HA_TYPE_BASIC 1 << 1 +#define HA_PREFIX_BASIC "Basic " + +typedef struct ha_basic_header +{ + const char* user; + const char* password; + unsigned char key[MD5_LEN]; +} +ha_basic_header_t; + +int ha_parsebasic(char* header, ha_buffer_t* buf, ha_basic_header_t* rec); + + +#define HA_TYPE_DIGEST 1 << 2 +#define HA_PREFIX_DIGEST "Digest " +#define HA_DIGEST_NONCE_LEN MD5_LEN * 2 + +/* Parsed Digest response from the client */ +typedef struct ha_digest_header +{ + const char* scheme; + const char* realm; + const char* username; + const char* nonce; + const char* uri; + const char* method; + const char* digest; + const char* algorithm; + const char* cnonce; + const char* opaque; + const char* message_qop; + const char* nc; + unsigned char key[MD5_LEN]; +} +ha_digest_header_t; + +/* Kept by the server for validating the client */ +typedef struct ha_digest_record +{ + unsigned char nonce[HA_DIGEST_NONCE_LEN]; + unsigned char userhash[MD5_LEN]; + unsigned char ha1[MD5_LEN]; + unsigned int nc; +} +ha_digest_record_t; + +int ha_digestparse(char* header, ha_buffer_t* buf, ha_digest_header_t* rec); +int ha_digestcheck(const char* realm, const char* method, const char* uri, + ha_buffer_t* buf, ha_digest_header_t* header, ha_digest_record_t* rec); + + +#define HA_TYPE_NTLM 1 << 3 +#define HA_PREFIX_NTLM "NTLM " + + +/* ----------------------------------------------------------------------- + * URI Parse Support + */ + +struct ha_uri_t +{ + /* Note: We only support HTTP uris */ + const char* user; + const char* pw; + const char* host; + unsigned short port; + const char* path; + const char* query; + const char* bookmark; +}; + + +char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf); +int ha_uriparse(const char* str, ha_uri_t* uri); + + +/* ----------------------------------------------------------------------- + * Locking + */ + +void ha_lock(); +void ha_unlock(); + +#endif /* __HTTPAUTHD_H__ */ diff --git a/daemon/ldap.c b/daemon/ldap.c new file mode 100644 index 0000000..2927b1d --- /dev/null +++ b/daemon/ldap.c @@ -0,0 +1,1114 @@ + +/* TODO: Include attribution for ideas, and code from mod_auth_digest */ + +#include "usuals.h" +#include "httpauthd.h" +#include "hash.h" +#include "defaults.h" + +#include + +/* LDAP library */ +#include + +/* ------------------------------------------------------------------------------- + * Defaults and Constants + */ + +/* This needs to be the same as an MD5 hash length */ +#define LDAP_HASH_KEY_LEN 16 +#define LDAP_ESTABLISHED (void*)1 + +/* TODO: We need to support more password types */ +#define LDAP_PW_CLEAR 0 +#define LDAP_PW_CRYPT 1 +#define LDAP_PW_MD5 2 +#define LDAP_PW_SHA 3 +#define LDAP_PW_UNKNOWN -1 + +typedef struct ldap_pw_type +{ + const char* name; + int type; +} +ldap_pw_type_t; + +static const ldap_pw_type_t kLDAPPWTypes[] = +{ + { "cleartext", LDAP_PW_CLEAR }, + { "crypt", LDAP_PW_CRYPT }, + { "md5", LDAP_PW_MD5 }, + { "sha", LDAP_PW_SHA } +}; + + +/* ------------------------------------------------------------------------------- + * Structures + */ + +/* Our hanler context */ +typedef struct ldap_context +{ + /* Settings ---------------------------------------------------------- */ + const char* servers; /* Servers to authenticate against (required) */ + const char* filter; /* Filter (either this or dnmap must be set) */ + const char* base; /* Base for the filter */ + const char* pw_attr; /* The clear password attribute */ + const char* ha1_attr; /* Password for an encrypted Digest H(A1) */ + const char* user; /* User to bind as */ + const char* password; /* Password to bind with */ + const char* realm; /* The realm to use in authentication */ + const char* dnmap; /* For mapping users to dns */ + int port; /* Port to connect to LDAP server on */ + int scope; /* Scope for filter */ + int dobind; /* Bind to do simple authentication */ + int pending_max; /* Maximum number of connections at once */ + int pending_timeout; /* Timeout for authentication (in seconds) */ + int ldap_timeout; /* Timeout for LDAP operations */ + + /* Context ----------------------------------------------------------- */ + hash_t* pending; /* Pending connections */ + hash_t* established; /* Established connections */ + LDAP** pool; /* Pool of available connections */ + int pool_mark; /* Amount of connections allocated */ +} +ldap_context_t; + + +/* The defaults for the context */ +static const ldap_defaults = +{XXXXX + NULL, NULL, "", "userPassword", NULL, NULL, NULL, "", NULL + LDAP_SCOPE_DEFAULT, 1, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT, + 30, NULL, NULL, NULL +}; + + +/* ------------------------------------------------------------------------------- + * Internal Functions + */ + +static void make_digest_ha1(unsigned char* digest, const char* user, + const char* realm, const char* password) +{ + struct MD5Context md5; + MD5_Init(&md5); + MD5_Update(&md5, user, strlen(user)); + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, realm, strlen(realm)); + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, password, strlen(pasword)); + MD5_Final(digest, &md5); +} + +static const char* make_password_md5(ha_buffer_t* buf, const char* clearpw) +{ + struct MD5Context md5; + unsigned char digest[MD5_LEN]; + + MD5_Init(&md5); + MD5_Update(&md5, clearpw, strlen(clearpw)); + MD5_Final(digest, &md5); + + ha_bufnext(buf); + ha_bufenc64(buf, digest, MD5_LEN); + return ha_bufdata(buf); +} + +static const char* make_password_sha(ha_buffer_t* buf, const char* clearpw) +{ + struct SHA1Context sha; + unsigned char digest[SHA1_LEN]; + + SHA1_Init(&sha); + SHA1_Update(&sha, clearpw, strlen(clearpw)); + SHA1_Final(digest, &sha); + + ha_bufnext(buf); + ha_bufenc64(buf, digest, SHA1_LEN); + return ha_bufdata(buf); +} + +static int parse_ldap_password(const char** password) +{ + const char* pw; + const char* scheme; + int i; + + ASSERT(password && *password); + + pw = *password; + + /* zero length passwords are clear */ + if(strlen(pw) == 0) + return LDAP_PW_CLEAR; + + /* passwords without a scheme are clear */ + if(pw[0] != '{') + return LDAP_PW_CLEAR; + + pw++; + scheme = pw; + + while(*pw && (isalpha(*pw) || isdigit(*pw) || *pw == '-')) + pw++; + + /* scheme should end in a brace */ + if(pw != '}') + return LDAP_PW_CLEAR; + + *password = pw + 1; + + /* find a scheme in our map */ + for(i = 0; i < countof(kLDAPPWTypes); i++) + { + if(strncasecmp(kLDAPSchemes[i].name, scheme, pw - scheme)) + return kLDAPSchemes[i].type; + } + + return LDAP_PW_UNKNOWN; +} + +static const char* find_cleartext_password(ha_buffer_t* buf, const char** pws) +{ + ha_bufnext(buf); + + for(; pws && *pws; pws++) + { + const char* pw = *pws; + + if(parse_ldap_password(&pw) == LDAP_PW_CLEAR) + return pw; + } + + return NULL; +} + + +static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha1) +{ + /* Raw binary */ + if(bv->bv_len == MD5_LEN) + { + memcpy(ha1, bv->bv_len, MD5_LEN); + return HA_OK; + } + + /* Hex encoded */ + else if(bv->bv_len == (MD5_LEN * 2)) + { + ha_bufnext(buf); + ha_bufdechex(buf, bv->bv_val, MD5_LEN * 2); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + if(ha_buflen(buf) == MD5_LEN) + { + memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN); + return HA_OK; + } + } + + /* B64 Encoded */ + else + { + ha_bufnext(buf); + ha_bufdec64(buf, (*pws)->bv_val, (*pws)->bv_len); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + if(ha_buflen(buf) == MD5_LEN) + { + memcpy(rec->ha1, ha_bufdata(buf), MD5_LEN); + return HA_OK; + } + } + + return HA_FALSE; +} + +static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry, + ha_buffer_t* buf, const char* user, const char* clearpw) +{ + const char** pws; + const char* pw; + const char* p; + int type; + int res = HA_FALSE; + int unknown = 0; + + ASSERT(entry && ld && ctx && clearpw); + + ASSERT(ctx->pw_attr); + pws = ldap_get_values(ld, entry, ctx->pw_attr); + + if(pws) + { + for( ; *pws; pws++) + { + pw = *pws; + type = parse_ldap_password(&pw); + + switch(type) + { + case LDAP_PW_CLEAR: + p = clearpw; + break; + + case LDAP_PW_MD5: + p = make_password_md5(buf, clearpw); + break; + + case LDAP_PW_CRYPT: + + /* Not sure if crypt is thread safe */ + ha_lock(NULL); + p = crypt(clearpw, pw); + ha_unlock(NULL); + break; + + case LDAP_PW_SHA: + p = make_password_sha(buf, clearpw); + break; + + case LDAP_PW_UNKNOWN: + unknown = 1; + continue; + + default: + /* Not reached */ + ASSERT(0); + }; + + if(!p) + { + res = HA_ERROR; + break; + } + + if(strcmp(pw, p) == 0) + { + res = HA_OK; + break; + } + } + + ldap_free_values(pws); + } + + if(res == HA_FALSE && unknown) + ha_messagex(LOG_ERR, "LDAP does not contain any compatible passwords for user: %s", user); + + return res; +} + +static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry, + ha_buffer_t* buf, const char* user, const char* clearpw) +{ + struct berval** ha1s; + unsigned char key[MD5_LEN]; + unsigned char k[MD5_LEN]; + int r, first = 1; + int res = HA_FALSE; + + if(!ctx->ha1_attr) + return HA_FALSE; + + ha1s = ldap_get_values_len(ld, entry, ctx->ha1_attr); + + if(ha1s) + { + make_digest_ha1(key, user, ctx->realm, clearpw); + + for( ; *ha1s; ha1s++) + { + r = parse_ldap_h1(buf, *ha1s, k); + if(r == HA_ERROR) + { + res = r; + break; + } + + if(r == HA_FALSE) + { + if(first) + ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user); + + first = 0; + continue; + } + + if(memcmp(key, k, MD5_LEN) == 0) + { + res = HA_OK; + break; + } + } + + ldap_free_values_len(ha1s); + } + + return res; +} + +static LDAP* get_ldap_connection(ldap_context_t* ctx) +{ + LDAP* ld; + int i, r; + + for(i = 0; i < ctx->pending_max; i++) + { + /* An open connection in the pool */ + if(ctx->pool[i]) + { + ld = ctx->pool[i]; + ctx->pool[i]; + return ld; + } + } + + if(ctx->pool_mark >= ctx->pending_max) + { + ha_messagex("too many open connections to LDAP"); + return NULL; + } + + ld = ldap_init(ctx->servers, ctx->port); + if(!ld) + { + ha_message("couldn't initialize ldap connection"); + return NULL; + } + + if(ctx->user || ctx->password) + { + r = ldap_simple_bind_s(ld, ctx->user ? ctx->user : "", + ctx->password ? ctx->password : ""); + if(r != LDAP_SUCCESS) + { + report_ldap(r, NULL); + ldap_unbind_s(ld); + return NULL; + } + } + + return ld; +} + +static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld) +{ + int i; + + if(!ld) + return; + + /* Make sure it's worth saving */ + switch(ld_errno(ld)) + { + case LDAP_SERVER_DOWN: + case LDAP_LOCAL_ERROR: + case LDAP_NO_MEMORY: + break; + + default: + for(i = 0; i < ctx->pending_max; i++) + { + /* An open connection in the pool */ + if(!ctx->pool[i]) + { + ctx->pool[i] = ld; + ld = NULL; + break; + } + } + + break; + }; + + if(ld != NULL) + ldap_unbind_s(ld); +} + +static int complete_digest_ha1(ldap_context_t* ctx, ha_digest_rec_t* rec, + const char* user) +{ + LDAP* ld = NULL; /* freed in finally */ + LDAPMessage* results = NULL; /* freed in finally */ + LDAPMessage* entry = NULL; /* no need to free */ + struct berval** pws; /* freed manually */ + int ret = HA_FALSE; + + /* Hash in the user name */ + ha_md5string(user, rec->userhash); + + + ld = get_ldap_connection(ctx); + if(!ld) + goto finally; + + /* + * Discover the DN of the user. If there's a DN map string + * then we can do this really quickly here without querying + * the LDAP tree + */ + if(ctx->dnmap) + { + /* The map can have %u and %r to denote user and realm */ + dn = substitute_params(ctx, buf, user, ctx->dnmap); + if(!dn) + { + ret = HA_ERROR; + goto finally; + } + } + + /* Okay now we contact the LDAP server. */ + r = retrieve_user_entry(ctx, buf, user, &dn, &entry, &results); + if(r != HA_OK) + { + ret = r; + goto finally; + } + + /* Figure out the users ha1 */ + if(ctx->ha1_attr) + pws = ldap_get_values_len(ld, entry, ctx->ha1_attr); + + if(pws) + { + if(*pws) + { + r = parse_ldap_ha1(buf, *pws, rec->ha1); + if(r != HA_OK) + { + ret = r + + if(ret != HA_FALSE) + ha_messagex(LOG_ERROR, "LDAP contains invalid HA1 digest hash for user: %s", user); + } + } + + ldap_free_values_len(pws); + goto finally; + } + + /* If no ha1 set or none found, use password and make a HA1 */ + pws = ldap_get_values_len(ld, entry, ctx->pw_attr); + + if(pws) + { + /* Find a cleartext password */ + const char* t = find_cleartext_password(buf, pws); + + ldap_free_values_len(pws); + + if(t) + { + make_digest_ha1(rec->ha1, user, ctx->realm, t); + ret = HA_OK; + goto finally; + } + } + + ha_messagex(LOG_ERROR, "LDAP contains no cleartext password for user: %s", user); + +finally: + + if(ld) + save_ldap_connection(ctx, ld); + + if(results) + ldap_msgfree(results); + + return ret; +} + +static int retrieve_user_entry(ldap_context_t* ctx, buffer_t* buf, LDAP* ld, + const char* user, const char** dn, + LDAPMessage** entry, LDAPMessage** result) +{ + timeval tv; + const char* filter; + const char* attrs[3]; + + if(ctx->filter) + { + /* Filters can also have %u and %r */ + filter = substitute_params(ctx, buf, user, ctx->filter); + if(!filter) + return HA_ERROR; + } + else + { + filter = "(objectClass=*)"; + } + + attrs[0] = ctx->dobind ? NULL : ctx->pw_attr; + attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr; + attrs[2] = NULL; + + tv.tv_sec = ctx->ldap_timeout; + tv.tv_usec = 0; + + r = ldap_search_st(ld, *dn ? *dn : ctx->base, + *dn ? LDAP_SCOPE_BASE : ctx->scope, + filter, attrs, 0, &tv, result); + + if(r != LDAP_SUCCESS) + return report_ldap(r, resp, &ret); + + + /* Only one result should exist */ + switch(r = ldap_count_entries(ld, *result)) + { + case 1: + *entry = ldap_first_entry(ld, *result); + if(!(*dn)) + *dn = ldap_get_dn(ld, entry); + return HA_OK; + + case 0: + ha_messagex(LOG_WARNING, "user not found in LDAP: %s", basic.user); + break; + + default: + ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter); + break; + }; + + ldap_msg_free(*result); + return HA_FALSE; +} + +static int basic_ldap_response(ldap_context_t* ctx, const char* header, + ha_response_t* resp, ha_buffer_t* buf) +{ + ha_basic_header_t basic; + LDAP* ld = NULL; + LDAPMessage* entry = NULL; + LDAPMessage* results = NULL; + const char* dn; + int ret = HA_FALSE; + int found = 0; + int r; + + ASSERT(buf && header && resp && buf); + + if(ha_parsebasic(header, buf, &basic) == HA_ERROR) + return HA_ERROR; + + /* Past this point we don't return directly */ + + /* Check and see if this connection is in the cache */ + ha_lock(NULL); + + if(hash_get(ctx->established, key) == BASIC_ESTABLISHED) + { + found = 1; + ret = HA_OK; + goto finally: + } + + ha_unlock(NULL); + + + /* If we have a user name and password */ + if(!basic.user || !basic.user[0] || + !basic.password || !basic.password[0]) + goto finally; + + + ld = get_ldap_connection(); + if(!ld) + { + resp->code = HA_SERVER_ERROR; + goto finally; + } + + + /* + * Discover the DN of the user. If there's a DN map string + * then we can do this really quickly here without querying + * the LDAP tree + */ + if(ctx->dnmap) + { + /* The map can have %u and %r to denote user and realm */ + dn = substitute_params(ctx, buf, basic.user, ctx->dnmap); + if(!dn) + { + ret = HA_ERROR; + goto finally; + } + } + + + /** + * Okay now we contact the LDAP server. There are many ways + * this is used for different authentication modes: + * + * - If a dn has been mapped above, this can apply a + * configured filter to narrow things down. + * - If no dn has been mapped, then this maps out a dn + * by using the single object the filter returns. + * - If not in 'dobind' mode we also retrieve the password + * here. + * + * All this results in only one query to the LDAP server, + * except for the case of dobind without a dnmap. + */ + + if(!ctx->dobind || !dn || ctx->filter) + { + r = retrieve_user_entry(ctx, buf, basic.user, &dn, &entry, &results); + if(r != HA_OK) + { + ret = r; + goto finally; + } + } + + + /* Now if in bind mode we try to bind as that user */ + if(ctx->dobind) + { + ASSERT(dn); + + r = ldap_simple_bind_s(ld, dn, basic.password); + if(r != LDAP_SUCCESS) + { + if(r == LDAP_INVALID_CREDENTIALS) + ha_messagex(LOG_WARNING, "invalid login for: %s", basic.user); + else + report_ldap(r, resp, &ret); + + goto finally; + } + + /* It worked! */ + resp->code = HA_SERVER_ACCEPT; + } + + + /* Otherwise we compare the password attribute */ + else + { + ret = validate_ldap_password(ctx, ld, entry, buf, basic.user, basic.password); + if(ret == HA_FALSE) + ret = validate_ldap_ha1(ctx, ld, entry, buf, basic.user, basic.password); + + if(ret == HA_OK) + resp->code = HA_SERVER_ACCEPT; + + else + ha_messagex(LOG_WARNING, "invalid or unrecognized password for user: %s", basic.user); + } + + +finally: + + if(ld) + save_ldap_connection(ctx, ld); + + if(results) + ldap_msgfree(results); + + if(resp->code == HA_SERVER_ACCEPT) + { + resp->details = basic.user; + + /* We put this connection into the successful connections */ + if(!hash_set(ctx->established, basic.key, LDAP_ESTABLISHED)) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + } + + return ret; +} + + +static int digest_ldap_response(ldap_context_t* ctx, const char* header, + const char* method, const char* uri, + ha_response_t* resp, ha_buffer_t* buf) +{ + ha_digest_header_t dg; + digest_rec_t* rec = NULL; + int ret = HA_FALSE; + int stale = 0; + int pending = 0; + + /* We use this below to send a default response */ + resp->code = -1; + + if(ha_parsedigest(header, buf, &rec) == HA_ERROR) + return HA_ERROR; + + /* Lookup our digest context based on the nonce */ + if(!dg.nonce || strlen(dg.nonce) != DIGEST_NONCE_LEN) + { + ha_messagex(LOG_WARNING, "digest response contains invalid nonce"); + goto finally; + } + + ha_lock(NULL); + + rec = (digest_rec_t*)hash_get(ctx->pending, dg.nonce) + if(rec) + { + pending = 1; + hash_rem(ctx->pending, dg.nonce); + } + + else + { + rec = (digest_rec_t*)hash_get(ctx->established, dg.nonce); + } + + ha_unlock(NULL); + + /* + * If nothing was found for this nonce, then it might + * be a stale nonce. In any case prompt the client + * to reauthenticate. + */ + if(!rec) + { + stale = 1; + goto finally; + } + + /* + * If we got a response from the pending table, then + * we need to lookup the user name and figure out + * who the dude is. + */ + if(pending) + { + ASSERT(rec); + + r = complete_digest_ha1(ctx, rec, dg->username); + if(r != HA_OK) + { + ret = r; + goto finally; + } + } + + /* Increment our nonce count */ + rec->nc++; + + ret = ha_digestcheck(ctx->realm, method, uri, buf, &dg, rec); + + if(ret == HA_OK) + { + resp->code = HA_SERVER_ACCEPT; + resp->details = dg->username; + + /* Put the connection back into established */ + + ha_lock(NULL); + + if(hash_set(ctx->established, dg.nonce, rec)) + { + rec = NULL; + } + else + { + ha_messagex(LOG_CRIT, "out of memory"); + ret = HA_ERROR; + } + + ha_unlock(NULL); + } + +finally: + + /* If the record wasn't stored away then free it */ + if(rec) + free(rec); + + /* If nobody above responded then challenge the client again */ + if(resp->code == -1) + return digest_ldap_challenge(ctx, resp, buf, stale); + + return ret; +} + + + + +/* ------------------------------------------------------------------------------- + * Handler Functions + */ + +int ldap_config(ha_context_t* context, const char* name, const char* value) +{ + ldap_context_t* ctx = (ldap_context_t*)(context.data); + + if(strcmp(name, "ldapservers") == 0) + { + ctx->servers = value; + return HA_OK; + } + + else if(strcmp(name, "ldapfilter") == 0) + { + ctx->filter = value; + return HA_OK; + } + + else if(strcmp(name, "ldapbase") == 0) + { + ctx->base = value; + return HA_OK; + } + + else if(strcmp(name, "ldappwattr") == 0) + { + ctx->pw_attr = value; + return HA_OK; + } + + else if(strcmp(name, "ldapha1attr") == 0) + { + ctx->ha1_attr = value; + return HA_OK; + } + + else if(strcmp(name, "ldapuser") == 0) + { + ctx->user = value; + return HA_OK; + } + + else if(strcmp(name, "ldappassword") == 0) + { + ctx->password = value; + return HA_OK; + } + + else if(strcmp(name, "ldapdnmap") == 0) + { + ctx->dnmap = value; + return HA_OK; + } + + else if(strcmp(name, "realm") == 0) + { + ctx->realm = value; + return HA_OK; + } + + else if(strcmp(name, "ldapscope") == 0) + { + if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0) + ctx->scope = LDAP_SCOPE_SUBTREE; + else if(strcmp(value, "base") == 0) + ctx->scope = LDAP_SCOPE_BASE; + else if(strcmp(value, "one") == 0 || strcmp(value, "onelevel") == 0) + ctx->scope = LDAP_SCOPE_ONELEVEL; + + else + { + messagex(LOG_ERR, "invalid value for '%s' (must be 'sub', 'base' or 'one')", name); + return HA_ERROR; + } + + return HA_OK; + } + + else if(strcmp(name, "ldapdobind") == 0) + { + return ha_confbool(name, value, &(ctx->dobind)); + } + + else if(strcmp(name, "pendingmax") == 0) + { + return ha_confint(name, value, 1, 256, &(ctx->pending_max)); + } + + else if(strcmp(name, "pendingtimeout") == 0) + { + return ha_confint(name, value, 0, 86400, &(ctx->pending_timeout)); + } + + else if(strcmp(name, "ldaptimeout") == 0) + { + return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout)); + } + + return HA_FALSE; +} + +int ldap_initialize(ha_context_t* context) +{ + /* No global initialization */ + if(!context) + return HA_OK; + + ldap_context_t* ctx = (ldap_context_t*)(context.data); + + + /* Make sure there are some types of authentication we can do */ + if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST))) + { + ha_messagex(LOG_ERR, "Digest module configured, but does not implement any " + "configured authentication type."); + return HA_ERROR; + } + + /* Check for mandatory configuration */ + if(!ctx->servers || (!ctx->dnmap || !ctx->filter)) + { + ha_messagex(LOG_ERR, "Digest LDAP configuration incomplete. " + "Must have LDAPServers and either LDAPFilter or LDAPDNMap."); + return HA_ERROR; + } + + + /* The hash tables */ + if(!(ctx->pending = hash_create(LDAP_HASH_KEY_LEN)) || + !(ctx->established = hash_create(LDAP_HASH_KEY_LEN))) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + /* + * Our connection pool. It's the size of our maximum + * amount of pending connections as that's the max + * we'd be able to use at a time anyway. + */ + XXXXX + ctx->pool = (LDAP**)malloc(sizeof(LDAP*) * ctx->pending_max); + if(!ctx->pool) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + memset(ctx->pool, 0, sizeof(LDAP*) * ctx->pending_max); + + return HA_OK; +} + +void ldap_destroy(ha_context_t* context) +{ + int i; + + if(!context) + return HA_OK; + + ldap_context_t* ctx = (digest_ldap_context_t*)(context.data); + + /* Note: We don't need to be thread safe here anymore */ + hash_free(ctx->pending); + hash_free(ctx->established); + + XXXXX + /* Close any connections we have open */ + for(i = 0; i < ctx->pending_max; i++) + { + if(ctx->pool[i]) + ldap_unbind_s(ctx->pool[i]); + } + + /* And free the connection pool */ + free(ctx->pool); +} + + +int ldap_process(ha_context_t* context, ha_request_t* req, + ha_response_t* resp, ha_buffer_t* buf) +{ + ldap_context_t* ctx = (ldap_context_t*)context; + time_t t = time(NULL); + const char* header = NULL; + int ret; + + ha_lock(NULL); + + XXXXXX + /* + * Purge out stale connection stuff. This includes + * authenticated connections which have expired as + * well as half open connections which expire. + */ + hash_purge(ctx->pending, t - ctx->pending_timeout); + hash_purge(ctx->established, t - ctx->timeout); + + ha_unlock(NULL); + + + /* We use this below to detect whether to send a default response */ + resp->code = -1; + + + /* Check the headers and see if we got a response thingy */ + if(ctx->types & HA_TYPE_DIGEST) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST); + if(header) + { + ret = digest_ldap_response(ctx, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + /* Or a basic authentication */ + if(!header && ctx->types & HA_TYPE_BASIC) + { + header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC); + if(header) + { + ret = basic_ldap_response(ctx, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + + /* Send a default response if that's what we need */ + if(resp->code == -1) + { + resp->code = HA_SERVER_DECLINE; + + if(ctx->types & HA_TYPE_DIGEST) + { + ret = digest_ldap_challenge(ctx, resp, buf, 0); + if(ret == HA_ERROR) + return ret; + } + + if(ctx->types & HA_TYPE_BASIC) + { + ha_bufnext(buf); + ha_bufcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL); + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); + } + } + + return ret; +} + + +/* ------------------------------------------------------------------------------- + * Handler Definition + */ + +ha_handler_t digest_ldap_handler = +{ + "LDAP", /* The type */ + ldap_initialize, /* Initialization function */ + ldap_destroy, /* Uninitialization routine */ + ldap_config, /* Config routine */ + ldap_process, /* Processing routine */ + &ldap_defaults, /* The context defaults */ + sizeof(ldap_context_t) +}; diff --git a/daemon/misc.c b/daemon/misc.c new file mode 100644 index 0000000..7091bfa --- /dev/null +++ b/daemon/misc.c @@ -0,0 +1,621 @@ + +#include "usuals.h" +#include "httpauthd.h" +#include "md5.h" + +#include +#include + +extern int g_debugging; +extern int g_daemonized; +extern pthread_mutex_t g_mutex; + + +/* ----------------------------------------------------------------------- + * Error Handling + */ + +const char kMsgDelimiter[] = ": "; +#define MAX_MSGLEN 128 + +void ha_messagex(int level, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + + /* Either to syslog or stderr */ + if(g_daemonized) + vsyslog(level, msg, ap); + + else + { + if(g_debugging || level > LOG_INFO) + vwarnx(msg, ap); + } + + va_end(ap); +} + +void ha_message(int level, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + + /* Either to syslog or stderr */ + if(g_daemonized) + { + char* m = (char*)alloca(strlen(msg) + MAX_MSGLEN + sizeof(kMsgDelimiter)); + + if(m) + { + strcpy(m, msg); + strcat(m, kMsgDelimiter); + strerror_r(errno, m + strlen(m), MAX_MSGLEN); + } + + vsyslog(LOG_ERR, m ? m : msg, ap); + } + else + { + if(g_debugging || level > LOG_INFO) + vwarnx(msg, ap); + } + + va_end(ap); +} + + +/* ----------------------------------------------------------------------- + * Header Functionality + */ + +ha_header_t* ha_findheader(ha_request_t* req, const char* name) +{ + int i; + + for(i = 0; i < MAX_HEADERS; i++) + { + if(req->headers[i].name) + { + if(strcasecmp(req->headers[i].name, name) == 0) + return &(req->headers[i]); + } + } + + return NULL; +} + +const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix) +{ + int i, l; + + for(i = 0; i < MAX_HEADERS; i++) + { + if(req->headers[i].name) + { + if(strcasecmp(req->headers[i].name, name) == 0) + { + if(!prefix) + return req->headers[i].data; + + l = strlen(prefix) + + if(strncasecmp(prefix, req->headers[i].value, l) == 0) + return req->headers[i].data + l; + } + } + } + + return NULL; +} + +void ha_addheader(ha_response_t* resp, const char* name, const char* data) +{ + int i = 0; + for(i = 0; i < MAX_HEADERS; i++) + { + if(!(resp->headers[i].name)) + { + resp->headers[i].name = name; + resp->headers[i].data = data; + return; + } + } + + ha_messagex(LOG_ERR, "too many headers in response. discarding '%s'", name); +} + + +/* ----------------------------------------------------------------------- + * Locking + */ + +void ha_lock(pthread_mutex_t* mtx) +{ + int r = pthread_mutex_lock(mtx ? mtx : &g_mutex); + if(r != 0) + { + errno = r; + ha_message(LOG_CRIT, "threading problem. couldn't lock mutex"); + } +} + +void ha_unlock(pthread_mutex_t* mtx) +{ + int r = pthread_mutex_unlock(mtx ? mtx : &g_mutex); + if(r != 0) + { + errno = r; + ha_message(LOG_CRIT, "threading problem. couldn't unlock mutex"); + } +} + + +/* ----------------------------------------------------------------------- + * Configuration + */ + +int ha_confbool(const char* name, const char* conf, int* value) +{ + if(value == NULL || + value[0] == 0 || + strcasecmp(conf, "0") == 0 || + strcasecmp(conf, "no") == 0 || + strcasecmp(conf, "false") == 0 || + strcasecmp(conf, "f") == 0 || + strcasecmp(conf, "off")) + { + *value = 0; + return HA_OK; + } + + if(strcasecmp(conf, "1") == 0 || + strcasecmp(conf, "yes") == 0 || + strcasecmp(conf, "true") == 0 || + strcasecmp(conf, "t") == 0 || + strcasecmp(conf, "on")) + { + *value = 1; + return HA_OK; + } + + ha_messagex(LOG_ERR, "invalid configuration value '%s': must be 'on' or 'off'.", name); + return HA_ERROR; +} + +int ha_confint(const char* name, const char* conf, int min, int max, int* value) +{ + const char* p; + errno = 0; + *value = strtol(conf, &p, 10); + + if(p != (name + strlen(name)) || errno == ERANGE || + (*value < min) || (*value > max)) + { + ha_messagex(LOG_ERR, "invalid configuration value '%s': must be a number between %d and %d", name, min, max); + return HA_ERROR; + } + + return HA_OK; +} + + +/* ----------------------------------------------------------------------- + * Hash Stuff + */ + +void ha_md5string(const char* data, unsigned char* hash) +{ + struct MD5Context md5; + MD5Init(&md5); + MD5Update(&md5, data, strlen(data)); + MD5Final(hash, &md5); +} + + +/* ----------------------------------------------------------------------- + * Client Authentication Functions + */ + +int ha_parsebasic(char* header, ha_buffer_t* buf, ha_basic_header_t* rec) +{ + char* t; + + memset(rec, 0, sizeof(*rec)); + + /* Trim the white space */ + while(*header && isspace(*header)) + header++; + + /* + * Authorization header is in this format: + * + * "Basic " B64(user ":" password) + */ + ha_bufnext(buf); + ha_bufdec64(buf, header); + + header = ha_bufdata(buf); + + if(!header) + return HA_ERROR; + + + /* We have a cache key at this point so hash it */ + ha_md5string(header, rec->key); + + + /* Parse the user. We need it in any case */ + t = strchr(header, ':'); + if(t != NULL) + { + /* Break the string in half */ + *t = 0; + + rec->user = header; + rec->password = t + 1; + } + + return HA_OK; +} + + +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +int ha_parsedigest(char* header, ha_buffer_t* buf, ha_digest_header_t* rec) +{ + /* + * This function destroys the contents of header by + * terminating strings in it all over the place. + */ + + char next; + char* key; + char* value; + + ha_bufnext(buf); + ha_bufcat(buf, header, NULL); + + header = ha_bufdata(buf); + + if(!header) + return HA_ERROR; + + memset(rec, 0, sizeof(rec)); + + while(header[0]) + { + /* find key */ + + while(header[0] && isspace(header[0])) + header++; + + key = header; + + while(header[0] && header[0] != '=' && header[0] != ',' && + !isspace(header[0])) + header++; + + /* null terminate and move on */ + next = header[0]; + header[0] = '\0'; + header++; + + if(!next) + break; + + if(isspace(t)) + { + while(header[0] && isspace(header[0])) + header++; + + next = header[0]; + } + + /* find value */ + + if(next == '=') + { + header++; + + while(header[0] && isspace(header[0])) + header++; + + vv = 0; + if(header[0] == '\"') /* quoted string */ + { + header++; + value = header; + + while(header[0] && header[0] != '\"') + header++; + + header[0] = 0; + header++; + } + + else /* token */ + { + value = header; + + while(header[0] && header[0] != ',' && !isspace(header[0])) + header++; + + header[0] = 0; + header++; + } + + while(header[0] && header[0] != ',') + header++; + + if(header[0]) + header++; + + if(!strcasecmp(key, "username")) + rec->username = value; + else if(!strcasecmp(key, "realm")) + rec->realm = value; + else if (!strcasecmp(key, "nonce")) + rec->nonce = value; + else if (!strcasecmp(key, "uri")) + rec->uri = value; + else if (!strcasecmp(key, "response")) + rec->digest = value; + else if (!strcasecmp(key, "algorithm")) + rec->algorithm = value; + else if (!strcasecmp(key, "cnonce")) + rec->cnonce = value; + else if (!strcasecmp(key, "opaque")) + rec->opaque = value; + else if (!strcasecmp(key, "qop")) + rec->qop = value; + else if (!strcasecmp(key, "nc")) + rec->nc = value; + } + } + + if(rec->nonce) + ha_md5string(rec->nonce, rec->key); + + return HA_OK; +} + +int ha_digestcheck(const char* realm, const char* method, const char* uri, + ha_buffer_t* buf, ha_digest_header_t* dg, ha_digest_record_t* rec) +{ + unsigned char hash[MD5_LEN]; + struct MD5Context md5; + const char* digest; + const char* t; + + /* Check for digest */ + if(!dg->digest || !dg->digest[0]) + { + ha_messagex(LOG_WARNING, "digest response missing digest"); + return HA_FALSE; + } + + /* Username */ + if(!dg->username || !dg->username[0] || + ha_md5strcmp(dg->username, rec->userhash) != 0) + { + ha_messagex(LOG_WARNING, "digest response missing username"); + return HA_FALSE; + } + + /* The realm */ + if(!dg->realm || strcmp(dg->realm, realm) != 0) + { + ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'", + dg->realm ? dg->realm : ""); + return HA_FALSE; + } + + /* Components in the new RFC */ + if(dg->qop) + { + /* + * We only support 'auth' qop. We don't have access to the data + * and wouldn't be able to support anything else. + */ + if(strcmp(dg->qop, "auth") != 0) + { + ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported qop: '%s'", + dg->qop ? dg->qop : ""); + return HA_FALSE; + } + + /* The nonce */ + if(!dg->cnonce || !dg->cnonce[0]) + { + ha_messagex(LOG_WARNING, "digest response is missing cnonce value"); + return HA_FALSE; + } + + /* The nonce count */ + if(!dg->nc || !dg->nc[0]) + { + ha_messagex(LOG_WARNING, "digest response is missing nc value"); + return HA_FALSE; + } + + /* Validate the nc */ + else + { + char* e; + long nc = strtol(dg->nc, &e, 10); + + if(e != (dg->nc + strlen(e)) || nc != rec->nc) + { + ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s", + dg->nc); + return HA_FALSE; + } + } + } + + /* The algorithm */ + if(dg->algorithm && strcasecmp(dg->algorithm, "MD5") != 0) + { + ha_messagex(LOG_WARNING, "digest response contains unknown or unsupported algorithm: '%s'", + dg->algorithm ? dg->algorithm : ""); + return HA_FALSE; + } + + /* Request URI */ + if(!dg->uri) + { + ha_messagex(LOG_WARNING, "digest response is missing uri"); + return HA_FALSE; + } + + if(strcmp(dg->uri, uri) != 0) + { + ha_uri_t d_uri; + ha_uri_t s_uri; + + if(ha_uriparse(dg->uri, &d_uri) == HA_ERROR) + { + ha_messagex(LOG_WARNING, "digest response constains invalid uri: %s", dg->uri); + return HA_FALSE; + } + + if(ha_uriparse(uri, &s_uri) == HA_ERROR) + { + ha_messagex(LOG_ERR, "server sent us an invalid uri"); + return HA_ERROR; + } + + if(ha_uricmp(&d_uri, &s_uri) != 0) + { + ha_messagex(LOG_WARNING, "digest response contains wrong uri: %s " + "(should be %s)", dg->uri, uri); + return HA_ERROR; + } + } + + /* + * nonce: should have already been validated by the caller who + * found this nice rec structure to pass us. + * + * opaque: We also don't use opaque. The caller should have validated it + * if it's used there. + */ + + /* + * Now we validate the digest response + */ + + /* Encode ha1 */ + ha_bufnext(buf); + ha_bufenchex(buf, rec->ha1, MD5_LEN); + + if((t = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + /* Encode ha2 */ + MD5Init(&md5); + MD5Update(&md5, method, strlen(method)); + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->uri, strlen(dg->uri)); + MD5Final(hash, &md5); + + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if(!ha_bufdata(buf)) + return HA_ERROR; + + + /* Old style digest (RFC 2069) */ + if(!dg->qop) + { + MD5Init(&md5); + MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha1 */ + MD5Final(hash, &md5); + } + + /* New style 'auth' digest (RFC 2617) */ + else + { + MD5Init(&md5); + MD5Update(&md5, t, MD5_LEN * 2); /* ha1 */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nonce, strlen(dg->nonce)); /* nonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->nc, strlen(dg->nc)); /* nc */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->cnonce, strlen(dg->cnonce)); /* cnonce */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->qop, strlen(dg->qop)); /* qop */ + MD5Update(&md5, ":", 1); + MD5Update(&md5, ha_bufdata(buf), MD5_LEN * 2); /* ha2 */ + MD5Final(hash, &md5); + } + + /* Encode the digest */ + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if((digest = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + if(strcasecmp(dg->digest, digest) != 0) + { + ha_messagex(LOG_WARNING, "digest authentication failed for user: %s", dg->username); + return HA_FALSE; + } + + return HA_OK; +} + +const char* ha_digestrespond(ha_buffer_t* buf, ha_digest_header_t* dg, + ha_digest_rec_t* rec) +{ + unsigned char hash[MD5_LEN]; + struct MD5Context md5; + const char* t; + const char* digest; + + /* Encode ha1 */ + ha_bufnext(buf); + ha_bufenchex(buf, rec->ha1, MD5_LEN); + + if((t = ha_bufdata(buf)) == NULL) + return HA_ERROR; + + /* Encode ha2 */ + MD5Init(&md5); + MD5Update(&md5, method, strlen(method)); + MD5Update(&md5, ":", 1); + MD5Update(&md5, dg->uri, strlen(dg->uri)); + MD5Final(hash, &md5); + + ha_bufnext(buf); + ha_bufenchex(buf, hash, MD5_LEN); + + if(!ha_bufdata(buf)) + return HA_ERROR; +} + + +char* ha_uriformat(const ha_uri_t* uri, ha_buffer_t* buf); +int ha_uriparse(const char* str, ha_uri_t* uri); diff --git a/daemon/ntlm.c b/daemon/ntlm.c new file mode 100644 index 0000000..880ae9e --- /dev/null +++ b/daemon/ntlm.c @@ -0,0 +1,703 @@ + +/* TODO: Include attribution for ideas, and code from mod_ntlm */ + +#include "usuals.h" +#include "httpauthd.h" +#include "hash.h" +#include "defaults.h" + +#include + +/* The NTLM headers */ +#include "ntlmssp.h" + +/* ------------------------------------------------------------------------------- + * Defaults and Constants + */ + +/* This needs to be the same as an MD5 hash length */ +#define NTLM_HASH_KEY_LEN 16 +#define NTLM_ESTABLISHED (void*)1 + + +/* ------------------------------------------------------------------------------- + * Structures and Globals + */ + +/* A pending connection */ +typedef struct ntlm_connection +{ + void* handle; + char nonce[NONCE_LEN]; + unsigned int flags; +} +ntlm_connection_t; + + +/* The main context */ +typedef struct ntlm_context +{ + /* Settings ---------------------------------------------------------- */ + const char* server; /* Server to authenticate against */ + const char* domain; /* NTLM domain to authenticate against */ + const char* backup; /* Backup server if primary is down */ + int pending_max; /* Maximum number of connections at once */ + int pending_timeout; /* Timeout for authentication (in seconds) */ + const char* basic_realm; /* The realm for basic authentication */ + + /* Context ----------------------------------------------------------- */ + hash_t* pending; /* Pending connections */ + hash_t* established; /* Established connections */ +} +ntlm_context_t; + + +/* The default context settings */ +static const ntlm_context_t ntlm_defaults = +{ + NULL, NULL, NULL, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT, + NULL, NULL, NULL +}; + + +/* Mutexes for accessing non-thread-safe smblib */ +static pthread_mutex_t g_smblib_mutex; +static pthread_mutexattr_t g_smblib_mutexattr; + + +/* ------------------------------------------------------------------------------- + * Internal Functions + */ + +static ntlm_connection_t* getpending(ntlm_context_t* ctx, const void* key) +{ + ntlm_connection_t* ret; + + ha_lock(NULL); + + if(ret = (ntlm_connection_t*)hash_get(ctx->pending, key)) + hash_rem(ctx->pending, key); + + ha_unlock(NULL); + + return ret; +} + +static int putpending(ntlm_context_t* ctx, const void* key, ntlm_connection_t* conn) +{ + int r = 0; + + ha_lock(NULL); + + if(hash_get(ctx->pending, key)) + { + if(!hash_set(ctx->pending, key, (void*)conn)) + { + ha_messagex(LOG_ERR, "out of memory"); + r = -1; + } + } + + ha_unlock(NULL); + + return r; +} + +static ntlm_connection_t* makeconnection(ntlm_context_t* ctx) +{ + ntlm_connection_t* conn; + + conn = (ntlm_connection_t*)malloc(sizeof(ntlm_connection_t)); + if(!conn) + { + ha_messagex(LOG_CRIT, "out of memory"); + return NULL; + } + + memset(conn, 0, sizeof(*conn)); + + /* + * Open a connection to to the domain controller. I don't think + * we can cache these connections or use them again as opening + * a connection here results in an nonce being generated. + */ + conn->handle = ntlmssp_connect(ctx->server, ctx->backup, + ctx->domain, conn->nonce); + if(!conn->handle) + { + ha_messagex(LOG_ERR, "couldn't connect to the domain server %s (backup: %s)", + ctx->server, ctx->backup ? ctx->backup : "none"); + free(conn); + return NULL; + } +} + +static void freeconnection(ntlm_context_t* ctx, ntlm_connection_t* conn) +{ + if(conn->handle) + { + ntlmssp_disconnect(conn->handle); + conn->handle = NULL; + } + + free(conn); +} + +int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header, + ha_response_t* resp, ha_buffer_t* buf) +{ + ntlm_connection_t* conn; + char* t; + ha_basic_header_t basic; + const char* domain = NULL; + + /* + * We're doing basic authentication on the connection + * which invalidates any NTLM authentication we've started + * or done on this connection. + */ + conn = getpending(ctx, key); + if(conn) + freeconnection(ctx, conn); + + if(ha_parsebasic(header, buf, &basic) == HA_ERROR) + return HA_ERROR; + + /* Check and see if this connection is in the cache */ + ha_lock(NULL); + + if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED) + found = 1; + + ha_unlock(NULL); + + + /* Try to find a domain in the user */ + if((t = strchr(basic.user, '\\')) != NULL || + (t = strchr(basic.user, '/')) != NULL) + { + /* Break at the domain */ + domain = basic.user; + basic.user = t + 1; + *t = 0; + + /* Make sure this is our domain */ + if(strcasecmp(domain, ctx->domain) != 0) + domain = NULL; + } + + if(!domain) + { + /* Use the default domain if none specified */ + domain = ctx->domain; + } + + /* Make sure above did not fail */ + if(!found && basic.user && basic.user[0] && basic.password && + basic.password[0] && domain && domain[0]) + { + /* We need to lock to go into smblib */ + ha_lock(&g_smblib_mutex); + + /* Found in smbval/valid.h */ + if(ntlmssp_validuser(basic.user, basic.password, ctx->server, + ctx->backup, domain) == NTV_NO_ERROR) + { + /* If valid then we return */ + resp->code = HA_SERVER_ACCEPT; + } + + ha_unlock(&g_smblib_mutex); + } + + if(resp->code = HA_SERVER_ACCEPT) + { + resp->detail = basic.user; + + /* We put this connection into the successful connections */ + if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED)) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + + return HA_OK; + } + + return HA_FALSE; +} + +int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header, + ha_response_t* resp, ha_buffer_t* buf) +{ + ntlmssp_info_rec ntlmssp; + ntlm_connection_t* conn = NULL; + unsigned int flags = 0; + int ret = HA_FALSE; + int r; + + /* + * Retrieve and remove the connection from the pending bag. + * We add it back again below if that's necessary. + */ + conn = getpending(ctx, key); + + /* + * We use the flags from an already established connection + * if we've been pending and stuff + */ + + if(conn && conn->flags) + flags = conn->flags; + + /* + * First we figure out what kind of message the client + * is sending us. + */ + + ha_bufnext(buf); + ha_bufdec64(buf, header); + header = ha_bufdata(buf); + + if(ha_buferr(buf)) + goto finally; + + r = ntlmssp_decode_msg(&ntlmssp, ha_bufdata(buf), ha_buflen(buf), &flags); + if(r != 0) + { + ha_messagex(LOG_ERR, "decoding NTLM message failed (error %d)", r); + resp->code = HA_SERVER_BADREQ; + goto finally; + } + + + switch(ntlmssp.msg_type) + { + + /* An initial NTLM request? */ + case 1: + { + /* Win9x doesn't seem to send a domain or host */ + int win9x = !ntlmssp.host[0] && !ntlmssp.domain[0]; + + /* + * If we already have a connection to the domain controller + * then we're in trouble. Basically this is the second + * type 1 message we've received over this connection. + * + * TODO: Eventually what we want to do here is wait for the + * other authentication request to complete, or something + * like that. + */ + if(conn) + { + /* + * In this case we also add the connection back into the + * pending stack so that the correct request will complete + * properly when it comes through. + */ + if(putpending(ctx, key, conn) != -1) + conn = NULL; + + ha_messagex(LOG_ERR, "received out of order NTLM request from client"); + resp->code = HA_SERVER_BADREQ; + goto finally; + } + + + /* + * Check how many connections we have to the domain controller + * and if too many then cut off here. + */ + if(ctx->pending_max != -1) + { + ha_lock(NULL); + r = (hash_count(ctx->pending) >= ctx->pending_max); + ha_unlock(NULL); + + if(r) + { + resp->code = HA_SERVER_BUSY; + goto finally; + } + } + + + /* + * Open a connection to to the domain controller. I don't think + * we can cache these connections or use them again as opening + * a connection here results in an nonce being generated. + */ + conn = makeconnection(ctx); + + if(!conn) + { + resp->code = HA_SERVER_ERROR; + goto finally; + } + + /* Save away any flags given us by ntlm_decode_msg */ + conn->flags = flags; + + /* Start building the header */ + ha_bufnext(buf); + ha_bufcat(buf, NTLM_PREFIX); + + if(win9x) + { + struct ntlm_msg2_win9x msg_win9x; + ntlmssp_encode_msg2_win9x(conn->nonce, &msg_win9x, (char*)ctx->domain, flags); + ha_bufenc64(buf, (unsigned char*)&msg_win9x, sizeof(msg_win9x)); + } + else + { + struct ntlm_msg2 msg; + ntlmssp_encode_msg2(conn->nonce, &msg); + ha_bufenc64(buf, (unsigned char*)&msg, sizeof(msg)); + } + + if(ha_buferr(buf)) + goto finally; + + /* + * TODO: Our callers need to be able to keep alive + * connections that have authentication going on. + */ + + /* Cache this connection in our pending set ... */ + if(putpending(ctx, key, conn) == -1) + { + resp->code = HA_SERVER_ERROR; + goto finally; + } + + /* + * By marking this as null, the cleanup code + * won't free the connection since it's been + * cached above. + */ + conn = NULL; + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); + resp->code = HA_SERVER_DECLINE; + goto finally; + } + + /* A response to a challenge */ + case 3: + { + /* + * We need to have a connection at this point or this whole thing + * has come in in the wrong order. Actually it's a client error + * for stuff to come in wrong. But since some web servers also + * kill keep-alives and stuff, we forgive and just ask the client + * for the authentication info again. + */ + if(!conn || !conn->handle) + { + ha_messagex(LOG_ERR, "received out of order NTLM response from client"); + resp->code = HA_SERVER_BADREQ; + goto finally; + } + + if(!ntlmssp.user) + { + ha_messagex(LOG_ERR, "received NTLM response without user name"); + resp->code = HA_SERVER_BADREQ; + goto finally; + } + + /* We have to lock while going into smblib */ + ha_lock(&g_smblib_mutex); + + /* Now authenticate them against the DC */ + r = ntlmssp_auth(conn->handle, ntlmssp.user, ntlmssp.nt, 1, + ntlmssp.domain[0] ? (char*)ntlmssp.domain : (char*)ctx->domain); + + ha_unlock(&g_smblib_mutex); + + /* The connection gets disconnected below */ + + if(r == NTV_LOGON_ERROR) + { + /* + * Note that we don't set a code here. This causes our + * caller to put in all the proper headers for us. + */ + ha_messagex(LOG_ERR, "failed NTLM logon for user '%s'", ntlmssp.user); + ret = HA_FALSE; + } + + /* A successful login ends here */ + else + { + resp->code = HA_SERVER_ACCEPT; + + /* We put this connection into the successful connections */ + if(!hash_set(ctx->established, key, NTLM_ESTABLISHED)) + { + ha_messagex(LOG_CRIT, "out of memory"); + ret = HA_ERROR; + } + } + + goto finally; + } + + default: + ha_messagex(LOG_ERR, "received invalid NTLM message (type %d)", ntlmssp.msg_type); + resp->code = HA_SERVER_BADREQ; + goto finally; + }; + + +finally: + if(ha_buferr(buf)) + ret = HA_ERROR; + + if(conn) + freeconnection(ctx, conn); + + return ret; +} + + +/* ------------------------------------------------------------------------------- + * Handler Functions + */ + +int ntlm_config(ha_context_t* context, const char* name, const char* value) +{ + ntlm_context_t* ctx = (ntlm_context_t*)(context->data); + + if(strcmp(name, "ntlmserver") == 0) + { + ctx->server = value; + return HA_OK; + } + + else if(strcmp(name, "ntlmbackup") == 0) + { + ctx->backup = value; + return HA_OK; + } + + else if(strcmp(name, "ntlmdomain") == 0) + { + ctx->domain = value; + return HA_OK; + } + + else if(strcmp(name, "pendingmax") == 0) + { + return ha_confint(name, value, 1, 256, &(ctx->pending_max)); + } + + else if(strcmp(name, "pendingtimeout") == 0) + { + return ha_confint(name, value, 1, 86400, &(ctx->pending_timeout)); + } + + else if(strcmp(name, "realm") == 0) + { + ctx->basic_realm = value; + return HA_OK; + } + + return HA_FALSE; +} + +int ntlm_init(ha_context_t* context) +{ + /* Per context initialization */ + if(context) + { + ntlm_context_t* ctx = (ntlm_context_t*)(context->data); + + /* Make sure there are some types of authentication we can do */ + if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_NTLM))) + { + ha_messagex(LOG_ERR, "NTLM module configured, but does not implement any " + "configured authentication type."); + return HA_ERROR; + } + + /* Check for mandatory configuration */ + if(!(ctx->server) || !(ctx->domain)) + { + ha_messagex(LOG_ERR, "NTLM configuration incomplete. " + "Must have NTLMServer and NTLMDomain configured."); + return HA_ERROR; + } + + /* Initialize our tables */ + if(!(ctx->pending = hash_create(NTLM_HASH_KEY_LEN)) || + !(ctx->established = hash_create(NTLM_HASH_KEY_LEN))) + { + ha_messagex(LOG_CRIT, "out of memory"); + return HA_ERROR; + } + } + + /* Global Initialization */ + else + { + /* Create the smblib mutex */ + if(pthread_mutexattr_init(&g_smblib_mutexattr) != 0 || + pthread_mutexattr_settype(&g_smblib_mutexattr, HA_MUTEX_TYPE) || + pthread_mutex_init(&g_mutex, &g_smblib_mutexattr) != 0) + { + ha_messagex(LOG_CRIT, "threading problem. can't create mutex"); + return HA_ERROR; + } + } + + return HA_OK; +} + +void ntlm_destroy(ha_context_t* context) +{ + /* Per context destroy */ + if(context) + { + ntlm_context_t* ctx = (ntlm_context_t*)(context->data); + + /* Note: We don't need to be thread safe here anymore */ + hash_free(ctx->pending); + hash_free(ctx->established); + } + + /* Global Destroy */ + else + { + /* Close the mutex */ + pthread_mutex_destroy(&g_smblib_mutex); + pthread_mutexattr_destroy(&g_smblib_mutexattr); + } +} + +int ntlm_process(ha_context_t* context, ha_request_t* req, + ha_response_t* resp, ha_buffer_t* buf) +{ + ntlm_context_t* ctx = (ntlm_context_t*)(context->data); + void* ntlm_connection_t = NULL; + unsigned char key[NTLM_HASH_KEY_LEN]; + const char* header = NULL; + time_t t = time(NULL); + int ret; + + resp->code = -1; + + /* Hash the unique key */ + ha_md5string(req->args[1], key); + + + ha_lock(NULL); + + XXX: These connections aren't being destroyed properly + + /* + * Purge out stale connection stuff. This includes + * authenticated connections which have expired as + * well as half open connections which expire. + */ + hash_purge(ctx->pending, t - ctx->pending_timeout); + hash_purge(ctx->established, t - context->timeout); + + ha_unlock(NULL); + + + /* Look for a NTLM header */ + if(context->types & HA_TYPES_NTLM) + { + header = ha_getheader(req, "Authorization", NTLM_PREFIX); + if(header) + { + /* Trim off for decoding */ + while(*header && isspace(*header)) + header++; + + ret = ntlm_auth_ntlm(ctx, key, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + /* If basic is enabled, and no NTLM */ + if(!header && context->types & HA_TYPE_BASIC) + { + /* Look for a Basic header */ + header = ha_getheader(req, "Authorization", BASIC_PREFIX); + if(header) + { + /* Trim off for decoding */ + while(*header && isspace(*header)) + header++; + + ret = ntlm_auth_basic(ctx, key, header, resp, buf); + if(ret == HA_ERROR) + return ret; + } + } + + /* The authorization header was not found */ + else + { + ha_lock(NULL); + + /* + * NTLM trusts a connection after it's been authenticated + * so just pass success for those. Note that we do this + * in the absence of a authorization header so that we + * allow connections to be re-authenticated. + */ + + if(hash_get(ctx->established, key) == NTLM_ESTABLISHED) + { + hash_touch(ctx->established, key); + resp->code = HA_SERVER_ACCEPT; + } + + ha_unlock(NULL); + } + + + /* If nobody's set any other response then... */ + if(resp->code != -1) + { + /* If authentication failed tell the browser about it */ + resp->code = HA_SERVER_DECLINE; + + if(context->types & HA_TYPE_NTLM) + ha_addheader(resp, "WWW-Authenticate", NTLM_PREFIX); + + if(context->types & HA_TYPE_BASIC) + { + ha_bufnext(buf); + ha_bufcat(buf, BASIC_PREFIX, "realm=\"", + ctx->basic_realm ? ctx->basic_realm : "", "\"", NULL); + + ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); + } + } + + return ret; +} + + + +/* ------------------------------------------------------------------------------- + * Handler Definition + */ + +ha_handler_t ntlm_handler = +{ + "NTLM", /* The type */ + ntlm_init, /* Initialization function */ + ntlm_destroy, /* Uninitialization routine */ + ntlm_config, /* Config routine */ + ntlm_process, /* Processing routine */ + ntlm_defaults, /* Default settings */ + sizeof(ntlm_context_t) +}; + diff --git a/daemon/ntlmssp.c b/daemon/ntlmssp.c new file mode 100644 index 0000000..3edb63b --- /dev/null +++ b/daemon/ntlmssp.c @@ -0,0 +1,398 @@ +/* + * $Id$ + * + */ + +#include "ntlmssp.h" +#include "smblib/smblib-priv.h" + +#define little_endian_word(x) x[0] + (((unsigned)x[1]) << 8) +/* fhz 02-02-09: typecasting is needed for a generic use */ +#define set_little_endian_word(x,y) (*((char *)x))=(y&0xff);*(((char*)x)+1)=((y>>8)&0xff) + +int ntlm_msg_type(unsigned char *raw_msg, unsigned msglen) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + + if (msglen < 9) + return -1; + if (strncmp(msg->protocol, "NTLMSSP", 8)) + return -1; + return msg->type; +} + +static int +ntlm_extract_mem(unsigned char *dst, + unsigned char *src, unsigned srclen, + unsigned char *off, unsigned char *len, + unsigned max) +{ + unsigned o = little_endian_word(off); + unsigned l = little_endian_word(len); + if (l > max) + return -1; + if (o >= srclen) + return -1; + if (o + l > srclen) + return -1; + src += o; + while (l-- > 0) + *dst++ = *src++; + return 0; +} + +static int +ntlm_extract_string(unsigned char *dst, + unsigned char *src, unsigned srclen, + unsigned char *off, unsigned char *len, + unsigned max) +{ + unsigned o = little_endian_word(off); + unsigned l = little_endian_word(len); + if (l > max) + return -1; + if (o >= srclen) + return -1; + if (o + l > srclen) + return -1; + src += o; + while (l-- > 0) { + if(*src != '\0' ) { + *dst = *src; + dst++; + } + src++; + } + *dst = 0; + return 0; +} + +static int +ntlm_put_in_unicode(unsigned char *dst, + unsigned char *src, unsigned srclen, unsigned max) +{ + unsigned l = srclen*2; + if (l > max) + l=max; /* fhz: bad very bad */ + while (l > 0) { + /* ASCII to unicode*/ + *dst++ = *src++; + *dst++=0; + l -=2; + } + return 0; + + + +} + +static int +ntlm_extract_unicode(unsigned char *dst, + unsigned char *src, unsigned srclen, + unsigned char *off, unsigned char *len, + unsigned max) +{ + unsigned o = little_endian_word(off); + unsigned l = little_endian_word(len) / 2; /* Unicode! */ + if (l > max) + return -1; + if (o >= srclen) + return -1; + if (o + l > srclen) + return -1; + src += o; + while (l > 0) { + /* Unicode to ASCII */ + *dst++ = *src; + src += 2; + l -= 2; + } + *dst = 0; + return 0; +} + +static int +ntlm_msg1_getntlmssp_flags(unsigned char *raw_msg, + unsigned char *ntlmssp_flags) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + *ntlmssp_flags=little_endian_word(msg->flags); + return 0; +} + +static int +ntlm_msg1_gethostname(unsigned char *raw_msg, + unsigned msglen, unsigned char *hostname) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + if (ntlm_extract_string(hostname, (char *) msg, msglen, + msg->host_off, msg->host_len, MAX_HOSTLEN)) + return 1; + return 0; +} + +static int +ntlm_msg1_getdomainname(unsigned char *raw_msg, + unsigned msglen, unsigned char *domainname) +{ + struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg; + if (ntlm_extract_string(domainname, (char *) msg, + msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 2; + return 0; +} + +static int +ntlm_msg3_getlm(unsigned char *raw_msg, unsigned msglen, + unsigned char *lm) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlm_extract_mem(lm, (char *) msg, msglen, msg->lm_off, + msg->lm_len, RESP_LEN)) + return 4; + return 0; +} + +static int +ntlm_msg3_getnt(unsigned char *raw_msg, unsigned msglen, + unsigned char *nt) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlm_extract_mem(nt, (char *) msg, msglen, msg->nt_off, + msg->nt_len, RESP_LEN)) + /* Win9x: we can't extract nt ... so we use lm... */ + if (ntlm_extract_mem(nt, (char *) msg, msglen, msg->lm_off, + msg->lm_len, RESP_LEN)) + return 8; + return 0; +} + +static int +ntlm_msg3_getusername(unsigned char *raw_msg, + unsigned msglen, unsigned char *username, + unsigned ntlmssp_flags) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + int c; + if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { + if (ntlm_extract_unicode(username, (char *) msg, msglen, + msg->user_off, msg->user_len, MAX_USERLEN)) + return 16; + } + else { /* ascii */ + if (ntlm_extract_string(username, (char *) msg, msglen, + msg->user_off, msg->user_len, MAX_USERLEN)) + return 16; + else { + /* Win9x client leave username in uppercase...fix it: */ + while (*username!=(unsigned char)NULL) { + c=tolower((int)*username); + *username=(unsigned char)c; + username++; + } + } + } + return 0; +} + +static int +ntlm_msg3_gethostname(unsigned char *raw_msg, unsigned msglen, + unsigned char *hostname,unsigned ntlmssp_flags) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { + if (ntlm_extract_unicode(hostname, (char *) msg, msglen, + msg->host_off, msg->host_len, MAX_HOSTLEN)) + return 0; /* this one FAILS, but since the value is not used, + * we just pretend it was ok. */ + } + else { /* ascii */ + if (ntlm_extract_string(hostname, (char *) msg, msglen, + msg->host_off, msg->host_len, MAX_HOSTLEN)) + return 0; /* this one FAILS, but since the value is not used, + * we just pretend it was ok. */ + } + return 0; +} + +static int +ntlm_msg3_getdomainname(unsigned char *raw_msg, + unsigned msglen, unsigned char *domainname, + unsigned ntlmssp_flags) +{ + struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg; + if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { + if (ntlm_extract_unicode(domainname, (char *) msg, msglen, + msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 64; + } + else { /* asii */ + if (ntlm_extract_string(domainname, (char *) msg, msglen, + msg->dom_off, msg->dom_len, MAX_DOMLEN)) + return 64; + } + return 0; +} + +int ntlmssp_decode_msg(struct ntlmssp_info *info, + unsigned char *raw_msg, unsigned msglen, + unsigned *ntlmssp_flags) +{ + unsigned char flags; + int ret; + switch (info->msg_type = ntlm_msg_type(raw_msg, msglen)) { + case 1: + ret = ntlm_msg1_getntlmssp_flags(raw_msg,&flags); + *ntlmssp_flags = (unsigned) flags; + return ntlm_msg1_gethostname(raw_msg, msglen, info->host) + + ntlm_msg1_getdomainname(raw_msg, msglen, info->domain); + case 3: + return ntlm_msg3_getlm(raw_msg, msglen, info->lm) + + ntlm_msg3_getnt(raw_msg, msglen, info->nt) + + ntlm_msg3_getusername(raw_msg, msglen, info->user,*ntlmssp_flags) + + ntlm_msg3_gethostname(raw_msg, msglen, info->host,*ntlmssp_flags) + + ntlm_msg3_getdomainname(raw_msg, msglen, info->domain,*ntlmssp_flags); + } + return -1; +} + +int ntlmssp_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg) +{ + memset(msg, 0, sizeof(struct ntlm_msg2)); + strcpy(msg->protocol, "NTLMSSP"); + msg->type = 0x02; + set_little_endian_word(msg->msg_len, sizeof(struct ntlm_msg2)); + set_little_endian_word(msg->flags, 0x8201); + memcpy(msg->nonce, nonce, sizeof(msg->nonce)); + return 0; +} + +int ntlmssp_encode_msg2_win9x(unsigned char *nonce, struct ntlm_msg2_win9x *msg,char *domainname,unsigned ntlmssp_flags) +{ + unsigned int size,len,flags; + + memset(msg, 0, sizeof(struct ntlm_msg2_win9x)); + strcpy(msg->protocol, "NTLMSSP"); + msg->type = 0x02; + if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) { + /* unicode case */ + + len=strlen(domainname); + ntlm_put_in_unicode((char *)msg->dom,domainname, + len, MAX_DOMLEN); + len=len*2; + if (len>MAX_DOMLEN) + len=MAX_DOMLEN; /* fhz: bad very bad */ + flags=NTLM_NTLMSSP_NEG_FLAGS | NTLMSSP_NEGOTIATE_UNICODE; + } else { + /* ascii case */ + len=strlen(domainname); + if (len>MAX_DOMLEN) + len=MAX_DOMLEN; /* fhz: bad very bad */ + strncpy(msg->dom,domainname,len); + flags=NTLM_NTLMSSP_NEG_FLAGS; + } + size=NTLM_MSG2_WIN9X_FIXED_SIZE+len; + set_little_endian_word(msg->dom_off, NTLM_MSG2_WIN9X_FIXED_SIZE); + set_little_endian_word(msg->dom_len1,len); + set_little_endian_word(msg->dom_len2,len); + set_little_endian_word(msg->msg_len,size); + set_little_endian_word(msg->flags,flags); + if (ntlmssp_flags & NTLMSSP_REQUEST_TARGET) + set_little_endian_word(msg->zero2, 0x01); /* == set NTLMSSP_TARGET_TYPE_DOMAIN */ + + memcpy(msg->nonce, nonce, sizeof(msg->nonce)); + return size; +} + + +int ntlmssp_validuser(const char* username, const char* password, const char* server, + const char* backup, const 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}; + SMB_Handle_Type con; + + SMB_Init(); + con = SMB_Connect_Server(NULL, (char*)server); + if (con == NULL) { /* Error ... */ + con = SMB_Connect_Server(NULL, (char*)backup); + 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, (char*)username, (char*)password, 0, (char*)domain) < 0) { + SMB_Discon(con, 0); + return (NTV_LOGON_ERROR); + } + SMB_Discon(con, 0); + return (NTV_NO_ERROR); +} + +void* ntlmssp_connect(const char* server, const char* backup, const 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, (char*)server); + if (con == NULL) { /* Error ... */ + con = SMB_Connect_Server(NULL, (char*)backup); + 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 ntlmssp_auth(void* handle, const char* user, const char* password, int flag, char* domain) +{ + if (SMB_Logon_Server(handle, (char*)user, (char*)password, flag, (char*)domain) < 0) { + return (NTV_LOGON_ERROR); + } + return NTV_NO_ERROR; +} + +void ntlmssp_disconnect(void* handle) +{ + SMB_Discon(handle, 0); +} diff --git a/daemon/ntlmssp.h b/daemon/ntlmssp.h new file mode 100644 index 0000000..7654a74 --- /dev/null +++ b/daemon/ntlmssp.h @@ -0,0 +1,140 @@ + +#ifndef __NTLMSSP_H__ +#define __NTLMSSP_H__ + +#define MAX_HOSTLEN 32 +#define MAX_DOMLEN 32 +#define MAX_USERLEN 32 +#define RESP_LEN 24 +#define NONCE_LEN 8 + +/* fhz, 01-10-15 : borrowed from samba code */ +/* NTLMSSP negotiation flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 +#define NTLMSSP_NEGOTIATE_OEM 0x00000002 +#define NTLMSSP_REQUEST_TARGET 0x00000004 +#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 +#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 +#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 +#define NTLMSSP_NEGOTIATE_00001000 0x00001000 +#define NTLMSSP_NEGOTIATE_00002000 0x00002000 +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 +#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 + +#define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1 +#define NTLM_NTLMSSP_NEG_FLAGS 0x00008206 +/* 8201 8207 */ + + + +#define LEN_NTLMSSP_FLAGS 4 +#define OFFSET_MSG1_NTLMSSP_FLAGS 12 + +struct ntlm_msg1 { + unsigned char protocol[8]; + unsigned char type; /* 1 */ + unsigned char zero1[3]; + unsigned char flags[2]; + unsigned char zero2[2]; + + unsigned char dom_len[4]; + unsigned char dom_off[4]; + + unsigned char host_len[4]; + unsigned char host_off[4]; + +#if 0 + unsigned char data[0]; +#endif +} __attribute__((packed)); + +struct ntlm_msg2 { + unsigned char protocol[8]; + unsigned char type; /* 2 */ + unsigned char zero1[7]; + unsigned char msg_len[4]; + unsigned char flags[2]; + unsigned char zero2[2]; + + unsigned char nonce[8]; + unsigned char zero3[8]; +} __attribute__((packed)); + +struct ntlm_msg3 { + unsigned char protocol[8]; + unsigned char type; /* 3 */ + unsigned char zero1[3]; + + unsigned char lm_len[4]; + unsigned char lm_off[4]; + + unsigned char nt_len[4]; + unsigned char nt_off[4]; + + unsigned char dom_len[4]; + unsigned char dom_off[4]; + + unsigned char user_len[4]; + unsigned char user_off[4]; + + unsigned char host_len[4]; + unsigned char host_off[4]; + + unsigned char msg_len[4]; /* Win9x: data begins here! */ + +#if 0 + unsigned char data[0]; +#endif +} __attribute__((packed)); + +struct ntlm_msg2_win9x { + unsigned char protocol[8]; + unsigned char type; /* 2 */ + unsigned char zero1[3]; + unsigned char dom_len1[2]; + unsigned char dom_len2[2]; + unsigned char dom_off[4]; + unsigned char flags[2]; + unsigned char zero2[2]; + + unsigned char nonce[8]; + unsigned char zero3[8]; + unsigned char zero4[4]; + unsigned char msg_len[4]; + unsigned char dom[MAX_DOMLEN]; +} __attribute__((packed)); + +/* size without dom[] : */ +#define NTLM_MSG2_WIN9X_FIXED_SIZE (sizeof(struct ntlm_msg2_win9x)-MAX_DOMLEN) + + +typedef struct ntlmssp_info { + int msg_type; + unsigned char user[MAX_USERLEN + 1]; + unsigned char host[MAX_HOSTLEN + 1]; + unsigned char domain[MAX_DOMLEN + 1]; + unsigned char lm[RESP_LEN]; + unsigned char nt[RESP_LEN]; +} ntlmssp_info_rec; + +int ntlmssp_decode_msg(struct ntlmssp_info *info, unsigned char *raw_msg, unsigned msglen, unsigned *ntlmssp_flags); +int ntlmssp_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg); +int ntlmssp_encode_msg2_win9x(unsigned char *nonce, struct ntlm_msg2_win9x *msg,char *domainname,unsigned ntlmssp_flags); + +#define NTV_NO_ERROR 0 +#define NTV_SERVER_ERROR 1 +#define NTV_PROTOCOL_ERROR 2 +#define NTV_LOGON_ERROR 3 + +int ntlmssp_validuser(const char* username, const char* password, const char* server, const char* backup, const char* domain); +void* ntlmssp_connect(const char* server, const char* backup, const char* domain, char* nonce); +int ntlmssp_auth(void* handle, const char* user, const char* password, int flag, char* domain); +void ntlmssp_disconnect(void* handle); + +#endif /* __NTLMSSP_H__ */ diff --git a/daemon/rfcnb/byteorder.h b/daemon/rfcnb/byteorder.h new file mode 100644 index 0000000..2dae575 --- /dev/null +++ b/daemon/rfcnb/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/daemon/rfcnb/rfcnb-common.h b/daemon/rfcnb/rfcnb-common.h new file mode 100644 index 0000000..0d7d5dd --- /dev/null +++ b/daemon/rfcnb/rfcnb-common.h @@ -0,0 +1,36 @@ +/* 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/daemon/rfcnb/rfcnb-error.h b/daemon/rfcnb/rfcnb-error.h new file mode 100644 index 0000000..bb49d68 --- /dev/null +++ b/daemon/rfcnb/rfcnb-error.h @@ -0,0 +1,75 @@ +/* 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 */ + +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/daemon/rfcnb/rfcnb-io.c b/daemon/rfcnb/rfcnb-io.c new file mode 100644 index 0000000..db2437f --- /dev/null +++ b/daemon/rfcnb/rfcnb-io.c @@ -0,0 +1,407 @@ +/* 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 "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) + +{ + /* 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; + memset(&(inact.sa_mask), 0, sizeof(inact.sa_mask)); + 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 -> err = 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/daemon/rfcnb/rfcnb-io.h b/daemon/rfcnb/rfcnb-io.h new file mode 100644 index 0000000..9af8e90 --- /dev/null +++ b/daemon/rfcnb/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/daemon/rfcnb/rfcnb-priv.h b/daemon/rfcnb/rfcnb-priv.h new file mode 100644 index 0000000..3541c0e --- /dev/null +++ b/daemon/rfcnb/rfcnb-priv.h @@ -0,0 +1,151 @@ +/* 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 err; /* 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/daemon/rfcnb/rfcnb-util.c b/daemon/rfcnb/rfcnb-util.c new file mode 100644 index 0000000..adcc092 --- /dev/null +++ b/daemon/rfcnb/rfcnb-util.c @@ -0,0 +1,532 @@ +/* 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" + +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 */ + +int 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 ln1[16], ln2[16], n1[32], n2[32], 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/daemon/rfcnb/rfcnb-util.h b/daemon/rfcnb/rfcnb-util.h new file mode 100644 index 0000000..b3f2315 --- /dev/null +++ b/daemon/rfcnb/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/daemon/rfcnb/rfcnb.h b/daemon/rfcnb/rfcnb.h new file mode 100644 index 0000000..a7cfe1f --- /dev/null +++ b/daemon/rfcnb/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/daemon/rfcnb/session.c b/daemon/rfcnb/session.c new file mode 100644 index 0000000..981fda8 --- /dev/null +++ b/daemon/rfcnb/session.c @@ -0,0 +1,364 @@ +/* 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" + +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 -> err = 0; /* no error yet */ + con -> timeout = 0; /* no timeout */ + con -> redirects = 0; + + /* 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; struct RFCNB_Hdr *hdr; + 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 ... */ + +int 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/daemon/rfcnb/std-includes.h b/daemon/rfcnb/std-includes.h new file mode 100644 index 0000000..e90e60a --- /dev/null +++ b/daemon/rfcnb/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/daemon/rfcnb/x_Makefile b/daemon/rfcnb/x_Makefile new file mode 100644 index 0000000..97a01be --- /dev/null +++ b/daemon/rfcnb/x_Makefile @@ -0,0 +1,38 @@ +# Find the LDFLAGS entry for your system type and uncomment it ... + +CC = gcc + +#CFLAGS = -g -DRFCNB_DEBUG +# Uncomment the above and recomment the below if you want debugging +CFLAGS = -g + +#CFLAGS = -g -DRFCNB_DEBUG -DRFCNB_PRINT_DATA +# Different LDFLAGS for different systems: +# ULTRIX and Digital UNIX (OSF/1) +# LDFALGS = +# +# Linux +# LDFLAGS = +# +# Solaris and maybe SunOS??? +# LDFLAGS = -lsocket -lnsl +# +# HP-UX ??? +# LDFLAGS = ??? + +INCLUDES = rfcnb.h rfcnb-priv.h rfcnb-util.h rfcnb-io.h + +.SUFFIXES: .c .o .h + +all: test_rfcnb + +.c.o: $(INCLUDES) + @echo Compiling $*.c + @$(CC) $(CFLAGS) -c $*.c + +test_rfcnb: test_rfcnb.o session.o rfcnb-util.o rfcnb-io.o + $(CC) $(CFLAGS) $(LDFLAGS) -o test_rfcnb test_rfcnb.o session.o rfcnb-util.o rfcnb-io.o + +clean: + rm *.o test_rfcnb + diff --git a/daemon/smblib/exper.c b/daemon/smblib/exper.c new file mode 100644 index 0000000..13f9ba6 --- /dev/null +++ b/daemon/smblib/exper.c @@ -0,0 +1,748 @@ +/* UNIX SMBlib NetBIOS implementation + + Version 1.0 + SMBlib Routines. Experimental Section ... + + 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/rfcnb.h" + +#include + + +/* Logon and tree connect to the server. If a tree handle was given to us, */ +/* we use it and return it, otherwise we create one ... */ + +SMB_Tree_Handle SMB_Logon_And_TCon(SMB_Handle_Type Con_Handle, + SMB_Tree_Handle Tree_Handle, + char *UserName, + char *PassWord, + char *service, + char *service_type) + +{ struct RFCNB_Pkt *pkt; + int param_len, i, pkt_len, andx_len, andx_param_len; + char *p, *AndXCom; + SMB_Tree_Handle tree; + + /* Lets create a tree if we need one ... */ + + if (Tree_Handle == NULL) { + + tree = (SMB_Tree_Handle)malloc(sizeof(struct SMB_Tree_Structure)); + + if (tree == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + return(tree); + + } + else { /* Initialize the tree */ + + tree -> con = Con_Handle; + tree -> prev = tree -> next = NULL; + + } + } + else + tree = Tree_Handle; + + /* 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 ... */ + + /* Since we are going to do a LogonAndX with a TCon as the second command*/ + /* We need the packet size correct. So TCon starts at wct field */ + + if (Con_Handle -> protocol < SMB_P_LanMan1) { + + SMBlib_errno = SMBlibE_ProtLow; + if (Tree_Handle == NULL) + free(tree); + return(NULL); + + } + + /* Now build the correct structure */ + + andx_len = SMB_tconx_len - SMB_hdr_wct_offset; + + /* We send a null password as we sent one in the setup and X */ + + andx_param_len = strlen(service) + 1 + strlen(service_type) + 1; + + if (Con_Handle -> protocol < SMB_P_NT1) { + +#ifdef SMBLIB_DEBUG + fprintf(stderr, "Doing an LM session setup etc ...\n"); +#endif + + /* We don't do encrypted passwords ... */ + + param_len = strlen(UserName) + 1 + strlen(PassWord) + 1 + + strlen(Con_Handle -> PDomain) + 1 + + strlen(Con_Handle -> OSName) + 1; + + pkt_len = SMB_ssetpLM_len + param_len + andx_len + andx_param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + if (Tree_Handle == NULL) + free(tree); + return(NULL); /* 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, 0); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10; + *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = SMBtconX; + SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, SMB_ssetpLM_len + param_len); + + 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, strlen(PassWord) + 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 no null at end */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord) + 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; + + AndXCom = SMB_Hdr(pkt) + SMB_ssetpLM_len + param_len - SMB_hdr_wct_offset; + + } + else { + + /* We don't admit to UNICODE support ... */ + +#ifdef SMBLIB_DEBUG + fprintf(stderr, "Doing NT LM Sess Setup etc ... \n"); +#endif + + param_len = strlen(UserName) + 1 + strlen(PassWord) + + strlen(Con_Handle -> PDomain) + 1 + + strlen(Con_Handle -> OSName) + 1 + + strlen(Con_Handle -> LMType) + 1; + + pkt_len = SMB_ssetpNTLM_len + param_len + andx_len + andx_param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + if (Tree_Handle == NULL) + free(tree); + return(NULL); /* 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, 0); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13; + *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = SMBtconX; + SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, SMB_ssetpNTLM_len + param_len); + + SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT); + SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 2); + 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, strlen(PassWord)); + 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 */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord); + + 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 set up the TCON Part ... from WCT, make up a pointer that will + help us ... */ + + AndXCom = SMB_Hdr(pkt) + SMB_ssetpNTLM_len + param_len - SMB_hdr_wct_offset; + + } + *(AndXCom + SMB_hdr_wct_offset) = 4; + *(AndXCom + SMB_tconx_axc_offset) = 0xFF; /* No command */ + SSVAL(AndXCom, SMB_tconx_axo_offset, 0); + SSVAL(AndXCom, SMB_tconx_flg_offset, 0); /* Don't disconnect TID */ + SSVAL(AndXCom, SMB_tconx_pwl_offset, 0); /* No password, */ + SSVAL(AndXCom, SMB_tconx_bcc_offset, andx_param_len); + + p = (char *)(AndXCom + SMB_tconx_buf_offset); + + /**p = 0; + p = p + 1; */ + strcpy(p, service); + p = p + strlen(service) + 1; + strcpy(p, service_type); + + /* Now send it and get a response */ + + if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending SessSetupAndTCon request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + free(tree); + 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 SessSetupAndTCon\n"); +#endif + + RFCNB_Free_Pkt(pkt); + free(tree); + 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_SessSetupAndTCon 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 + + /* Note, here, that we have not properly handled the error processing */ + /* and so we cannot tell how much of our request crapped out */ + + SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset); + free(tree); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_Remote; + return(NULL); + + } + +#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); + + /* And pick up the TID as well */ + + tree -> tid = SVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset); + + tree -> mbs = Con_Handle -> max_xmit; + + /* Link the tree into the list in con */ + + 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); + +} + +/* Logon and TCon and Open to a file on the server, but we need to pass */ +/* back a file pointer, so we better have one in the parameter list */ + +int SMB_Logon_TCon_Open(SMB_Handle_Type Con_Handle, char *UserName, + char *PassWord, + char *service, + char *service_type, + SMB_Tree_Handle *Tree_Handle, + char *filename, + WORD mode, + WORD search, + SMB_File **File_Handle) + +{ struct RFCNB_Pkt *pkt; + int param_len, i, pkt_len, tcon_len, tcon_param_len, open_len, + open_param_len, header_len; + struct SMB_File_Def *file_tmp; + SMB_Tree_Handle tree; + char *p, *AndXCom; + + /* First, we need a tree STRUCTURE as we are going to tree connect */ + + tree = (SMB_Tree_Handle)malloc(sizeof(struct SMB_Tree_Structure)); + + if (tree == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + else { + + tree -> con = Con_Handle; + tree -> next = tree -> prev = NULL; + + } + + /* Next, we need a file handle as we are going to pass one back ... */ + /* Hmm, there is a bug here ... We should check on File_Handle ... */ + + if ((file_tmp = (SMB_File *)malloc(sizeof(SMB_File))) == NULL){ + +#ifdef DEBUG + fprintf(stderr, "Could not allocate file handle space ..."); +#endif + + SMBlib_errno = SMBlibE_NoSpace; + free(tree); + return(SMBlibE_BAD); + + } + + /* Next 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 ... */ + + /* Since we are going to do a LogonAndX with a TCon as the second command*/ + /* We need the packet size correct. So TCon starts at wct field */ + + if (Con_Handle -> protocol < SMB_P_LanMan1) { + + free(tree); + free(file_tmp); + SMBlib_errno = SMBlibE_ProtLow; + return(SMBlibE_BAD); + + } + + /* Now build the correct structure */ + + /* We send a null password in the TconAndX ... */ + + tcon_len = SMB_tconx_len - SMB_hdr_wct_offset; + tcon_param_len = strlen(service) + 1 + strlen(service_type) + 1; + + open_len = SMB_openx_len - SMB_hdr_wct_offset; + open_param_len = 1 + strlen(filename) + 1; /* AsciiID + null */ + + if (Con_Handle -> protocol < SMB_P_NT1) { + + /* We don't do encrypted passwords yet */ + + param_len = strlen(UserName) + 1 + strlen(PassWord) + 1 + + strlen(Con_Handle -> PDomain) + 1 + + strlen(Con_Handle -> OSName) + 1; + + header_len = SMB_ssetpLM_len + param_len; + + pkt_len = header_len + tcon_len + tcon_param_len + + open_len + open_param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + free(tree); + free(file_tmp); + 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, 0); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10; + *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = SMBtconX; + SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, SMB_ssetpLM_len + param_len); + + 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, strlen(PassWord) + 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 no null at end */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord) + 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; + + AndXCom = SMB_Hdr(pkt) + SMB_ssetpLM_len + param_len - SMB_hdr_wct_offset; + + } + else { + + /* We don't admit to UNICODE support ... */ + + param_len = strlen(UserName) + 1 + strlen(PassWord) + + strlen(Con_Handle -> PDomain) + 1 + + strlen(Con_Handle -> OSName) + 1 + + strlen(Con_Handle -> LMType) + 1; + + header_len = SMB_ssetpNTLM_len + param_len; + + pkt_len = header_len + tcon_len + tcon_param_len + + open_len + open_param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + free(tree); + free(file_tmp); /* Should only do if we created one ... */ + 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, 0); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13; + *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = SMBtconX; + SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, SMB_ssetpNTLM_len + param_len); + + SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT); + SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 2); + 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, strlen(PassWord)); + 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 */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord); + + 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 set up the TCON Part ... from WCT, make up a pointer that will + help us ... */ + + AndXCom = SMB_Hdr(pkt) + SMB_ssetpNTLM_len + param_len - SMB_hdr_wct_offset; + + } + + *(AndXCom + SMB_hdr_wct_offset) = 4; + *(AndXCom + SMB_tconx_axc_offset) = SMBopenX; + SSVAL(AndXCom, SMB_tconx_axo_offset, (header_len + + tcon_len + tcon_param_len)); + SSVAL(AndXCom, SMB_tconx_flg_offset, 0); /* Don't disconnect TID */ + SSVAL(AndXCom, SMB_tconx_pwl_offset, 0); /* No password */ + SSVAL(AndXCom, SMB_tconx_bcc_offset, tcon_param_len); + + p = (char *)(AndXCom + SMB_tconx_buf_offset); + +/* *p = 0; + p = p + 1; */ + strcpy(p, service); + p = p + strlen(service) + 1; + strcpy(p, service_type); + + /* Now the open bit ... */ + + AndXCom = AndXCom + tcon_len + tcon_param_len; /* Should get us there */ + + *(AndXCom + SMB_hdr_wct_offset) = 15; + *(AndXCom + SMB_openx_axc_offset) = 0xFF; + *(AndXCom + SMB_openx_axr_offset) = 0; + SSVAL(AndXCom, SMB_openx_axo_offset, 0); + SSVAL(AndXCom, SMB_openx_flg_offset, 0); + SSVAL(AndXCom, SMB_openx_mod_offset, mode); + SSVAL(AndXCom, SMB_openx_atr_offset, search); + SSVAL(AndXCom, SMB_openx_fat_offset, 0); + SIVAL(AndXCom, SMB_openx_tim_offset, 0); + SSVAL(AndXCom, SMB_openx_ofn_offset, 0x0011); /* Create or open */ + SIVAL(AndXCom, SMB_openx_als_offset, 0); + SSVAL(AndXCom, SMB_openx_bcc_offset, open_param_len); + + p = (char *)(AndXCom + SMB_openx_buf_offset); + + /* *p = SMBasciiID; */ + strcpy(p, filename); + + /* Now send it and get a response */ + + if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending SessSetupAndTCon request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + free(tree); + free(file_tmp); + 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 SessSetupAndTCon\n"); +#endif + + RFCNB_Free_Pkt(pkt); + free(tree); + free(file_tmp); + 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_SessSetupAndTCon 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 + + /* Note, here, that we have not properly handled the error processing */ + /* and so we cannot tell how much of our request crapped out */ + + SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset); + RFCNB_Free_Pkt(pkt); + free(tree); + free(file_tmp); + 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); + + /* And pick up the TID as well */ + + tree -> tid = SVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset); + tree -> mbs = Con_Handle -> max_xmit; /* We need this */ + +#ifdef DEBUG + fprintf(stderr, "mbs=%i\n", tree -> mbs); +#endif + + /* Now we populate the file hanble and pass it back ... */ + + strncpy(file_tmp -> filename, filename, sizeof(file_tmp -> filename) - 1); + file_tmp -> tree = tree; + + /* Pick up a pointer to the right part ... */ + + AndXCom = SMB_Hdr(pkt) + SVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset) - + SMB_hdr_wct_offset; + + /* Now skip the response to the TConX */ + + AndXCom = SMB_Hdr(pkt) + SVAL(AndXCom, SMB_tconxr_axo_offset) - + SMB_hdr_wct_offset; + +#ifdef DEBUG + fprintf(stderr, "Word Params = %x, AXO = %x\n", + CVAL(AndXCom, SMB_hdr_wct_offset), + SVAL(AndXCom, SMB_openxr_axo_offset)); +#endif + + /* Now pick up the things from the openX response that we need */ + + file_tmp -> fid = SVAL(AndXCom, SMB_openxr_fid_offset); + file_tmp -> lastmod = IVAL(AndXCom, SMB_openxr_tim_offset); + file_tmp -> size = IVAL(AndXCom, SMB_openxr_fsz_offset); + file_tmp -> access = SVAL(AndXCom, SMB_openxr_acc_offset); + file_tmp -> fileloc = 0; + + *File_Handle = file_tmp; + + /* Now link the tree into the right place ... */ + + 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); + + *Tree_Handle = tree; + + return(0); + +} + diff --git a/daemon/smblib/file.c b/daemon/smblib/file.c new file mode 100644 index 0000000..6a62da5 --- /dev/null +++ b/daemon/smblib/file.c @@ -0,0 +1,1306 @@ +/* UNIX SMBlib NetBIOS implementation + + Version 1.0 + SMBlib File Access 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/rfcnb.h" + +/* Open a file with file_name using desired mode and search attr */ +/* If File_Handle is null, then create and populate a file handle */ + +SMB_File *SMB_Open(SMB_Tree_Handle Tree_Handle, + SMB_File *File_Handle, + char *file_name, + WORD mode, + WORD search) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; char *p; + struct SMB_File_Def *file_tmp; + + /* We allocate a file object and copy some things ... */ + + file_tmp = File_Handle; + + if (File_Handle == NULL) { + + if ((file_tmp = (SMB_File *)malloc(sizeof(SMB_File))) == NULL){ + +#ifdef DEBUG + fprintf(stderr, "Could not allocate file handle space ..."); +#endif + + SMBlib_errno = SMBlibE_NoSpace; + return(NULL); + + } + + } + + strncpy(file_tmp -> filename, file_name, sizeof(file_tmp -> filename) - 1); + file_tmp -> tree = Tree_Handle; + file_tmp -> fid = 0xFFFF; /* Is this an invalid FID? */ + + param_len = strlen(file_name) + 2; /* 1 for null, 1 for ASCII marker */ + + pkt_len = SMB_open_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(max(pkt_len, SMB_openr_len)); + + if (pkt == NULL) { /* Really should do some error handling */ + + if (File_Handle == NULL) + free(file_tmp); + SMBlib_errno = SMBlibE_NoSpace; + return(NULL); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_open_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBopen; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Tree_Handle -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, Tree_Handle -> tid); + 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) = 2; + + SSVAL(SMB_Hdr(pkt), SMB_open_mod_offset, mode); + SSVAL(SMB_Hdr(pkt), SMB_open_atr_offset, search); + SSVAL(SMB_Hdr(pkt), SMB_open_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_open_buf_offset); + *p = SMBasciiID; + strcpy(p+1, file_name); + p = p + strlen(file_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Open request\n"); +#endif + + if (File_Handle == NULL) + free(file_tmp); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(NULL); + + } + + /* Now get the response ... */ + +#ifdef DEBUG + fprintf(stderr, "Pkt_Len for Open resp = %i\n", pkt_len); +#endif + + if (RFCNB_Recv(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to open request\n"); +#endif + + if (File_Handle = NULL) + free(file_tmp); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(NULL); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Open 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 (File_Handle = NULL) + free(file_tmp); + SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_Remote; + return(NULL); /* Should clean up ... */ + + } + + file_tmp -> fid = SVAL(SMB_Hdr(pkt), SMB_openr_fid_offset); + file_tmp -> lastmod = IVAL(SMB_Hdr(pkt), SMB_openr_tim_offset); + file_tmp -> size = IVAL(SMB_Hdr(pkt), SMB_openr_fsz_offset); + file_tmp -> access = SVAL(SMB_Hdr(pkt), SMB_openr_acc_offset); + file_tmp -> fileloc = 0; + + RFCNB_Free_Pkt(pkt); /* Free up this space */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Open succeeded, FID = %i\n", file_tmp -> fid); +#endif + + RFCNB_Free_Pkt(pkt); + + return(file_tmp); + +} + +/* Close the file referred to in File_Handle */ + +int SMB_Close(SMB_File *File_Handle) + +{ struct SMB_Close_Prot_Def *prot_pkt; + struct SMB_Hdr_Def_LM12 *resp_pkt; + struct RFCNB_Pkt *pkt; + int pkt_len; + + if (File_Handle == NULL) { /* Error */ + + /*SMBLIB_errno = SMBLIBE_BadHandle; */ + return(-1); + + } + + pkt_len = SMB_clos_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_clos_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBclose; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, File_Handle -> tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, File_Handle -> tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, File_Handle -> tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, File_Handle -> tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 3; + + SSVAL(SMB_Hdr(pkt), SMB_clos_fid_offset, File_Handle -> fid); + SIVAL(SMB_Hdr(pkt), SMB_clos_tim_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_clos_bcc_offset, 0); + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(File_Handle -> tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Open request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(File_Handle -> tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to open request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Close 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); /* Should clean up ... */ + + } + +#ifdef DEBUG + fprintf(stderr, "File %s closed successfully.\n", File_Handle -> filename); +#endif /* DEBUG */ + + /* We should deallocate the File_Handle now ... */ + + File_Handle -> tree = NULL; + File_Handle -> filename[0] = 0; + File_Handle -> fid = 0xFFFF; + + RFCNB_Free_Pkt(pkt); + free(File_Handle); + + return(0); +} + +/* Read numbytes into data from the file pointed to by File_Handle from */ +/* the offset in the File_Handle. */ + +int SMB_Read(SMB_File *File_Handle, char *data, int numbytes) + +{ int tot_read; + struct RFCNB_Pkt *snd_pkt, *recv_pkt, *data_ptr; + int snd_pkt_len, recv_pkt_len, this_read, bytes_left = numbytes; + int max_read_data, bytes_read = 0; + + /* We loop around, reading the data, accumulating it into the buffer */ + /* We build an SMB packet, where the data is pointed to by a fragment*/ + /* tagged onto the end */ + + data_ptr = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(0); + if (data_ptr == NULL) { + + /* We should handle the error here */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + snd_pkt_len = SMB_read_len; /* size for the read SMB */ + recv_pkt_len = SMB_readr_len + 3; /* + 3 for the datablockID and blklen */ + + snd_pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(snd_pkt_len); + + if (snd_pkt == NULL) { + + RFCNB_Free_Pkt(data_ptr); + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + recv_pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(recv_pkt_len); + + if (recv_pkt == NULL) { + + RFCNB_Free_Pkt(snd_pkt); + RFCNB_Free_Pkt(data_ptr); + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Put the recv pkt together */ + + recv_pkt -> next = data_ptr; + + /* Now build the read request and the receive packet etc ... */ + + bzero(SMB_Hdr(snd_pkt), SMB_read_len); + SIVAL(SMB_Hdr(snd_pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(snd_pkt) + SMB_hdr_com_offset) = SMBread; + SSVAL(SMB_Hdr(snd_pkt), SMB_hdr_pid_offset, File_Handle -> tree -> con -> pid); + SSVAL(SMB_Hdr(snd_pkt), SMB_hdr_tid_offset, File_Handle -> tree -> tid); + SSVAL(SMB_Hdr(snd_pkt), SMB_hdr_mid_offset, File_Handle -> tree -> con -> mid); + SSVAL(SMB_Hdr(snd_pkt), SMB_hdr_uid_offset, File_Handle -> tree -> con -> uid); + *(SMB_Hdr(snd_pkt) + SMB_hdr_wct_offset) = 5; + SSVAL(SMB_Hdr(snd_pkt), SMB_read_fid_offset, File_Handle -> fid); + + max_read_data = (File_Handle -> tree -> mbs) - recv_pkt_len; + + while (bytes_left > 0) { + + this_read = (bytes_left > max_read_data?max_read_data: bytes_left); + + SSVAL(SMB_Hdr(snd_pkt), SMB_read_cnt_offset, this_read); + SIVAL(SMB_Hdr(snd_pkt), SMB_read_ofs_offset, File_Handle -> fileloc); + SSVAL(SMB_Hdr(snd_pkt), SMB_read_clf_offset, 0x0); + SSVAL(SMB_Hdr(snd_pkt), SMB_read_bcc_offset, 0x0); + + /* Now send the packet and wait for a response */ + + if (RFCNB_Send(File_Handle -> tree -> con -> Trans_Connect, snd_pkt, snd_pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending read request\n"); +#endif + + data_ptr -> data = NULL; + data_ptr -> len = 0; + RFCNB_Free_Pkt(recv_pkt); + RFCNB_Free_Pkt(snd_pkt); + SMBlib_errno = SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... first point the data portion to the right */ + /* place in the read buffer ... what we are doing is ugly */ + + data_ptr -> data = (data + bytes_read); + data_ptr -> len = this_read; + + if (RFCNB_Recv(File_Handle -> tree -> con -> Trans_Connect, recv_pkt, recv_pkt_len + this_read) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to write\n"); +#endif + + data_ptr -> len = 0; + data_ptr -> data = NULL; + RFCNB_Free_Pkt(recv_pkt); + RFCNB_Free_Pkt(snd_pkt); + SMBlib_errno = SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + if (CVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Read failed with errorclass = %i, Error Code = %i\n", + CVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset), + SVAL(SMB_Hdr(recv_pkt), SMB_hdr_err_offset)); +#endif + + SMBlib_SMB_Error = IVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset); + data_ptr -> data = NULL; + data_ptr -> len = 0; + RFCNB_Free_Pkt(recv_pkt); + RFCNB_Free_Pkt(snd_pkt); + SMBlib_errno = SMBlibE_Remote; + return(-1); + + } + + /* Ok, that worked, so update some things here ... */ + + bytes_read = bytes_read + SVAL(SMB_Hdr(recv_pkt), SMB_readr_cnt_offset); + bytes_left = bytes_left - SVAL(SMB_Hdr(recv_pkt), SMB_readr_cnt_offset); + + } + + /* Now free those packet headers that we allocated ... */ + + data_ptr -> data = NULL; /* Since recv_pkt points to data_ptr */ + data_ptr -> len = 0; /* it is freed too */ + RFCNB_Free_Pkt(recv_pkt); + RFCNB_Free_Pkt(snd_pkt); + + return(bytes_read); + +} + +/* Lseek seeks just like the UNIX version does ... */ + +off_t SMB_Lseek(SMB_File *File_Handle, off_t offset, int whence) + +{ + + /* We should check that the file handle is kosher ... We may also blow up + if we get a 64 bit offset ... should avoid wrap-around ... */ + + switch (whence) { + case SEEK_SET: + + File_Handle -> fileloc = offset; + break; + + case SEEK_CUR: + + File_Handle -> fileloc = File_Handle -> fileloc + offset; + break; + + case SEEK_END: + + File_Handle -> fileloc = File_Handle -> size + offset; + break; + + default: + return(-1); + + } + + return(File_Handle -> fileloc); + +} + + +/* Write numbytes from data to the file pointed to by the File_Handle at */ +/* the offset in the File_Handle. */ + +int SMB_Write(SMB_File *File_Handle, char *data, int numbytes) + +{ int tot_written = 0; + struct RFCNB_Pkt *pkt, *data_ptr; + int pkt_len, i, this_write, max_write_data, bytes_left = numbytes; + + /* We loop around, writing the data, accumulating what was written */ + /* We build an SMB packet, where the data is pointed to by a fragment */ + /* tagged onto the end ... */ + + data_ptr = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(0); + if (data_ptr == NULL) { + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + pkt_len = SMB_write_len + 3; /* + 3 for the datablockID and blklen */ + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { + + RFCNB_Free_Pkt(data_ptr); + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now init the things that will be the same across the possibly multiple + packets to write this data. */ + + bzero(SMB_Hdr(pkt), SMB_write_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBwrite; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, File_Handle -> tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, File_Handle -> tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, File_Handle -> tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, File_Handle -> tree -> con -> uid); + SSVAL(SMB_Hdr(pkt), SMB_write_fid_offset, File_Handle -> fid); + + /* We will program this as send/response for the moment, but if we could + only send the second block before getting the first, we could speed + things up a bit ... */ + + max_write_data = (File_Handle -> tree -> mbs) - pkt_len; + + /* the 3 is for the data block id and length that preceeds the data */ + + while (bytes_left > 0) { + + /* bytes to write? */ + + this_write = (bytes_left > max_write_data?max_write_data:bytes_left); + + data_ptr -> next = NULL; + data_ptr -> len = this_write; + data_ptr -> data = data + tot_written; + + pkt -> next = data_ptr; /* link the data on the end */ + + SSVAL(SMB_Hdr(pkt), SMB_hdr_flg_offset, 0); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 5; + SSVAL(SMB_Hdr(pkt), SMB_write_fid_offset, File_Handle -> fid); + SSVAL(SMB_Hdr(pkt), SMB_write_cnt_offset, this_write); + SIVAL(SMB_Hdr(pkt), SMB_write_ofs_offset, File_Handle -> fileloc); + SSVAL(SMB_Hdr(pkt), SMB_write_clf_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_write_bcc_offset, (this_write + 3)); + + *(SMB_Hdr(pkt) + SMB_write_buf_offset) = SMBdatablockID; + SSVAL(SMB_Hdr(pkt), SMB_write_buf_offset + 1, this_write); + + /* Now send the packet and wait for a response */ + + if (RFCNB_Send(File_Handle -> tree -> con -> Trans_Connect, pkt, pkt_len + this_write) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending write request\n"); +#endif + + data_ptr -> next = NULL; + data_ptr -> len = 0; + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(-1); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(File_Handle -> tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to write\n"); +#endif + + data_ptr -> next = NULL; + data_ptr -> len = 0; + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(-1); + + } + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Write 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); + data_ptr -> data = NULL; + data_ptr -> len = 0; + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_Remote; + return(SMBlibE_BAD); + + } + + /* Ok, that worked, so update some things here ... */ + + tot_written = tot_written + this_write; + bytes_left = bytes_left - this_write; + + /* Assume that it is ok to update this now, but what about only part */ + /* of the write succeeding? */ + + File_Handle -> fileloc = File_Handle -> fileloc + this_write; + +#ifdef DEBUG + fprintf(stderr, "--This_write = %i, bytes_left = %i\n", + this_write, bytes_left); +#endif + + } + + /* Let's get rid of those packet headers we are using ... */ + + data_ptr -> data = NULL; + pkt -> next = NULL; + + RFCNB_Free_Pkt(pkt); + + return(tot_written); + +} + +/* Create file on the server with name file_name and attributes search */ + +SMB_File *SMB_Create(SMB_Tree_Handle Tree_Handle, + SMB_File *File_Handle, + char *file_name, + WORD search) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; char *p; + struct SMB_File_Def *file_tmp; + + /* We allocate a file object and copy some things ... */ + + file_tmp = File_Handle; + + if (File_Handle == NULL) { + + if ((file_tmp = (SMB_File *)malloc(sizeof(SMB_File))) == NULL){ + +#ifdef DEBUG + fprintf(stderr, "Could not allocate file handle space ..."); +#endif + + SMBlib_errno = SMBlibE_NoSpace; + return(NULL); + + } + + } + + strncpy(file_tmp -> filename, file_name, sizeof(file_tmp -> filename)); + file_tmp -> tree = Tree_Handle; + file_tmp -> fid = 0xFFFF; /* Is this an invalid FID? */ + + param_len = strlen(file_name) + 2; /* 1 for null, 1 for ASCII marker */ + + pkt_len = SMB_creat_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + if (File_Handle == NULL) + free(file_tmp); + SMBlib_errno = SMBlibE_NoSpace; + return(NULL); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_creat_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBcreate; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Tree_Handle -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, Tree_Handle -> tid); + 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) = 3; + + SSVAL(SMB_Hdr(pkt), SMB_creat_atr_offset, search); + SSVAL(SMB_Hdr(pkt), SMB_creat_tim_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_creat_dat_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_creat_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_creat_buf_offset); + *p = SMBasciiID; + strcpy(p+1, file_name); + p = p + strlen(file_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Open request\n"); +#endif + + if (File_Handle == NULL) + free(file_tmp); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(NULL); + + } + + /* Now get the response ... */ + +#ifdef DEBUG + fprintf(stderr, "Pkt_Len for Create resp = %i\n", pkt_len); +#endif + + if (RFCNB_Recv(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to create request\n"); +#endif + + if (File_Handle == NULL) + free(file_tmp); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(NULL); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Create 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 (File_Handle == NULL) + free(file_tmp); + SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset); + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_Remote; + return(NULL); /* Should clean up ... */ + + } + + file_tmp -> fid = SVAL(SMB_Hdr(pkt), SMB_creatr_fid_offset); + file_tmp -> lastmod = 0; + file_tmp -> size = 0; + file_tmp -> access = SMB_AMODE_OPENRW; + file_tmp -> fileloc = 0; + + RFCNB_Free_Pkt(pkt); /* Free up this space */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Create succeeded, FID = %i\n", file_tmp -> fid); +#endif + + return(file_tmp); + +} + +/* Delete the file passed in as file_name. */ + +int SMB_Delete(SMB_Tree_Handle tree, char *file_name, WORD search) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; + char *p; + + param_len = strlen(file_name) + 2; + pkt_len = SMB_delet_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_delet_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBunlink; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 1; + + SIVAL(SMB_Hdr(pkt), SMB_delet_sat_offset, search); + SSVAL(SMB_Hdr(pkt), SMB_delet_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_delet_buf_offset); + *p = SMBasciiID; + strcpy(p+1, file_name); + p = p + strlen(file_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Delete request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to delete request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Delete 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); /* Should clean up ... */ + + } + +#ifdef DEBUG + fprintf(stderr, "File %s deleted successfully.\n", file_name); +#endif /* DEBUG */ + + RFCNB_Free_Pkt(pkt); + + return(0); +} + +/* Create the directory passed in as dir_name */ + +int SMB_Create_Dir(SMB_Tree_Handle tree, char *dir_name) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; + char *p; + + param_len = strlen(dir_name) + 2; /* + null and + asciiID */ + pkt_len = SMB_creatdir_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_creatdir_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBmkdir; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0; + + SSVAL(SMB_Hdr(pkt), SMB_creatdir_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_creatdir_buf_offset); + *p = SMBasciiID; + strcpy(p+1, dir_name); + p = p + strlen(dir_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Create Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Create Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Create_Dir 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); /* Should clean up ... */ + + } + +#ifdef DEBUG + fprintf(stderr, "Directory %s created successfully.\n", dir_name); +#endif + + RFCNB_Free_Pkt(pkt); + + return(0); +} + +/* Delete the directory passed as dir_name, as long as it is empty ... */ + +int SMB_Delete_Dir(SMB_Tree_Handle tree, char *dir_name) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; + char *p; + + param_len = strlen(dir_name) + 2; /* + null and + asciiID */ + pkt_len = SMB_deletdir_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_deletdir_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBrmdir; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0; + + SSVAL(SMB_Hdr(pkt), SMB_deletdir_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_deletdir_buf_offset); + *p = SMBasciiID; + strcpy(p+1, dir_name); + p = p + strlen(dir_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Delete Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Delete Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Delete_Dir 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); /* Should clean up ... */ + + } + +#ifdef DEBUG + fprintf(stderr, "Directory %s deleted successfully.\n", dir_name); +#endif + + RFCNB_Free_Pkt(pkt); + + return(0); +} + +/* Check for the existence of the directory in dir_name */ + +int SMB_Check_Dir(SMB_Tree_Handle tree, char *dir_name) + +{ struct RFCNB_Pkt *pkt; + int pkt_len, param_len; + char *p; + + param_len = strlen(dir_name) + 2; /* + null and + asciiID */ + pkt_len = SMB_checkdir_len + param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_checkdir_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBchkpth; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0; + + SSVAL(SMB_Hdr(pkt), SMB_checkdir_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_checkdir_buf_offset); + *p = SMBasciiID; + strcpy(p+1, dir_name); + p = p + strlen(dir_name); + *(p+1) = 0; /* plug in a null ... */ + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Check Dir Path request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Check Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Check_Dir 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); /* Should clean up ... */ + + } + +#ifdef DEBUG + fprintf(stderr, "Directory %s checked successfully.\n", dir_name); +#endif + + RFCNB_Free_Pkt(pkt); + + return(0); +} + +/* Search directory for the files listed ... Relative to the TID in the */ +/* Con Handle. Return number of Dir Ents returned as the result. */ + +int SMB_Search(SMB_Tree_Handle tree, + char *dir_name, + WORD search, + SMB_CP_dirent *dirents, + int direntc, + char *resumekey, + int resumekey_len) + +{ struct RFCNB_Pkt *pkt, *recv_pkt; + int pkt_len, param_len, recv_param_len, recv_pkt_len, ret_count, i; + char *p; + + param_len = strlen(dir_name) + 2 + resumekey_len + 3; /* You have to know */ + pkt_len = SMB_search_len + param_len; + + recv_param_len = direntc * SMB_searchr_dirent_len + 3; + recv_pkt_len = SMB_searchr_len + recv_param_len; + + pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len); + + if (pkt == NULL) { /* Really should do some error handling */ + + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + recv_pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(recv_pkt_len); + + if (recv_pkt == NULL) { /* Really should do some error handling */ + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_NoSpace; + return(SMBlibE_BAD); + + } + + + /* Now plug in the bits we need */ + + bzero(SMB_Hdr(pkt), SMB_search_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsearch; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + + /* Tell server we known about non-dos names and extended attibutes */ + + SSVAL(SMB_Hdr(pkt), SMB_hdr_flg2_offset, + (SMB_FLG2_NON_DOS | SMB_FLG2_EXT_ATR)); + + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 2; + + SSVAL(SMB_Hdr(pkt), SMB_search_mdc_offset, direntc); /* How many we want */ + SSVAL(SMB_Hdr(pkt), SMB_search_atr_offset, search); + SSVAL(SMB_Hdr(pkt), SMB_search_bcc_offset, param_len); + + /* Now plug in the file name ... */ + + p = (char *)(SMB_Hdr(pkt) + SMB_search_buf_offset); + *p = SMBasciiID; + strcpy(p+1, dir_name); + p = p + strlen(dir_name) + 2; /* Skip the null */ + + + + *p = SMBvariableblockID; + p = p + 1; + + /* And now the resume key */ + + SSVAL(p, 0, resumekey_len); + + p = p + 2; + + bcopy(resumekey, p, resumekey_len); + + /* Now send the packet and get the response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending search request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + RFCNB_Free_Pkt(recv_pkt); + SMBlib_errno = -SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, recv_pkt, recv_pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Check Dir request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + RFCNB_Free_Pkt(recv_pkt); + SMBlib_errno = -SMBlibE_RecvFailed; + return(SMBlibE_BAD); + + } + + /* Now parse the response and pass back any error ... */ + + if (CVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */ + +#ifdef DEBUG + fprintf(stderr, "SMB_Check_Dir failed with errorclass = %i, Error Code = %i\n", + CVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset), + SVAL(SMB_Hdr(recv_pkt), SMB_hdr_err_offset)); +#endif + + SMBlib_SMB_Error = IVAL(SMB_Hdr(recv_pkt), SMB_hdr_rcls_offset); + RFCNB_Free_Pkt(pkt); + RFCNB_Free_Pkt(recv_pkt); + SMBlib_errno = SMBlibE_Remote; + return(SMBlibE_BAD); /* Should clean up ... */ + + } + + /* Now copy the results into the user's structure */ + + ret_count = SVAL(SMB_Hdr(recv_pkt), SMB_searchr_dec_offset); + + p = SMB_Hdr(recv_pkt) + SMB_searchr_buf_offset + 3; + + /* Hmmm, should check that we have the right number of bytes ... */ + + for (i = 0; i < ret_count; i++) { + + bcopy(p, dirents[i].resume_key, 21); + + p = p + 21; + + dirents[i].file_attributes = (unsigned char)*p; + + p = p + 1; + + dirents[i].date_time = IVAL(p, 0); /* Should this be IVAL? */ + + p = p + 4; + + dirents[i].size = IVAL(p, 0); + + p = p + 4; + + bcopy(p, dirents[i].filename, 13); /* Copy in file name */ + + p = p + 13; + + } + + return(ret_count); + +} diff --git a/daemon/smblib/find_password.c b/daemon/smblib/find_password.c new file mode 100644 index 0000000..c444732 --- /dev/null +++ b/daemon/smblib/find_password.c @@ -0,0 +1,281 @@ +/* Find passwords ... */ +/* We do it in a brute force way ... Cycle through all the possible passwords + sending a logon to see if all it works ... We have to wait for any timeout + the the server implements before we try the next one. We could open lots + of connections to the server and then send the logon request and not wait + for the reply. This would allow us to have lots of outstanding attempts at + a time. */ + +#include +#include + +#include "smblib.h" + +int verbose = FALSE; +int lotc = FALSE; + +char *SMB_Prots[] = {"PC NETWORK PROGRAM 1.0", + "MICROSOFT NETWORKS 1.03", + "MICROSOFT NETWORKS 3.0", + "LANMAN1.0", + "LM1.2X002", + "LANMAN2.1", + "NT LM 0.12", + "NT LANMAN 1.0", + NULL}; + +void usage() + +{ + fprintf(stderr,"Usage: find_password -u -l server\n"); +} + +/* figure out next password */ + +static int pwinit = FALSE, pwpos = 0; + +int next_password(char *pw, int pwlen) + +{ int i, carry = FALSE; + + if (pwinit == FALSE) { + + pwinit = TRUE; + bzero(pw, pwlen + 1); + pwpos = 0; + + } + + i = pwpos; + + while (TRUE) { + + pw[i] = pw[i] + 1; + + /* If it has wrapped around, then inc to 1 and carry up the chain */ + + if (pw[i] == 0) { + + pw[i] = 1; + i = i - 1; + + if (i < 0) { /* If we went off the end, increment pwpos */ + + pwpos = pwpos + 1; + if (pwpos >= pwlen) return(FALSE); /* No more passwords */ + + pw[pwpos] = 1; + return(TRUE); + + } + + } + else + return(TRUE); + + return(FALSE); + } +} + +static char pwd_str[1024]; /* Where we put passwords as we convert them */ + +char *print_password(char * password) + +{ int i,j; + char temp[4]; + + j = 0; + + for (i = 0; i < strlen(password); i++){ + + if (((unsigned)password[i] <= ' ') || ((unsigned)password[i] > 127)) { + + pwd_str[j] = '\\'; + sprintf(temp, "%03i", (int)password[i]); + strcpy(&pwd_str[j + 1], temp); + j = j + 3; /* Space for \ accounted for below */ + + } + else + pwd_str[j] = password[i]; + + j = j + 1; + + } + + pwd_str[j] = 0; /* Put a null on the end ... */ + + return(pwd_str); + +} + +main(int argc, char *argv[]) + +{ void *con, *tree; + extern char *optarg; + extern int optind; + int opt, error, SMB_Error, err_class, err_code, pwlen, tries = 0; + char server[80], service[80], service_name[160], password[80], username[80]; + char old_password[80], err_string[1024]; + + server[0] = 0; + strncpy(service, "IPC$", sizeof(service) - 1); + service_name[0] = 0; + username[0] = 0; + password[0] = 0; + old_password[0] = 0; + + while ((opt = getopt(argc, argv, "s:u:l:v")) != EOF) { + + switch (opt) { + case 's': + + strcpy(service, optarg); + break; + + case 'u': /* Pick up the user name */ + + strncpy(username, optarg, sizeof(username) - 1); + break; + + case 'l': /* pick up password len */ + + pwlen = atoi(optarg); + break; + + case 'v': /* Verbose? */ + verbose = TRUE; + break; + + default: + + usage(); + exit(1); + break; + } + + } + + if (optind < argc) { /* Some more parameters, assume is the server */ + strncpy(server, argv[optind], sizeof(server) - 1); + optind++; + } + else { + strcpy(server, "nemesis"); + } + + if (verbose == TRUE) { /* Print out all we know */ + + fprintf(stderr, "Finding password for User: %s, on server: %s\n", + username, server); + fprintf(stderr, "with a pwlen = %i\n", pwlen); + + } + + SMB_Init(); /* Initialize things ... */ + + /* We connect to the server and negotiate */ + + con = SMB_Connect_Server(NULL, server); + + if (con == NULL) { /* Error processing */ + + fprintf(stderr, "Unable to connect to server %s ...\n", server); + + if (SMB_Get_Last_Error() == SMBlibE_Remote) { + + SMB_Error = SMB_Get_Last_SMB_Err(); + SMB_Get_SMB_Error_Msg(SMBlib_Error_Class(SMB_Error), + SMBlib_Error_Code(SMB_Error), + err_string, + sizeof(err_string) - 1); + + } + else { + SMB_Get_Error_Msg(SMB_Get_Last_Error(), err_string, sizeof(err_string) - 1); + } + + printf(" %s\n", err_string); + exit(1); + + } + + /* We need to negotiate a protocol better than PC NetWork Program */ + + if (SMB_Negotiate(con, SMB_Prots) < 0) { + + fprintf(stderr, "Unable to negotiate a protocol with server %s ...\n", + server); + + if (SMB_Get_Last_Error() == SMBlibE_Remote) { + + SMB_Error = SMB_Get_Last_SMB_Err(); + SMB_Get_SMB_Error_Msg(SMBlib_Error_Class(SMB_Error), + SMBlib_Error_Code(SMB_Error), + err_string, + sizeof(err_string) - 1); + + } + else { + SMB_Get_Error_Msg(SMB_Get_Last_Error(), err_string, sizeof(err_string) - 1); + } + + printf(" %s\n", err_string); + exit(1); + + } + + sprintf(service_name, "\\\\%s\\%s", server, service); /* Could blow up */ + + /* Now loop through all password possibilities ... */ + + bzero(password, sizeof(password)); + + while (next_password(password, pwlen) == TRUE) { + + if ((tree = SMB_Logon_And_TCon(con, + NULL, + username, + password, + service_name, "?????")) == NULL) { + + if (verbose == TRUE) { /* Lets hear about the error */ + + fprintf(stderr, "Unable to logon and tree connect to server %s ...\n", + server); + fprintf(stderr, "With username: %s, and password: %s\n", + username, print_password(password)); + + if (SMB_Get_Last_Error() == SMBlibE_Remote) { + + SMB_Error = SMB_Get_Last_SMB_Err(); + SMB_Get_SMB_Error_Msg(SMBlib_Error_Class(SMB_Error), + SMBlib_Error_Code(SMB_Error), + err_string, + sizeof(err_string) - 1); + + } + else { + SMB_Get_Error_Msg(SMB_Get_Last_Error(), err_string, sizeof(err_string) - 1); + } + + printf(" %s\n", err_string); + + } + } + else { /* Password match */ + + fprintf(stderr, "Logged in with password:%s\n", + print_password(password)); + + /* Exit now ... */ + + exit(0); + + } + + } + + fprintf(stderr, "Passwords exhausted."); + +} diff --git a/daemon/smblib/smb-errors.c b/daemon/smblib/smb-errors.c new file mode 100644 index 0000000..c2af25d --- /dev/null +++ b/daemon/smblib/smb-errors.c @@ -0,0 +1,220 @@ +/* UNIX SMBlib NetBIOS implementation + + Version 1.0 + SMBlib Error values etc ... + + Copyright (C) Richard Sharpe, Andrew Tridgell, and Merik Karman, 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. +*/ + +/* This code ripped out of smbclient, where it was attributed to Merik */ +/* Karman merik@blackadder.dsh.oz.au */ +/* Modified by Richard Sharpe to try to make it more bullit proof and */ +/* ensure we don't overwrite strings when not passed enough space. Also */ +/* added code to say unknown error codes if we see any */ + + +#include + +typedef struct +{ + char *name; + int code; + char *message; +} err_code_struct; + +/* Dos Error Messages */ +err_code_struct dos_msgs[] = { + {"ERRbadfunc",1,"Invalid function."}, + {"ERRbadfile",2,"File not found."}, + {"ERRbadpath",3,"Directory invalid."}, + {"ERRnofids",4,"No file descriptors available"}, + {"ERRnoaccess",5,"Access denied."}, + {"ERRbadfid",6,"Invalid file handle."}, + {"ERRbadmcb",7,"Memory control blocks destroyed."}, + {"ERRnomem",8,"Insufficient server memory to perform the requested function."} +, + {"ERRbadmem",9,"Invalid memory block address."}, + {"ERRbadenv",10,"Invalid environment."}, + {"ERRbadformat",11,"Invalid format."}, + {"ERRbadaccess",12,"Invalid open mode."}, + {"ERRbaddata",13,"Invalid data."}, + {"ERR",14,"reserved."}, + {"ERRbaddrive",15,"Invalid drive specified."}, + {"ERRremcd",16,"A Delete Directory request attempted to remove the server's current directory."}, + {"ERRdiffdevice",17,"Not same device."}, + {"ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."}, + {"ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing FIDs on the file."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRbaddevtyp",66,"The device type is incorrect for a tree connect."}, + {"ERRbadnetnam",67,"The network name is incorrect or inappropriate."}, + {"ERRfilexists",80,"The file named in a Create Directory, Make New File or Link request already exists."}, + {"ERRbadpipe",230,"Pipe invalid."}, + {"ERRpipebusy",231,"All instances of the requested pipe are busy."}, + {"ERRpipeclosing",232,"Pipe close in progress."}, + {"ERRnotconnected",233,"No process on other end of pipe."}, + {"ERRmoredata",234,"There is more data to be returned."}, + {"ERRinvapi", 2142, "The API is invalid."}, + {NULL,-1,NULL}}; + +/* Server Error Messages */ +err_code_struct server_msgs[] = { + {"ERRerror",1,"Non-specific error code."}, + {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."}, + {"ERRbadtype",3,"reserved."}, + {"ERRaccess",4,"Network access denied. The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."}, + {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."}, + {"ERRinvnetname",6,"Invalid network name in tree connect."}, + {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."}, + {"ERRqfull",49,"Print queue full (files) -- returned by open print file."}, + {"ERRqtoobig",50,"Print queue full -- no space."}, + {"ERRqeof",51,"EOF on print queue dump."}, + {"ERRinvpfid",52,"Invalid print file FID."}, + {"ERRsmbcmd",64,"The server did not recognize the command received."}, + {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."}, + {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."}, + {"ERRreserved",68,"reserved."}, + {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."}, + {"ERRreserved",70,"reserved."}, + {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."}, + {"ERRpaused",81,"Server is paused."}, + {"ERRmsgoff",82,"Not receiving messages."}, + {"ERRnoroom",83,"No room to buffer message."}, + {"ERRrmuns",87,"Too many remote user names."}, + {"ERRtimeout",88,"Operation timed out."}, + {"ERRnoresource",89,"No resources currently available for request."}, + {"ERRtoomanyuids",90,"Too many UIDs active on this session."}, + {"ERRbaduid",91,"The UID is not known as a valid ID on this session."}, + {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."}, + {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."}, + {"ERRcontmpx",252,"Continue in MPX mode."}, + {"ERRreserved",253,"reserved."}, + {"ERRreserved",254,"reserved."}, + {"ERRpwdexp",2242,"Password has expired."}, + {"ERRnosupport",0xFFFF,"Function not supported."}, + {NULL,-1,NULL}}; + +/* Hard Error Messages */ +err_code_struct hard_msgs[] = { + {"ERRnowrite",19,"Attempt to write on write-protected diskette."}, + {"ERRbadunit",20,"Unknown unit."}, + {"ERRnotready",21,"Drive not ready."}, + {"ERRbadcmd",22,"Unknown command."}, + {"ERRdata",23,"Data error (CRC)."}, + {"ERRbadreq",24,"Bad request structure length."}, + {"ERRseek",25 ,"Seek error."}, + {"ERRbadmedia",26,"Unknown media type."}, + {"ERRbadsector",27,"Sector not found."}, + {"ERRnopaper",28,"Printer out of paper."}, + {"ERRwrite",29,"Write fault."}, + {"ERRread",30,"Read fault."}, + {"ERRgeneral",31,"General failure."}, + {"ERRbadshare",32,"A open conflicts with an existing open."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRwrongdisk",34,"The wrong disk was found in a drive."}, + {"ERRFCBUnavail",35,"No FCBs are available to process request."}, + {"ERRsharebufexc",36,"A sharing buffer has been exceeded."}, + {"ERRdiskfull",39,"The disk is full."}, + {NULL,-1,NULL}}; + +struct +{ + int code; + char *class; + err_code_struct *err_msgs; +} err_classes[] = { + {0,"SUCCESS",NULL}, + {0x01,"ERRDOS",dos_msgs}, + {0x02,"ERRSRV",server_msgs}, + {0x03,"ERRHRD",hard_msgs}, + {0x04,"ERRXOS",NULL}, + {0xE1,"ERRRMX1",NULL}, + {0xE2,"ERRRMX2",NULL}, + {0xE3,"ERRRMX3",NULL}, + {0xFF,"ERRCMD",NULL}, + {-1,NULL,NULL}}; + +/* Return in the string an error message after decoding the class and code */ + +int SMB_Get_SMB_Error_Msg(int err_class, int err_code, char *msg_buf, int len) + +{ int i,j; + char internal_buf[80]; + + for (i=0;err_classes[i].class;i++) + + if (err_classes[i].code == err_class) { + + if (err_classes[i].err_msgs) { + + err_code_struct *err = err_classes[i].err_msgs; + + for (j=0;err[j].name;j++) + + if (err_code == err[j].code) { + + /* Put together the message */ + + strncpy(msg_buf, err_classes[i].class, len); + strncat(msg_buf, " - ", len - strlen(msg_buf)); + strncat(msg_buf, err[j].name, len - strlen(msg_buf)); + strncat(msg_buf, " (", len - strlen(msg_buf)); + strncat(msg_buf, err[j].message, len - strlen(msg_buf)); + strncat(msg_buf, ").", len - strlen(msg_buf)); + + return(strlen(msg_buf)); + } + + /* We only get here if the error code is one we don't know about */ + /* Just print out the code etc ... */ + + strncpy(msg_buf, err_classes[i].class, len); + strncat(msg_buf, " - ", len - strlen(msg_buf)); + sprintf(internal_buf, "%d", err_code); + strncat(msg_buf, internal_buf, len - strlen(msg_buf)); + strncat(msg_buf, " (Unknown error code).", len - strlen(msg_buf)); + + return(strlen(msg_buf)); + + } + else { + + strncpy(msg_buf, err_classes[i].class, len); + strncat(msg_buf, " - ", len - strlen(msg_buf)); + sprintf(internal_buf, "%d", err_code); + strncat(msg_buf, internal_buf, len - strlen(msg_buf)); + + return(strlen(msg_buf)); + + } + + } + + /* If we get here, we did not recognize the error class */ + + sprintf(internal_buf, "%d", err_class); + strncat(msg_buf, internal_buf, len - strlen(msg_buf)); + strncat(msg_buf, " (Unknown Error Class) - ", len - strlen(msg_buf)); + sprintf(internal_buf, "%d", err_code); + strncat(msg_buf, internal_buf, len - strlen(msg_buf)); + strncat(msg_buf, "(error code).", len - strlen(msg_buf)); + + return(strlen(msg_buf)); + +} diff --git a/daemon/smblib/smbencrypt.c b/daemon/smblib/smbencrypt.c new file mode 100644 index 0000000..d46adc9 --- /dev/null +++ b/daemon/smblib/smbencrypt.c @@ -0,0 +1,202 @@ +#ifdef SMB_PASSWD +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-1995 + 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 "includes.h" +#include "loadparm.h" +#include "des.h" +#include "md4.h" + +extern int DEBUGLEVEL; + +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef int16 +#define int16 unsigned short +#endif +#ifndef uint16 +#define uint16 unsigned short +#endif +#ifndef uint32 +#define uint32 unsigned int +#endif + +#include "byteorder.h" + +void str_to_key(uchar *str,uchar *key) +{ + void des_set_odd_parity(des_cblock *); + 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); + } + des_set_odd_parity((des_cblock *)key); +} + +void D1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); + des_set_key(deskey,ks); + des_ecb_encrypt(d, out, ks, DES_DECRYPT); +} + +void E1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); + des_set_key(deskey,ks); + des_ecb_encrypt(d, out, ks, DES_ENCRYPT); +} + +void E_P16(uchar *p14,uchar *p16) +{ + uchar sp7[7]; + /* the following constant makes us compatible with other + implementations. Note that publishing this constant does not reduce the + security of the encryption mechanism */ + uchar sp8[] = {0xAA,0xD3,0xB4,0x35,0xB5,0x14,0x4,0xEE}; + uchar x[8]; + + memset(sp7,'\0',7); + + D1(sp7, sp8, x); + E1(p14, x, p16); + E1(p14+7, x, p16+8); +} + +void E_P24(uchar *p21, uchar *c8, uchar *p24) +{ + E1(p21, c8, p24); + E1(p21+7, c8, p24+8); + E1(p21+14, c8, p24+16); +} + + +/* + 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 i, len; + int16 wpwd[129]; + MDstruct MD; + + /* Password cannot be longer than 128 characters */ + len = strlen(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); + + MDbegin(&MD); + for(i = 0; i + 64 <= len; i += 64) + MDupdate(&MD,wpwd + (i/2), 512); + MDupdate(&MD,wpwd + (i/2),(len-i)*8); + SIVAL(p16,0,MD.buffer[0]); + SIVAL(p16,4,MD.buffer[1]); + SIVAL(p16,8,MD.buffer[2]); + SIVAL(p16,12,MD.buffer[3]); +} + +/* 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); +} + +#else +void smbencrypt_dummy(void){} +#endif diff --git a/daemon/smblib/smblib-api.c b/daemon/smblib/smblib-api.c new file mode 100644 index 0000000..f74e5fd --- /dev/null +++ b/daemon/smblib/smblib-api.c @@ -0,0 +1,379 @@ +/* UNIX SMBlib NetBIOS implementation + + Version 1.0 + SMB API Calls ... + + 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/rfcnb.h" + +SMB_Tree_Handle SMBapi_Tree = NULL; + +/* Send an api request to the \\server\IPC$ tree, with a \PIPE\LANMAN api */ +/* request to change the user's password */ + +#define SMB_LMAPI_SLOT "\\PIPE\\LANMAN" +#define SMB_LMAPI_SUPW_DESC "zb16b16WW" + +int SMBapi_NetUserPasswordSet(SMB_Tree_Handle tree, char *user, + char *oldpass, char *newpass, int *apiStatus) + +{ struct RFCNB_Pkt *pkt; + int param_len, i, pkt_len, pad_api_name = FALSE; + char *p; + + /* Get a packet, we need one with space for a transact plus. The calc */ + /* below lays it all out as it is, including the empty string after the */ + /* descriptor and before the username */ + + param_len = 2 + strlen(SMB_LMAPI_SUPW_DESC) + 1 + + 1 /* for empty string :-) */ + strlen(user) + + 1 + 16 + 16 + 2 + 2; + + /* We have no setup words, wo we don't account for them */ + + pkt_len = SMB_trans_len + 2 /* for bcc */ + strlen(SMB_LMAPI_SLOT) + 1; + + /* Pad things onto a word boundary ... */ + + if (pkt_len & 0x0001) { + pkt_len = pkt_len + 1; + pad_api_name = TRUE; + } + + + pkt_len = pkt_len + param_len; + + /* Now allocate space for the packet, build it and send it */ + + 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_trans_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtrans; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 14; + + SSVAL(SMB_Hdr(pkt), SMB_trans_tpc_offset, param_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_tdc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_mpc_offset, 4); + SSVAL(SMB_Hdr(pkt), SMB_trans_mdc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_msc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_flg_offset, 0); + SIVAL(SMB_Hdr(pkt), SMB_trans_tmo_offset, 5000); + SSVAL(SMB_Hdr(pkt), SMB_trans_pbc_offset, param_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_pbo_offset, SMB_trans_len + 2 + + strlen(SMB_LMAPI_SLOT) + 1); + SSVAL(SMB_Hdr(pkt), SMB_trans_dbc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_dbo_offset, 0); + + /* Now put in the bcc and the rest of the info ... */ + + SSVAL(SMB_Hdr(pkt), SMB_trans_len, param_len + strlen(SMB_LMAPI_SLOT) + 1); + + p = SMB_Hdr(pkt) + SMB_trans_len + 2; /* Skip the BCC and ect */ + + strcpy(p, SMB_LMAPI_SLOT); + p = p + strlen(SMB_LMAPI_SLOT) + 1; + + if (pad_api_name == TRUE) /* Pad if we need to */ + p = p + 1; + +/* SSVAL(p, 0, 65000); /* Check the result */ + SSVAL(p, 0, SMB_LMapi_UserPasswordSet); /* The api call */ + + p = p + 2; + + strcpy(p, SMB_LMAPI_SUPW_DESC); /* Copy in the param desc */ + + p = p + strlen(SMB_LMAPI_SUPW_DESC) + 1; + + *p = 0; /* Stick in that null string */ + p = p + 1; + + strcpy(p, user); + + p = p + strlen(user) + 1; + + strncpy(p, oldpass, 16); + + p = p + 16; + + strncpy(p, newpass, 16); + + p = p + 16; + + SSVAL(p, 0, 0); /* Seems to be zero always? */ + SSVAL(p, 2, strlen(newpass)); /* Length of new password ...*/ + + /* Now send the lot and get a response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Trans request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Trans request\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_trans 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); + + } + + /* All ok, pass back the status */ + + *apiStatus = SVAL(SMB_Hdr(pkt), SVAL(SMB_Hdr(pkt), SMB_transr_pbo_offset)); + RFCNB_Free_Pkt(pkt); + + return(0); + +} + +#define SMB_LMAPI_SUI_DESC "zWsTPWW" +#define SMB_LMAPI_SUI_DATA_DESC "B16" + + +/* Set user info ... specifically, password */ + +int SMBapi_NetSetUserInfo(SMB_Tree_Handle tree, char *user, + char *newpass, int *apiStatus) + +{ struct RFCNB_Pkt *pkt; + int param_len, i, pkt_len, data_len, pad_api_name = FALSE; + int pad_params = FALSE; + char *p; + + /* Get a packet, we need one with space for a transact plus. The calc */ + /* below lays it all out as it is, including the empty string after the */ + /* descriptor and before the username */ + + param_len = 2 + strlen(SMB_LMAPI_SUI_DESC) + 1 + + + strlen(SMB_LMAPI_SUI_DATA_DESC) + 1 + strlen(user) + + 1 + 2 + 2 + 2 + 2; + + data_len = 16; + + /* We have no setup words, so we don't account for them */ + + pkt_len = SMB_trans_len + 2 /* for bcc */ + strlen(SMB_LMAPI_SLOT) + 1; + + if (pkt_len & 0x0001) { /* Pad to a WORD boundary */ + + pad_api_name = TRUE; + + } + + if (param_len & 0x0001) { /* pad to a WORD boundary */ + + pad_params = TRUE; + + } + + pkt_len = pkt_len + param_len + data_len; + + if (pad_api_name == TRUE) pkt_len = pkt_len + 1; + if (pad_params == TRUE) pkt_len = pkt_len + 1; + + /* Now allocate space for the packet, build it and send it */ + + 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_trans_len); + SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */ + *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtrans; + SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, tree -> con -> pid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, tree -> tid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, tree -> con -> mid); + SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, tree -> con -> uid); + *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 14; + + SSVAL(SMB_Hdr(pkt), SMB_trans_tpc_offset, param_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_tdc_offset, data_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_mpc_offset, 4); + SSVAL(SMB_Hdr(pkt), SMB_trans_mdc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_msc_offset, 0); + SSVAL(SMB_Hdr(pkt), SMB_trans_flg_offset, 0); + SIVAL(SMB_Hdr(pkt), SMB_trans_tmo_offset, 5000); + SSVAL(SMB_Hdr(pkt), SMB_trans_pbc_offset, param_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_pbo_offset, SMB_trans_len + 2 + + strlen(SMB_LMAPI_SLOT) + 1); + SSVAL(SMB_Hdr(pkt), SMB_trans_dbc_offset, data_len); + SSVAL(SMB_Hdr(pkt), SMB_trans_dbo_offset, pkt_len - data_len); + + /* Now put in the bcc and the rest of the info ... */ + + SSVAL(SMB_Hdr(pkt), SMB_trans_len, param_len + strlen(SMB_LMAPI_SLOT) + + 1 + data_len); + + p = SMB_Hdr(pkt) + SMB_trans_len + 2; /* Skip the BCC and ect */ + + strcpy(p, SMB_LMAPI_SLOT); + p = p + strlen(SMB_LMAPI_SLOT) + 1; + + if (pad_api_name == TRUE) /* Pad to a word boundary */ + p = p + 1; + +/* SSVAL(p, 0, 65000); /* Check the result */ + SSVAL(p, 0, SMB_LMapi_SetUserInfo); /* The api call */ + + p = p + 2; + + strcpy(p, SMB_LMAPI_SUI_DESC); /* Copy in the param desc */ + + p = p + strlen(SMB_LMAPI_SUI_DESC) + 1; + + strcpy(p, SMB_LMAPI_SUI_DATA_DESC); /* Copy in second descriptor */ + + p = p + strlen(SMB_LMAPI_SUI_DATA_DESC) + 1; + + strcpy(p, user); + + p = p + strlen(user) + 1; + + SSVAL(p, 0, 1); /* Claim that we have a level 1 struct ? */ + + p = p + 2; + + SSVAL(p, 0, 3); /* Set the password */ + SSVAL(p, 2, 1); /* Seems to be one ... */ + SSVAL(p, 4, strlen(newpass)); /* Length of new password ...*/ + + /* Now copy the data in ... */ + + p = p + 6; + + if (pad_params == TRUE) + p = p + 1; + + strcpy(p, newpass); + + /* Now send the lot and get a response ... */ + + if (RFCNB_Send(tree -> con -> Trans_Connect, pkt, pkt_len) < 0){ + +#ifdef DEBUG + fprintf(stderr, "Error sending Trans SetUserInfo request\n"); +#endif + + RFCNB_Free_Pkt(pkt); + SMBlib_errno = SMBlibE_SendFailed; + return(SMBlibE_BAD); + + } + + /* Now get the response ... */ + + if (RFCNB_Recv(tree -> con -> Trans_Connect, pkt, pkt_len) < 0) { + +#ifdef DEBUG + fprintf(stderr, "Error receiving response to Trans SetUserInfo request\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_trans SetUserInfo 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); + + } + + /* All ok, pass back the status */ + + *apiStatus = SVAL(SMB_Hdr(pkt), SVAL(SMB_Hdr(pkt), SMB_transr_pbo_offset)); + RFCNB_Free_Pkt(pkt); + + return(0); + +} + +/* List all the shares available on a server */ + +int SMBapi_NetShareEnum(SMB_Tree_Handle tree, char *enum_buf, int bufsiz, + int *shares_returned, int *shares_total) + +{ + + +} diff --git a/daemon/smblib/smblib-common.h b/daemon/smblib/smblib-common.h new file mode 100644 index 0000000..ff2a160 --- /dev/null +++ b/daemon/smblib/smblib-common.h @@ -0,0 +1,184 @@ +/* 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; diff --git a/daemon/smblib/smblib-priv.h b/daemon/smblib/smblib-priv.h new file mode 100644 index 0000000..58cda9d --- /dev/null +++ b/daemon/smblib/smblib-priv.h @@ -0,0 +1,624 @@ +/* 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 "../rfcnb/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 ... */ + + +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}; + +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]; + +}; + +#define SMBLIB_DEFAULT_DOMAIN "SMBlib_dom" +#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); diff --git a/daemon/smblib/smblib-util.c b/daemon/smblib/smblib-util.c new file mode 100644 index 0000000..c91a46e --- /dev/null +++ b/daemon/smblib/smblib-util.c @@ -0,0 +1,783 @@ +/* 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/rfcnb.h" + +/* 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 SMB_Neg_Prot_Def *prot_pkt; + struct SMB_Neg_Prot_Resp_Def *resp_pkt; + 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 + 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_negrLM_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) + +{ int loc; + + 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, i, 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)); + + } + } + +} diff --git a/daemon/smblib/smblib.c b/daemon/smblib/smblib.c new file mode 100644 index 0000000..2074420 --- /dev/null +++ b/daemon/smblib/smblib.c @@ -0,0 +1,549 @@ +/* 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 + +#include +#include "smblib-priv.h" + +#include "../rfcnb/rfcnb.h" + +#include + +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) + +{ SMB_Handle_Type con; + char temp[80], 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, 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 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 = 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, i, pkt_len; + char *p; + + /* 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); + + } + + /* Now build the correct structure */ + + if (Con_Handle -> protocol < SMB_P_NT1) { + + /* We don't handle encrypted passwords ... */ + + param_len = strlen(UserName) + 1 + strlen(PassWord) + 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, strlen(PassWord) + 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 */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord) + 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 + strlen(PassWord) + + 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, strlen(PassWord)); + 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 */ + + strcpy(p, PassWord); + + p = p + strlen(PassWord); + + 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/daemon/smblib/smblib.h b/daemon/smblib/smblib.h new file mode 100644 index 0000000..c485ef5 --- /dev/null +++ b/daemon/smblib/smblib.h @@ -0,0 +1,95 @@ +/* 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); + +/* 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); diff --git a/daemon/smblib/std-defines.h b/daemon/smblib/std-defines.h new file mode 100644 index 0000000..c58329d --- /dev/null +++ b/daemon/smblib/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 diff --git a/daemon/usuals.h b/daemon/usuals.h new file mode 100644 index 0000000..22dcb3a --- /dev/null +++ b/daemon/usuals.h @@ -0,0 +1,31 @@ + + +#ifndef __USUALS_H__ +#define __USUALS_H__ + +#include + +#include "config.h" + +#include +#include +#include +#include + +#include "compat.h" + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define countof(x) (sizeof(x) / sizeof(x[0])) + +#endif /* __USUALS_H__ */ diff --git a/doc/protocol.txt b/doc/protocol.txt new file mode 100644 index 0000000..05e32f2 --- /dev/null +++ b/doc/protocol.txt @@ -0,0 +1,46 @@ + + HTTP/AUTH PROTOCOL + +The protocol used between the stateful authenticator and the web servers +that wish to authenticate is described below. It's a simple text protocol, +similar to HTTP. The web servers send commands and headers to the daemon, +which replies with HTTP codes and headers. + +Multiple authentication requests can be processed on the same connection, +although the connection is not stateful. A authentication request initially +processed through one connection to the daemon can later be completed +through another. The commands are described below. + +AUTH method uri + + The AUTH command asks the daemon to perform authentication + for a given set of headers. + + method: is the authentication type. It might be 'NTLM'. + uri: the URI being authenticated. + + The AUTH command is followed by HTTP headers, one per line + until a blank line is found. These should contain the + authentication headers for the authentication protocol being + used. Extraneous headers are ignored. + + If multiple HTTP headers with the same name are received, then + the last one is used. Note that this is somewhat different than + the HTTP protocol. + +QUIT + + This closes the connection to the daemon. + + +The response from the daemon consists of an HTTP code, followed by headers +one per line. Note that only the headers to be added for authentication are +returned. For example: + + 401 + Header: value + Header2: value + +Success returns a 200, just like normal HTTP. Note that success can contain +headers that must also be sent to the client. + diff --git a/sample/httpauthd.conf b/sample/httpauthd.conf new file mode 100644 index 0000000..142f766 --- /dev/null +++ b/sample/httpauthd.conf @@ -0,0 +1,8 @@ +# Some comments + +# and blank lines +Socket: /var/run/blah.sock +MaxThreads: 18 + +Realm: My test realm + -- cgit v1.2.3