summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/.cvsignore4
-rw-r--r--daemon/Makefile.am13
-rw-r--r--daemon/basic.c243
-rw-r--r--daemon/defaults.h9
-rw-r--r--daemon/httpauthd.c1031
-rw-r--r--daemon/httpauthd.h328
-rw-r--r--daemon/ldap.c1114
-rw-r--r--daemon/misc.c621
-rw-r--r--daemon/ntlm.c703
-rw-r--r--daemon/ntlmssp.c398
-rw-r--r--daemon/ntlmssp.h140
-rw-r--r--daemon/rfcnb/byteorder.h80
-rw-r--r--daemon/rfcnb/rfcnb-common.h36
-rw-r--r--daemon/rfcnb/rfcnb-error.h75
-rw-r--r--daemon/rfcnb/rfcnb-io.c407
-rw-r--r--daemon/rfcnb/rfcnb-io.h28
-rw-r--r--daemon/rfcnb/rfcnb-priv.h151
-rw-r--r--daemon/rfcnb/rfcnb-util.c532
-rw-r--r--daemon/rfcnb/rfcnb-util.h50
-rw-r--r--daemon/rfcnb/rfcnb.h48
-rw-r--r--daemon/rfcnb/session.c364
-rw-r--r--daemon/rfcnb/std-includes.h45
-rw-r--r--daemon/rfcnb/x_Makefile38
-rw-r--r--daemon/smblib/exper.c748
-rw-r--r--daemon/smblib/file.c1306
-rw-r--r--daemon/smblib/find_password.c281
-rw-r--r--daemon/smblib/smb-errors.c220
-rw-r--r--daemon/smblib/smbencrypt.c202
-rw-r--r--daemon/smblib/smblib-api.c379
-rw-r--r--daemon/smblib/smblib-common.h184
-rw-r--r--daemon/smblib/smblib-priv.h624
-rw-r--r--daemon/smblib/smblib-util.c783
-rw-r--r--daemon/smblib/smblib.c549
-rw-r--r--daemon/smblib/smblib.h95
-rw-r--r--daemon/smblib/std-defines.h45
-rw-r--r--daemon/usuals.h31
36 files changed, 11905 insertions, 0 deletions
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__ */