diff options
54 files changed, 13522 insertions, 0 deletions
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 @@ -0,0 +1 @@ +nielsen@memberwebs.com @@ -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 --- /dev/null +++ b/ChangeLog @@ -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` + @@ -0,0 +1 @@ +See ChangeLog
\ No newline at end of file @@ -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 <syslog.h> + +/* ----------------------------------------------------------------------- + * 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 <sys/types.h> + +#ifndef HAVE_STDARG_H +#error ERROR: Must have a working stdarg.h header +#else +#include <stdarg.h> +#endif + +#ifndef HAVE_REALLOCF +void* reallocf(void* p, size_t sz); +#endif + +#include <pthread.h> + +/* 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 <nielsen@memberwebs.com> + */ + +/* 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 <sys/types.h> +#include <stdlib.h> +#include "hash.h" + +#ifdef HASH_TIMESTAMP +#include <time.h> +#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 + * <http://citeseer.nj.nec.com/salz92internetnews.html>. + * + * 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 <rse@engelschall.com> + */ + 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 <nielsen@memberwebs.com> + */ + +/* 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 <string.h> +#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<<s | 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 <ijackson@nyx.cs.du.edu>. + * 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 <nielsen@memberwebs.com> +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 <pthread.h> ])], [ #include <pthread.h> ]) + + +# 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 <syslog.h> +#include <fcntl.h> +#include <unistd.h> + +#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 <sys/types.h> +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <syslog.h> +#include <pthread.h> +#include <fcntl.h> +#include <err.h> +#include <sys/un.h> +#include <sys/socket.h> + +#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 <syslog.h> + +/* LDAP library */ +#include <ldap.h> + +/* ------------------------------------------------------------------------------- + * 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 <syslog.h> +#include <err.h> + +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 <syslog.h> + +/* 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 <sys/uio.h> +#include <sys/signal.h> + +int RFCNB_Timeout = 0; /* Timeout in seconds ... */ + +void rfcnb_alarm(int sig) + +{ + + fprintf(stderr, "IO Timed out ...\n"); + +} + +/* Set timeout value and setup signal handling */ + +int RFCNB_Set_Timeout(int seconds) + +{ + /* 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 <netinet/tcp.h> +#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 <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#define TRUE 1 +#define FALSE 0 + +/* Pick up define for INADDR_NONE */ + +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif 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 <signal.h> + + +/* 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 <sys/types.h> +#include <unistd.h> + +#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 <user> -l <pwd-len-max> 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 <stdio.h> + +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 <sys/types.h> +#include <unistd.h> + +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_<protreq>_len do not include */ +/* the data that follows in the SMB packet, so the code will have to */ +/* take that into account. */ + +#define SMB_hdr_idf_offset 0 /* 0xFF,'SMB' 0-3 */ +#define SMB_hdr_com_offset 4 /* BYTE 4 */ +#define SMB_hdr_rcls_offset 5 /* BYTE 5 */ +#define SMB_hdr_reh_offset 6 /* BYTE 6 */ +#define SMB_hdr_err_offset 7 /* WORD 7 */ +#define SMB_hdr_reb_offset 9 /* BYTE 9 */ +#define SMB_hdr_flg_offset 9 /* same as reb ...*/ +#define SMB_hdr_res_offset 10 /* 7 WORDs 10 */ +#define SMB_hdr_res0_offset 10 /* WORD 10 */ +#define SMB_hdr_flg2_offset 10 /* WORD */ +#define SMB_hdr_res1_offset 12 /* WORD 12 */ +#define SMB_hdr_res2_offset 14 +#define SMB_hdr_res3_offset 16 +#define SMB_hdr_res4_offset 18 +#define SMB_hdr_res5_offset 20 +#define SMB_hdr_res6_offset 22 +#define SMB_hdr_tid_offset 24 +#define SMB_hdr_pid_offset 26 +#define SMB_hdr_uid_offset 28 +#define SMB_hdr_mid_offset 30 +#define SMB_hdr_wct_offset 32 + +#define SMB_hdr_len 33 /* 33 byte header? */ + +#define SMB_hdr_axc_offset 33 /* AndX Command */ +#define SMB_hdr_axr_offset 34 /* AndX Reserved */ +#define SMB_hdr_axo_offset 35 /* Offset from start to WCT of AndX cmd */ + +/* Format of the Negotiate Protocol SMB */ + +#define SMB_negp_bcc_offset 33 +#define SMB_negp_buf_offset 35 /* Where the buffer starts */ +#define SMB_negp_len 35 /* plus the data */ + +/* Format of the Negotiate Response SMB, for CoreProtocol, LM1.2 and */ +/* NT LM 0.12. wct will be 1 for CoreProtocol, 13 for LM 1.2, and 17 */ +/* for NT LM 0.12 */ + +#define SMB_negrCP_idx_offset 33 /* Response to the neg req */ +#define SMB_negrCP_bcc_offset 35 +#define SMB_negrLM_idx_offset 33 /* dialect index */ +#define SMB_negrLM_sec_offset 35 /* Security mode */ +#define SMB_sec_user_mask 0x01 /* 0 = share, 1 = user */ +#define SMB_sec_encrypt_mask 0x02 /* pick out encrypt */ +#define SMB_negrLM_mbs_offset 37 /* max buffer size */ +#define SMB_negrLM_mmc_offset 39 /* max mpx count */ +#define SMB_negrLM_mnv_offset 41 /* max number of VCs */ +#define SMB_negrLM_rm_offset 43 /* raw mode support bit vec*/ +#define SMB_read_raw_mask 0x01 +#define SMB_write_raw_mask 0x02 +#define SMB_negrLM_sk_offset 45 /* session key, 32 bits */ +#define SMB_negrLM_st_offset 49 /* Current server time */ +#define SMB_negrLM_sd_offset 51 /* Current server date */ +#define SMB_negrLM_stz_offset 53 /* Server Time Zone */ +#define SMB_negrLM_ekl_offset 55 /* encryption key length */ +#define SMB_negrLM_res_offset 57 /* reserved */ +#define SMB_negrLM_bcc_offset 59 /* bcc */ +#define SMB_negrLM_len 61 /* 61 bytes ? */ +#define SMB_negrLM_buf_offset 61 /* Where the fun begins */ + +#define SMB_negrNTLM_idx_offset 33 /* Selected protocol */ +#define SMB_negrNTLM_sec_offset 35 /* Security more */ +#define SMB_negrNTLM_mmc_offset 36 /* Different format above */ +#define SMB_negrNTLM_mnv_offset 38 /* Max VCs */ +#define SMB_negrNTLM_mbs_offset 40 /* MBS now a long */ +#define SMB_negrNTLM_mrs_offset 44 /* Max raw size */ +#define SMB_negrNTLM_sk_offset 48 /* Session Key */ +#define SMB_negrNTLM_cap_offset 52 /* Capabilities */ +#define SMB_negrNTLM_stl_offset 56 /* Server time low */ +#define SMB_negrNTLM_sth_offset 60 /* Server time high */ +#define SMB_negrNTLM_stz_offset 64 /* Server time zone */ +#define SMB_negrNTLM_ekl_offset 66 /* Encrypt key len */ +#define SMB_negrNTLM_bcc_offset 67 /* Bcc */ +#define SMB_negrNTLM_len 69 +#define SMB_negrNTLM_buf_offset 69 + +/* Offsets related to Tree Connect */ + +#define SMB_tcon_bcc_offset 33 +#define SMB_tcon_buf_offset 35 /* where the data is for tcon */ +#define SMB_tcon_len 35 /* plus the data */ + +#define SMB_tconr_mbs_offset 33 /* max buffer size */ +#define SMB_tconr_tid_offset 35 /* returned tree id */ +#define SMB_tconr_bcc_offset 37 +#define SMB_tconr_len 39 + +#define SMB_tconx_axc_offset 33 /* And X Command */ +#define SMB_tconx_axr_offset 34 /* reserved */ +#define SMB_tconx_axo_offset 35 /* Next command offset */ +#define SMB_tconx_flg_offset 37 /* Flags, bit0=1 means disc TID */ +#define SMB_tconx_pwl_offset 39 /* Password length */ +#define SMB_tconx_bcc_offset 41 /* bcc */ +#define SMB_tconx_buf_offset 43 /* buffer */ +#define SMB_tconx_len 43 /* up to data ... */ + +#define SMB_tconxr_axc_offset 33 /* Where the AndX Command is */ +#define SMB_tconxr_axr_offset 34 /* Reserved */ +#define SMB_tconxr_axo_offset 35 /* AndX offset location */ + +/* Offsets related to tree_disconnect */ + +#define SMB_tdis_bcc_offset 33 /* bcc */ +#define SMB_tdis_len 35 /* total len */ + +#define SMB_tdisr_bcc_offset 33 /* bcc */ +#define SMB_tdisr_len 35 + +/* Offsets related to Open Request */ + +#define SMB_open_mod_offset 33 /* Mode to open with */ +#define SMB_open_atr_offset 35 /* Attributes of file */ +#define SMB_open_bcc_offset 37 /* bcc */ +#define SMB_open_buf_offset 39 /* File name */ +#define SMB_open_len 39 /* Plus the file name */ + +#define SMB_openx_axc_offset 33 /* Next command */ +#define SMB_openx_axr_offset 34 /* Reserved */ +#define SMB_openx_axo_offset 35 /* offset of next wct */ +#define SMB_openx_flg_offset 37 /* Flags, bit0 = need more info */ + /* bit1 = exclusive oplock */ + /* bit2 = batch oplock */ +#define SMB_openx_mod_offset 39 /* mode to open with */ +#define SMB_openx_atr_offset 41 /* search attributes */ +#define SMB_openx_fat_offset 43 /* File attributes */ +#define SMB_openx_tim_offset 45 /* time and date of creat */ +#define SMB_openx_ofn_offset 49 /* Open function */ +#define SMB_openx_als_offset 51 /* Space to allocate on */ +#define SMB_openx_res_offset 55 /* reserved */ +#define SMB_openx_bcc_offset 63 /* bcc */ +#define SMB_openx_buf_offset 65 /* Where file name goes */ +#define SMB_openx_len 65 + +#define SMB_openr_fid_offset 33 /* FID returned */ +#define SMB_openr_atr_offset 35 /* Attributes opened with */ +#define SMB_openr_tim_offset 37 /* Last mod time of file */ +#define SMB_openr_fsz_offset 41 /* File size 4 bytes */ +#define SMB_openr_acc_offset 45 /* Access allowed */ +#define SMB_openr_bcc_offset 47 +#define SMB_openr_len 49 + +#define SMB_openxr_axc_offset 33 /* And X command */ +#define SMB_openxr_axr_offset 34 /* reserved */ +#define SMB_openxr_axo_offset 35 /* offset to next command */ +#define SMB_openxr_fid_offset 37 /* FID returned */ +#define SMB_openxr_fat_offset 39 /* File attributes returned*/ +#define SMB_openxr_tim_offset 41 /* File creation date etc */ +#define SMB_openxr_fsz_offset 45 /* Size of file */ +#define SMB_openxr_acc_offset 49 /* Access granted */ + +#define SMB_clos_fid_offset 33 /* FID to close */ +#define SMB_clos_tim_offset 35 /* Last mod time */ +#define SMB_clos_bcc_offset 39 /* bcc */ +#define SMB_clos_len 41 + +/* Offsets related to Write requests */ + +#define SMB_write_fid_offset 33 /* FID to write */ +#define SMB_write_cnt_offset 35 /* bytes to write */ +#define SMB_write_ofs_offset 37 /* location to write to */ +#define SMB_write_clf_offset 41 /* advisory count left */ +#define SMB_write_bcc_offset 43 /* bcc = data bytes + 3 */ +#define SMB_write_buf_offset 45 /* Data=0x01, len, data */ +#define SMB_write_len 45 /* plus the data ... */ + +#define SMB_writr_cnt_offset 33 /* Count of bytes written */ +#define SMB_writr_bcc_offset 35 /* bcc */ +#define SMB_writr_len 37 + +/* Offsets related to read requests */ + +#define SMB_read_fid_offset 33 /* FID of file to read */ +#define SMB_read_cnt_offset 35 /* count of words to read */ +#define SMB_read_ofs_offset 37 /* Where to read from */ +#define SMB_read_clf_offset 41 /* Advisory count to go */ +#define SMB_read_bcc_offset 43 +#define SMB_read_len 45 + +#define SMB_readr_cnt_offset 33 /* Count of bytes returned */ +#define SMB_readr_res_offset 35 /* 4 shorts reserved, 8 bytes */ +#define SMB_readr_bcc_offset 43 /* bcc */ +#define SMB_readr_bff_offset 45 /* buffer format char = 0x01 */ +#define SMB_readr_len_offset 46 /* buffer len */ +#define SMB_readr_len 45 /* length of the readr before data */ + +/* Offsets for Create file */ + +#define SMB_creat_atr_offset 33 /* Attributes of new file ... */ +#define SMB_creat_tim_offset 35 /* Time of creation */ +#define SMB_creat_dat_offset 37 /* 4004BCE :-) */ +#define SMB_creat_bcc_offset 39 /* bcc */ +#define SMB_creat_buf_offset 41 +#define SMB_creat_len 41 /* Before the data */ + +#define SMB_creatr_fid_offset 33 /* FID of created file */ + +/* Offsets for Delete file */ + +#define SMB_delet_sat_offset 33 /* search attribites */ +#define SMB_delet_bcc_offset 35 /* bcc */ +#define SMB_delet_buf_offset 37 +#define SMB_delet_len 37 + +/* Offsets for SESSION_SETUP_ANDX for both LM and NT LM protocols */ + +#define SMB_ssetpLM_mbs_offset 37 /* Max buffer Size, allow for AndX */ +#define SMB_ssetpLM_mmc_offset 39 /* max multiplex count */ +#define SMB_ssetpLM_vcn_offset 41 /* VC number if new VC */ +#define SMB_ssetpLM_snk_offset 43 /* Session Key */ +#define SMB_ssetpLM_pwl_offset 47 /* password length */ +#define SMB_ssetpLM_res_offset 49 /* reserved */ +#define SMB_ssetpLM_bcc_offset 53 /* bcc */ +#define SMB_ssetpLM_len 55 /* before data ... */ +#define SMB_ssetpLM_buf_offset 55 + +#define SMB_ssetpNTLM_mbs_offset 37 /* Max Buffer Size for NT LM 0.12 */ + /* and above */ +#define SMB_ssetpNTLM_mmc_offset 39 /* Max Multiplex count */ +#define SMB_ssetpNTLM_vcn_offset 41 /* VC Number */ +#define SMB_ssetpNTLM_snk_offset 43 /* Session key */ +#define SMB_ssetpNTLM_cipl_offset 47 /* Case Insensitive PW Len */ +#define SMB_ssetpNTLM_cspl_offset 49 /* Unicode pw len */ +#define SMB_ssetpNTLM_res_offset 51 /* reserved */ +#define SMB_ssetpNTLM_cap_offset 55 /* server capabilities */ +#define SMB_ssetpNTLM_bcc_offset 59 /* bcc */ +#define SMB_ssetpNTLM_len 61 /* before data */ +#define SMB_ssetpNTLM_buf_offset 61 + +#define SMB_ssetpr_axo_offset 35 /* Offset of next response ... */ +#define SMB_ssetpr_act_offset 37 /* action, bit 0 = 1 => guest */ +#define SMB_ssetpr_bcc_offset 39 /* bcc */ +#define SMB_ssetpr_buf_offset 41 /* Native OS etc */ + +/* Offsets for SMB create directory */ + +#define SMB_creatdir_bcc_offset 33 /* only a bcc here */ +#define SMB_creatdir_buf_offset 35 /* Where things start */ +#define SMB_creatdir_len 35 + +/* Offsets for SMB delete directory */ + +#define SMB_deletdir_bcc_offset 33 /* only a bcc here */ +#define SMB_deletdir_buf_offset 35 /* where things start */ +#define SMB_deletdir_len 35 + +/* Offsets for SMB check directory */ + +#define SMB_checkdir_bcc_offset 33 /* Only a bcc here */ +#define SMB_checkdir_buf_offset 35 /* where things start */ +#define SMB_checkdir_len 35 + +/* Offsets for SMB search */ + +#define SMB_search_mdc_offset 33 /* Max Dir ents to return */ +#define SMB_search_atr_offset 35 /* Search attributes */ +#define SMB_search_bcc_offset 37 /* bcc */ +#define SMB_search_buf_offset 39 /* where the action is */ +#define SMB_search_len 39 + +#define SMB_searchr_dec_offset 33 /* Dir ents returned */ +#define SMB_searchr_bcc_offset 35 /* bcc */ +#define SMB_searchr_buf_offset 37 /* Where the action starts */ +#define SMB_searchr_len 37 /* before the dir ents */ + +#define SMB_searchr_dirent_len 43 /* 53 bytes */ + +/* Defines for SMB transact and transact2 calls */ + +#define SMB_trans_tpc_offset 33 /* Total param count */ +#define SMB_trans_tdc_offset 35 /* total Data count */ +#define SMB_trans_mpc_offset 37 /* Max params bytes to return */ +#define SMB_trans_mdc_offset 39 /* Max data bytes to return */ +#define SMB_trans_msc_offset 41 /* Max setup words to return */ +#define SMB_trans_rs1_offset 42 /* Reserved byte */ +#define SMB_trans_flg_offset 43 /* flags */ +#define SMB_trans_tmo_offset 45 /* Timeout, long */ +#define SMB_trans_rs2_offset 49 /* Next reserved */ +#define SMB_trans_pbc_offset 51 /* Param Byte count in buf */ +#define SMB_trans_pbo_offset 53 /* Offset to param bytes */ +#define SMB_trans_dbc_offset 55 /* Data byte count in buf */ +#define SMB_trans_dbo_offset 57 /* Data byte offset */ +#define SMB_trans_suc_offset 59 /* Setup count - byte */ +#define SMB_trans_rs3_offset 60 /* Reserved to pad ... */ +#define SMB_trans_len 61 /* Up to setup, still need bcc */ + +#define SMB_transr_tpc_offset 33 /* Total param bytes returned */ +#define SMB_transr_tdc_offset 35 +#define SMB_transr_rs1_offset 37 +#define SMB_transr_pbc_offset 39 +#define SMB_transr_pbo_offset 41 +#define SMB_transr_pdi_offset 43 /* parameter displacement */ +#define SMB_transr_dbc_offset 45 +#define SMB_transr_dbo_offset 47 +#define SMB_transr_ddi_offset 49 +#define SMB_transr_suc_offset 51 +#define SMB_transr_rs2_offset 52 +#define SMB_transr_len 53 + +/* Bit masks for SMB Capabilities ... */ + +#define SMB_cap_raw_mode 0x0001 +#define SMB_cap_mpx_mode 0x0002 +#define SMB_cap_unicode 0x0004 +#define SMB_cap_large_files 0x0008 +#define SMB_cap_nt_smbs 0x0010 +#define SMB_rpc_remote_apis 0x0020 +#define SMB_cap_nt_status 0x0040 +#define SMB_cap_level_II_oplocks 0x0080 +#define SMB_cap_lock_and_read 0x0100 +#define SMB_cap_nt_find 0x0200 + +/* SMB LANMAN api call defines */ + +#define SMB_LMapi_SetUserInfo 0x0072 +#define SMB_LMapi_UserPasswordSet 0x0073 + +/* Structures and defines we use in the client interface */ + +/* The protocols we might support. Perhaps a bit ambitious, as only RFCNB */ +/* has any support so far 0(sometimes called NBT) */ + +typedef enum {SMB_RFCNB, SMB_IPXNB, SMB_NETBEUI, SMB_X25} SMB_Transport_Types; + +typedef enum {SMB_Con_FShare, SMB_Con_PShare, SMB_Con_IPC} SMB_Con_Types; + +typedef enum {SMB_State_NoState, SMB_State_Stopped, SMB_State_Started} SMB_State_Types; + +/* The following two arrays need to be in step! */ +/* We must make it possible for callers to specify these ... */ + + +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 <string.h> +#include "smblib-priv.h" + +#include "../rfcnb/rfcnb.h" + +#include <signal.h> + +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 <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <strings.h> + +#define TRUE 1 +#define FALSE 0 + +#endif 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 <sys/types.h> + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#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 + |