diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 2003-04-11 16:34:49 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 2003-04-11 16:34:49 +0000 |
commit | 2e7f00fce6696c380a93ea939bf233760f499640 (patch) | |
tree | 4054b7b0d7a6722c69691523b63159feb4ce06b0 /c | |
parent | 2002-04-10 Mike Siers <mikes@poliac.com> (diff) | |
download | rtems-2e7f00fce6696c380a93ea939bf233760f499640.tar.bz2 |
2003-04-11 Joel Sherrill <joel@OARcorp.com>
* rtems_webserver/cgi.c, rtems_webserver/sockGen.c,
rtems_webserver/umui.c, rtems_webserver/websSSL.c,
rtems_webserver/websSSL.h, rtems_webserver/websda.c,
rtems_webserver/websda.h: New files. Not included in previous commit.
Diffstat (limited to 'c')
-rw-r--r-- | c/src/libnetworking/ChangeLog | 7 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/cgi.c | 331 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/sockGen.c | 1044 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/umui.c | 641 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/websSSL.c | 706 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/websSSL.h | 67 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/websda.c | 244 | ||||
-rw-r--r-- | c/src/libnetworking/rtems_webserver/websda.h | 41 |
8 files changed, 3081 insertions, 0 deletions
diff --git a/c/src/libnetworking/ChangeLog b/c/src/libnetworking/ChangeLog index ea2d0bf722..a65bdccbd9 100644 --- a/c/src/libnetworking/ChangeLog +++ b/c/src/libnetworking/ChangeLog @@ -1,3 +1,10 @@ +2003-04-11 Joel Sherrill <joel@OARcorp.com> + + * rtems_webserver/cgi.c, rtems_webserver/sockGen.c, + rtems_webserver/umui.c, rtems_webserver/websSSL.c, + rtems_webserver/websSSL.h, rtems_webserver/websda.c, + rtems_webserver/websda.h: New files. Not included in previous commit. + 2002-04-10 Mike Siers <mikes@poliac.com> * rtems_webserver/NOTES, rtems_webserver/asp.c, diff --git a/c/src/libnetworking/rtems_webserver/cgi.c b/c/src/libnetworking/rtems_webserver/cgi.c new file mode 100644 index 0000000000..c2328eab6e --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/cgi.c @@ -0,0 +1,331 @@ +/* + * cgi.c -- CGI processing (for the GoAhead Web server + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/********************************** Description *******************************/ +/* + * This module implements the /cgi-bin handler. CGI processing differs from + * goforms processing in that each CGI request is executed as a separate + * process, rather than within the webserver process. For each CGI request the + * environment of the new process must be set to include all the CGI variables + * and its standard input and output must be directed to the socket. This + * is done using temporary files. + */ + +/*********************************** Includes *********************************/ +#include "wsIntrn.h" +#ifdef UEMF + #include "uemf.h" +#else + #include "basic/basicInternal.h" +#endif + +/************************************ Locals **********************************/ +typedef struct { /* Struct for CGI tasks which have completed */ + webs_t wp; /* pointer to session websRec */ + char_t *stdIn; /* file desc. for task's temp input fd */ + char_t *stdOut; /* file desc. for task's temp output fd */ + char_t *cgiPath; /* path to executable process file */ + char_t **argp; /* pointer to buf containing argv tokens */ + char_t **envp; /* pointer to array of environment strings */ + int handle; /* process handle of the task */ + long fplacemark; /* seek location for CGI output file */ +} cgiRec; +static cgiRec **cgiList; /* hAlloc chain list of wp's to be closed */ +static int cgiMax; /* Size of hAlloc list */ + +/************************************* Code ***********************************/ + +/* + * Process a form request. Returns 1 always to indicate it handled the URL + */ +int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, + char_t *url, char_t *path, char_t* query) +{ + cgiRec *cgip; + sym_t *s; + char_t cgiBuf[FNAMESIZE], *stdIn, *stdOut, cwd[FNAMESIZE]; + char_t *cp, *cgiName, *cgiPath, **argp, **envp, **ep; + int n, envpsize, argpsize, pHandle, cid; + a_assert(websValid(wp)); + a_assert(url && *url); + a_assert(path && *path == '/'); + websStats.cgiHits++; +/* + * Extract the form name and then build the full path name. The form + * name will follow the first '/' in path. + */ + gstrncpy(cgiBuf, path, TSZ(cgiBuf)); + if ((cgiName = gstrchr(&cgiBuf[1], '/')) == NULL) { + websError(wp, 200, T("Missing CGI name")); + return 1; + } + cgiName++; + if ((cp = gstrchr(cgiName, '/')) != NULL) { + *cp = '\0'; + } + fmtAlloc(&cgiPath, FNAMESIZE, T("%s/%s/%s"), websGetDefaultDir(), + CGI_BIN, cgiName); +#ifndef VXWORKS +/* + * See if the file exists and is executable. If not error out. + * Don't do this step for VxWorks, since the module may already + * be part of the OS image, rather than in the file system. + */ + { + gstat_t sbuf; + if (gstat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) { + websError(wp, 200, T("CGI process file does not exist")); + bfree(B_L, cgiPath); + return 1; + } +#if (defined (WIN) || defined (CE)) + if (gstrstr(cgiPath, T(".exe")) == NULL && + gstrstr(cgiPath, T(".bat")) == NULL) { +#elif (defined (NW)) + if (gstrstr(cgiPath, T(".nlm")) == NULL) { +#else + if (gaccess(cgiPath, X_OK) != 0) { +#endif /* WIN || CE */ + websError(wp, 200, T("CGI process file is not executable")); + bfree(B_L, cgiPath); + return 1; + } + } +#endif /* ! VXWORKS */ + + +/* + * Get the CWD for resetting after launching the child process CGI + */ + ggetcwd(cwd, FNAMESIZE); +/* + * Retrieve the directory of the child process CGI + */ + if ((cp = gstrrchr(cgiPath, '/')) != NULL) { + *cp = '\0'; + gchdir(cgiPath); + *cp = '/'; + } +/* + * Build command line arguments. Only used if there is no non-encoded + * = character. This is indicative of a ISINDEX query. POST separators + * are & and others are +. argp will point to a balloc'd array of + * pointers. Each pointer will point to substring within the + * query string. This array of string pointers is how the spawn or + * exec routines expect command line arguments to be passed. Since + * we don't know ahead of time how many individual items there are in + * the query string, the for loop includes logic to grow the array + * size via brealloc. + */ + argpsize = 10; + argp = balloc(B_L, argpsize * sizeof(char_t *)); + *argp = cgiPath; + n = 1; + if (gstrchr(query, '=') == NULL) { + websDecodeUrl(query, query, gstrlen(query)); + for (cp = gstrtok(query, T(" ")); cp != NULL; ) { + *(argp+n) = cp; + n++; + if (n >= argpsize) { + argpsize *= 2; + argp = brealloc(B_L, argp, argpsize * sizeof(char_t *)); + } + cp = gstrtok(NULL, T(" ")); + } + } + *(argp+n) = NULL; +/* + * Add all CGI variables to the environment strings to be passed + * to the spawned CGI process. This includes a few we don't + * already have in the symbol table, plus all those that are in + * the cgiVars symbol table. envp will point to a balloc'd array of + * pointers. Each pointer will point to a balloc'd string containing + * the keyword value pair in the form keyword=value. Since we don't + * know ahead of time how many environment strings there will be the + * for loop includes logic to grow the array size via brealloc. + */ + envpsize = WEBS_SYM_INIT; + envp = balloc(B_L, envpsize * sizeof(char_t *)); + n = 0; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("PATH_TRANSLATED"), cgiPath); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s/%s"),T("SCRIPT_NAME"), + CGI_BIN, cgiName); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("REMOTE_USER"), wp->userName); + n++; + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("AUTH_TYPE"), wp->authType); + n++; + for (s = symFirst(wp->cgiVars); s != NULL; s = symNext(wp->cgiVars)) { + if (s->content.valid && s->content.type == string && + gstrcmp(s->name.value.string, T("REMOTE_HOST")) != 0 && + gstrcmp(s->name.value.string, T("HTTP_AUTHORIZATION")) != 0) { + fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), s->name.value.string, + s->content.value.string); + n++; + if (n >= envpsize) { + envpsize *= 2; + envp = brealloc(B_L, envp, envpsize * sizeof(char_t *)); + } + } + } + *(envp+n) = NULL; +/* + * Create temporary file name(s) for the child's stdin and stdout. + * For POST data the stdin temp file (and name) should already exist. + */ + if (wp->cgiStdin == NULL) { + wp->cgiStdin = websGetCgiCommName(); + } + stdIn = wp->cgiStdin; + stdOut = websGetCgiCommName(); +/* + * Now launch the process. If not successful, do the cleanup of resources. + * If successful, the cleanup will be done after the process completes. + */ + if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut)) + == -1) { + websError(wp, 200, T("failed to spawn CGI task")); + for (ep = envp; *ep != NULL; ep++) { + bfreeSafe(B_L, *ep); + } + bfreeSafe(B_L, cgiPath); + bfreeSafe(B_L, argp); + bfreeSafe(B_L, envp); + bfreeSafe(B_L, stdOut); + } else { +/* + * If the spawn was successful, put this wp on a queue to be + * checked for completion. + */ + cid = hAllocEntry((void***) &cgiList, &cgiMax, sizeof(cgiRec)); + cgip = cgiList[cid]; + cgip->handle = pHandle; + cgip->stdIn = stdIn; + cgip->stdOut = stdOut; + cgip->cgiPath = cgiPath; + cgip->argp = argp; + cgip->envp = envp; + cgip->wp = wp; + cgip->fplacemark = 0; + websTimeoutCancel(wp); + } +/* + * Restore the current working directory after spawning child CGI + */ + gchdir(cwd); + return 1; +} + + + +/******************************************************************************/ +/* + * Any entry in the cgiList need to be checked to see if it has + */ +void websCgiGatherOutput (cgiRec *cgip) +{ + gstat_t sbuf; + char_t cgiBuf[FNAMESIZE]; + if ((gstat(cgip->stdOut, &sbuf) == 0) && + (sbuf.st_size > cgip->fplacemark)) { + int fdout; + fdout = gopen(cgip->stdOut, O_RDONLY | O_BINARY, 0444 ); +/* + * Check to see if any data is available in the + * output file and send its contents to the socket. + */ + if (fdout >= 0) { + webs_t wp = cgip->wp; + int nRead; +/* + * Write the HTTP header on our first pass + */ + if (cgip->fplacemark == 0) { + websWrite(wp, T("HTTP/1.0 200 OK\r\n")); + } + glseek(fdout, cgip->fplacemark, SEEK_SET); + while ((nRead = gread(fdout, cgiBuf, FNAMESIZE)) > 0) { + websWriteBlock(wp, cgiBuf, nRead); + cgip->fplacemark += nRead; + } + gclose(fdout); + } + } +} + + + +/******************************************************************************/ +/* + * Any entry in the cgiList need to be checked to see if it has + * completed, and if so, process its output and clean up. + */ +void websCgiCleanup() +{ + cgiRec *cgip; + webs_t wp; + char_t **ep; + int cid, nTries; + for (cid = 0; cid < cgiMax; cid++) { + if ((cgip = cgiList[cid]) != NULL) { + wp = cgip->wp; + websCgiGatherOutput (cgip); + if (websCheckCgiProc(cgip->handle) == 0) { +/* + * We get here if the CGI process has terminated. Clean up. + */ + nTries = 0; +/* + * Make sure we didn't miss something during a task switch. + * Maximum wait is 100 times 10 msecs (1 second). + */ + while ((cgip->fplacemark == 0) && (nTries < 100)) { + websCgiGatherOutput(cgip); +/* + * There are some cases when we detect app exit + * before the file is ready. + */ + if (cgip->fplacemark == 0) { +#ifdef WIN + Sleep(10); +#endif /* WIN*/ + } + nTries++; + } + if (cgip->fplacemark == 0) { + websError(wp, 200, T("CGI generated no output")); + } else { + websDone(wp, 200); + } +/* + * Remove the temporary re-direction files + */ + gunlink(cgip->stdIn); + gunlink(cgip->stdOut); +/* + * Free all the memory buffers pointed to by cgip. + * The stdin file name (wp->cgiStdin) gets freed as + * part of websFree(). + */ + cgiMax = hFree((void***) &cgiList, cid); + for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) { + bfreeSafe(B_L, *ep); + } + bfreeSafe(B_L, cgip->cgiPath); + bfreeSafe(B_L, cgip->argp); + bfreeSafe(B_L, cgip->envp); + bfreeSafe(B_L, cgip->stdOut); + bfreeSafe(B_L, cgip); + } + } + } +} +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/sockGen.c b/c/src/libnetworking/rtems_webserver/sockGen.c new file mode 100644 index 0000000000..99a53c8919 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/sockGen.c @@ -0,0 +1,1044 @@ + +/* + * sockGen.c -- Posix Socket support module for general posix use + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * $Id$ + */ + +/******************************** Description *********************************/ + +/* + * Posix Socket Module. This supports blocking and non-blocking buffered + * socket I/O. + */ + +#if (!defined (WIN) || defined (LITTLEFOOT) || defined (WEBS)) + +/********************************** Includes **********************************/ + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> + +#ifdef UEMF + #include "uemf.h" +#else + #include <socket.h> + #include <types.h> + #include <unistd.h> + #include "emfInternal.h" +#endif + +#ifdef VXWORKS + #include <hostLib.h> +#endif + +/************************************ Locals **********************************/ + +extern socket_t **socketList; /* List of open sockets */ +extern int socketMax; /* Maximum size of socket */ +extern int socketHighestFd; /* Highest socket fd opened */ +static int socketOpenCount = 0; /* Number of task using sockets */ + +/***************************** Forward Declarations ***************************/ + +static void socketAccept(socket_t *sp); +static int socketDoEvent(socket_t *sp); +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr); + +/*********************************** Code *************************************/ +/* + * Open socket module + */ + +int socketOpen() +{ +#if (defined (CE) || defined (WIN)) + WSADATA wsaData; +#endif + + if (++socketOpenCount > 1) { + return 0; + } + +#if (defined (CE) || defined (WIN)) + if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) { + return -1; + } + if (wsaData.wVersion != MAKEWORD(1,1)) { + WSACleanup(); + return -1; + } +#endif + + socketList = NULL; + socketMax = 0; + socketHighestFd = -1; + + return 0; +} + +/******************************************************************************/ +/* + * Close the socket module, by closing all open connections + */ + +void socketClose() +{ + int i; + + if (--socketOpenCount <= 0) { + for (i = socketMax; i >= 0; i--) { + if (socketList && socketList[i]) { + socketCloseConnection(i); + } + } + socketOpenCount = 0; + } +} + +/******************************************************************************/ +/* + * Open a client or server socket. Host is NULL if we want server capability. + */ + +int socketOpenConnection(char *host, int port, socketAccept_t accept, int flags) +{ +#if (!defined (NO_GETHOSTBYNAME) && !defined (VXWORKS)) + struct hostent *hostent; /* Host database entry */ +#endif /* ! (NO_GETHOSTBYNAME || VXWORKS) */ + socket_t *sp; + struct sockaddr_in sockaddr; + int sid, bcast, dgram, rc; + + if (port > SOCKET_PORT_MAX) { + return -1; + } +/* + * Allocate a socket structure + */ + if ((sid = socketAlloc(host, port, accept, flags)) < 0) { + return -1; + } + sp = socketList[sid]; + a_assert(sp); + +/* + * Create the socket address structure + */ + memset((char *) &sockaddr, '\0', sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons((short) (port & 0xFFFF)); + + if (host == NULL) { + sockaddr.sin_addr.s_addr = INADDR_ANY; + } else { + sockaddr.sin_addr.s_addr = inet_addr(host); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { +/* + * If the OS does not support gethostbyname functionality, the macro: + * NO_GETHOSTBYNAME should be defined to skip the use of gethostbyname. + * Unfortunatly there is no easy way to recover, the following code + * simply uses the basicGetHost IP for the sockaddr. + */ + +#ifdef NO_GETHOSTBYNAME + if (strcmp(host, basicGetHost()) == 0) { + sockaddr.sin_addr.s_addr = inet_addr(basicGetAddress()); + } + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + socketFree(sid); + return -1; + } +#elif (defined (VXWORKS)) + sockaddr.sin_addr.s_addr = (unsigned long) hostGetByName(host); + if (sockaddr.sin_addr.s_addr == NULL) { + errno = ENXIO; + socketFree(sid); + return -1; + } +#else + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &sockaddr.sin_addr, + (char *) hostent->h_addr_list[0], + (size_t) hostent->h_length); + } else { + char *asciiAddress; + char_t *address; + + address = basicGetAddress(); + asciiAddress = ballocUniToAsc(address, gstrlen(address)); + sockaddr.sin_addr.s_addr = inet_addr(asciiAddress); + bfree(B_L, asciiAddress); + if (sockaddr.sin_addr.s_addr == INADDR_NONE) { + errno = ENXIO; + socketFree(sid); + return -1; + } + } +#endif /* (NO_GETHOSTBYNAME || VXWORKS) */ + } + } + + bcast = sp->flags & SOCKET_BROADCAST; + if (bcast) { + sp->flags |= SOCKET_DATAGRAM; + } + dgram = sp->flags & SOCKET_DATAGRAM; + +/* + * Create the socket. Support for datagram sockets. Set the close on + * exec flag so children don't inherit the socket. + */ + sp->sock = socket(AF_INET, dgram ? SOCK_DGRAM: SOCK_STREAM, 0); + if (sp->sock < 0) { + socketFree(sid); + return -1; + } +#ifndef __NO_FCNTL + fcntl(sp->sock, F_SETFD, FD_CLOEXEC); +#endif + socketHighestFd = max(socketHighestFd, sp->sock); + +/* + * If broadcast, we need to turn on broadcast capability. + */ + if (bcast) { + int broadcastFlag = 1; + if (setsockopt(sp->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &broadcastFlag, sizeof(broadcastFlag)) < 0) { + socketFree(sid); + return -1; + } + } + +/* + * Host is set if we are the client + */ + if (host) { +/* + * Connect to the remote server in blocking mode, then go into + * non-blocking mode if desired. + */ + if (!dgram) { + if (! (sp->flags & SOCKET_BLOCK)) { +/* + * sockGen.c is only used for Windows products when blocking + * connects are expected. This applies to FieldUpgrader + * agents and open source webserver connectws. Therefore the + * asynchronous connect code here is not compiled. + */ +#if (defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS)) + int flag; + + sp->flags |= SOCKET_ASYNC; +/* + * Set to non-blocking for an async connect + */ + flag = 1; + if (ioctlsocket(sp->sock, FIONBIO, &flag) == SOCKET_ERROR) { + socketFree(sid); + return -1; + } +#else + socketSetBlock(sid, 1); +#endif /* #if (WIN || CE) && !(LITTLEFOOT || WEBS) */ + + } + if ((rc = connect(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr))) < 0 && + (rc = tryAlternateConnect(sp->sock, + (struct sockaddr *) &sockaddr)) < 0) { +#if (defined (WIN) || defined (CE)) + if (socketGetError() != EWOULDBLOCK) { + socketFree(sid); + return -1; + } +#else + socketFree(sid); + return -1; + +#endif /* WIN || CE */ + + } + } + } else { +/* + * Bind to the socket endpoint and the call listen() to start listening + */ + rc = 1; + setsockopt(sp->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&rc, sizeof(rc)); + if (bind(sp->sock, (struct sockaddr *) &sockaddr, + sizeof(sockaddr)) < 0) { + socketFree(sid); + return -1; + } + + if (! dgram) { + if (listen(sp->sock, SOMAXCONN) < 0) { + socketFree(sid); + return -1; + } +#ifndef UEMF + sp->fileHandle = emfCreateFileHandler(sp->sock, SOCKET_READABLE, + (emfFileProc *) socketAccept, (void *) sp); +#else + sp->flags |= SOCKET_LISTENING; +#endif + } + sp->handlerMask |= SOCKET_READABLE; + } + +/* + * Set the blocking mode + */ + + if (flags & SOCKET_BLOCK) { + socketSetBlock(sid, 1); + } else { + socketSetBlock(sid, 0); + } + return sid; +} + + +/******************************************************************************/ +/* + * If the connection failed, swap the first two bytes in the + * sockaddr structure. This is a kludge due to a change in + * VxWorks between versions 5.3 and 5.4, but we want the + * product to run on either. + */ + +static int tryAlternateConnect(int sock, struct sockaddr *sockaddr) +{ +#ifdef VXWORKS + char *ptr; + + ptr = (char *)sockaddr; + *ptr = *(ptr+1); + *(ptr+1) = 0; + return connect(sock, sockaddr, sizeof(struct sockaddr)); +#else + return -1; +#endif /* VXWORKS */ +} + +/******************************************************************************/ +/* + * Close a socket + */ + +void socketCloseConnection(int sid) +{ + socket_t *sp; + + if ((sp = socketPtr(sid)) == NULL) { + return; + } + socketFree(sid); +} + +/******************************************************************************/ +/* + * Accept a connection. Called as a callback on incoming connection. + */ + +static void socketAccept(socket_t *sp) +{ + struct sockaddr_in addr; + socket_t *nsp; + size_t len; + char *pString; + int newSock, nid; + + +#ifdef NW + NETINET_DEFINE_CONTEXT; +#endif + + a_assert(sp); + +/* + * Accept the connection and prevent inheriting by children (F_SETFD) + */ + len = sizeof(struct sockaddr_in); + if ((newSock = accept(sp->sock, (struct sockaddr *) &addr, (int *) &len)) < 0) { + return; + } +#ifndef __NO_FCNTL + fcntl(newSock, F_SETFD, FD_CLOEXEC); +#endif + socketHighestFd = max(socketHighestFd, newSock); + +/* + * Create a socket structure and insert into the socket list + */ + nid = socketAlloc(sp->host, sp->port, sp->accept, sp->flags); + nsp = socketList[nid]; + a_assert(nsp); + nsp->sock = newSock; + nsp->flags &= ~SOCKET_LISTENING; + + if (nsp == NULL) { + return; + } +/* + * Set the blocking mode before calling the accept callback. + */ + + socketSetBlock(nid, (nsp->flags & SOCKET_BLOCK) ? 1: 0); +/* + * Call the user accept callback. The user must call socketCreateHandler + * to register for further events of interest. + */ + if (sp->accept != NULL) { + pString = inet_ntoa(addr.sin_addr); + if ((sp->accept)(nid, pString, ntohs(addr.sin_port), sp->sid) < 0) { + socketFree(nid); + } +#ifdef VXWORKS + free(pString); +#endif + } +} + +/******************************************************************************/ +/* + * Get more input from the socket and return in buf. + * Returns 0 for EOF, -1 for errors and otherwise the number of bytes read. + */ + +int socketGetInput(int sid, char *buf, int toRead, int *errCode) +{ + struct sockaddr_in server; + socket_t *sp; + int len, bytesRead; + + a_assert(buf); + a_assert(errCode); + + *errCode = 0; + + if ((sp = socketPtr(sid)) == NULL) { + return -1; + } + +/* + * If we have previously seen an EOF condition, then just return + */ + if (sp->flags & SOCKET_EOF) { + return 0; + } +#if ((defined (WIN) || defined (CE)) && (!defined (LITTLEFOOT) && !defined (WEBS))) + if ( !(sp->flags & SOCKET_BLOCK) + && ! socketWaitForEvent(sp, FD_CONNECT, errCode)) { + return -1; + } +#endif + +/* + * Read the data + */ + if (sp->flags & SOCKET_DATAGRAM) { + len = sizeof(server); + bytesRead = recvfrom(sp->sock, buf, toRead, 0, + (struct sockaddr *) &server, &len); + } else { + bytesRead = recv(sp->sock, buf, toRead, 0); + } + + /* + * BUG 01865 -- CPU utilization hangs on Windows. The original code used + * the 'errno' global variable, which is not set by the winsock functions + * as it is under *nix platforms. We use the platform independent + * socketGetError() function instead, which does handle Windows correctly. + * Other, *nix compatible platforms should work as well, since on those + * platforms, socketGetError() just returns the value of errno. + * Thanks to Jonathan Burgoyne for the fix. + */ + if (bytesRead < 0) + { + *errCode = socketGetError(); + if (*errCode == ECONNRESET) + { + sp->flags |= SOCKET_CONNRESET; + return 0; + } + return -1; + } + return bytesRead; +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +#ifndef UEMF + +static int socketEventProc(void *data, int mask) +{ + socket_t *sp; + ringq_t *rq; + int sid; + + sid = (int) data; + + a_assert(sid >= 0 && sid < socketMax); + a_assert(socketList[sid]); + + if ((sp = socketPtr(sid)) == NULL) { + return 1; + } + +/* + * If now writable and flushing in the background, continue flushing + */ + if (mask & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. + */ + if (sp->handler && (sp->handlerMask & mask)) { + (sp->handler)(sid, mask & sp->handlerMask, sp->handler_data); + } + if (socketList && sid < socketMax && socketList[sid] == sp) { + socketRegisterInterest(sp, sp->handlerMask); + } + return 1; +} +#endif /* ! UEMF */ + +/******************************************************************************/ +/* + * Define the events of interest + */ + +void socketRegisterInterest(socket_t *sp, int handlerMask) +{ + a_assert(sp); + + sp->handlerMask = handlerMask; +#ifndef UEMF + if (handlerMask) { + sp->fileHandle = emfCreateFileHandler(sp->sock, handlerMask, + (emfFileProc *) socketEventProc, (void *) sp->sid); + } else { + emfDeleteFileHandler(sp->fileHandle); + sp->fileHandle = -1; + } +#endif /* ! UEMF */ +} + +/******************************************************************************/ +/* + * Wait until an event occurs on a socket. Return 1 on success, 0 on failure. + * or -1 on exception (UEMF only) + */ + +int socketWaitForEvent(socket_t *sp, int handlerMask, int *errCode) +{ + int mask; + + a_assert(sp); + + mask = sp->handlerMask; + sp->handlerMask |= handlerMask; + while (socketSelect(sp->sid, 1000)) { + if (sp->currentEvents & (handlerMask | SOCKET_EXCEPTION)) { + break; + } + } + sp->handlerMask = mask; + if (sp->currentEvents & SOCKET_EXCEPTION) { + return -1; + } else if (sp->currentEvents & handlerMask) { + return 1; + } + if (errCode) { + *errCode = errno = EWOULDBLOCK; + } + return 0; +} + +/******************************************************************************/ +/* + * Return TRUE if there is a socket with an event ready to process, + */ + +int socketReady(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + sid = 0; + all = 1; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (sp->flags & SOCKET_CONNRESET) { + socketCloseConnection(sid); + return 0; + } + if (sp->currentEvents & sp->handlerMask) { + return 1; + } +/* + * If there is input data, also call select to test for new events + */ + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + socketSelect(sid, 0); + return 1; + } + if (! all) { + break; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Wait for a handle to become readable or writable and return a number of + * noticed events. Timeout is in milliseconds. + */ + +#if (defined (WIN) || defined (CE) || defined (NW)) + +int socketSelect(int sid, int timeout) +{ + struct timeval tv; + socket_t *sp; + fd_set readFds, writeFds, exceptFds; + int nEvents; + int all, socketHighestFd; /* Highest socket fd opened */ + + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + FD_ZERO(&exceptFds); + socketHighestFd = -1; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + +/* + * Set the select event masks for events to watch + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + a_assert(sp); +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + if (sp->handlerMask & SOCKET_READABLE) { + FD_SET(sp->sock, &readFds); + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + FD_SET(sp->sock, &writeFds); + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + FD_SET(sp->sock, &exceptFds); + nEvents++; + } + if (! all) { + break; + } + } + +/* + * Windows select() fails if no descriptors are set, instead of just sleeping + * like other, nice select() calls. So, if WIN, sleep. + */ + if (nEvents == 0) { + Sleep(timeout); + return 0; + } + +/* + * Wait for the event or a timeout. + */ + nEvents = select(socketHighestFd+1, &readFds, &writeFds, &exceptFds, &tv); + + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + continue; + } + + if (FD_ISSET(sp->sock, &readFds) || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (FD_ISSET(sp->sock, &writeFds)) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (FD_ISSET(sp->sock, &exceptFds)) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; + } + } + + return nEvents; +} + +#else /* not WIN || CE || NW */ + +int socketSelect(int sid, int timeout) +{ + socket_t *sp; + struct timeval tv; + fd_mask *readFds, *writeFds, *exceptFds; + int all, len, nwords, index, bit, nEvents; + +/* + * Allocate and zero the select masks + */ + nwords = (socketHighestFd + NFDBITS) / NFDBITS; + len = nwords * sizeof(int); + + readFds = balloc(B_L, len); + memset(readFds, 0, len); + writeFds = balloc(B_L, len); + memset(writeFds, 0, len); + exceptFds = balloc(B_L, len); + memset(exceptFds, 0, len); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + +/* + * Set the select event masks for events to watch + */ + all = nEvents = 0; + + if (sid < 0) { + all++; + sid = 0; + } + + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (all == 0) { + break; + } else { + continue; + } + } + a_assert(sp); + +/* + * Initialize the ready masks and compute the mask offsets. + */ + index = sp->sock / (NBBY * sizeof(fd_mask)); + bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); + +/* + * Set the appropriate bit in the ready masks for the sp->sock. + */ + if (sp->handlerMask & SOCKET_READABLE) { + readFds[index] |= bit; + nEvents++; + if (socketInputBuffered(sid) > 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + if (sp->handlerMask & SOCKET_WRITABLE) { + writeFds[index] |= bit; + nEvents++; + } + if (sp->handlerMask & SOCKET_EXCEPTION) { + exceptFds[index] |= bit; + nEvents++; + } + if (! all) { + break; + } + } + +/* + * Wait for the event or a timeout. Reset nEvents to be the number of actual + * events now. + */ + nEvents = select(socketHighestFd + 1, (fd_set *) readFds, + (fd_set *) writeFds, (fd_set *) exceptFds, &tv); + + if (nEvents > 0) { + if (all) { + sid = 0; + } + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (all == 0) { + break; + } else { + continue; + } + } + + index = sp->sock / (NBBY * sizeof(fd_mask)); + bit = 1 << (sp->sock % (NBBY * sizeof(fd_mask))); + + if (readFds[index] & bit || socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + if (writeFds[index] & bit) { + sp->currentEvents |= SOCKET_WRITABLE; + } + if (exceptFds[index] & bit) { + sp->currentEvents |= SOCKET_EXCEPTION; + } + if (! all) { + break; + } + } + } + + bfree(B_L, readFds); + bfree(B_L, writeFds); + bfree(B_L, exceptFds); + + return nEvents; +} +#endif /* WIN || CE */ + +/******************************************************************************/ +/* + * Process socket events + */ + +void socketProcess(int sid) +{ + socket_t *sp; + int all; + + all = 0; + if (sid < 0) { + all = 1; + sid = 0; + } +/* + * Process each socket + */ + for (; sid < socketMax; sid++) { + if ((sp = socketList[sid]) == NULL) { + if (! all) { + break; + } else { + continue; + } + } + if (socketReady(sid)) { + socketDoEvent(sp); + } + if (! all) { + break; + } + } +} + +/******************************************************************************/ +/* + * Process an event on the event queue + */ + +static int socketDoEvent(socket_t *sp) +{ + ringq_t *rq; + int sid; + + a_assert(sp); + + sid = sp->sid; + if (sp->currentEvents & SOCKET_READABLE) { + if (sp->flags & SOCKET_LISTENING) { + socketAccept(sp); + sp->currentEvents = 0; + return 1; + } + + } else { +/* + * If there is still read data in the buffers, trigger the read handler + * NOTE: this may busy spin if the read handler doesn't read the data + */ + if (sp->handlerMask & SOCKET_READABLE && socketInputBuffered(sid) > 0) { + sp->currentEvents |= SOCKET_READABLE; + } + } + + +/* + * If now writable and flushing in the background, continue flushing + */ + if (sp->currentEvents & SOCKET_WRITABLE) { + if (sp->flags & SOCKET_FLUSHING) { + rq = &sp->outBuf; + if (ringqLen(rq) > 0) { + socketFlush(sp->sid); + } else { + sp->flags &= ~SOCKET_FLUSHING; + } + } + } + +/* + * Now invoke the users socket handler. NOTE: the handler may delete the + * socket, so we must be very careful after calling the handler. + */ + if (sp->handler && (sp->handlerMask & sp->currentEvents)) { + (sp->handler)(sid, sp->handlerMask & sp->currentEvents, + sp->handler_data); +/* + * Make sure socket pointer is still valid, then reset the currentEvents. + */ + if (socketList && sid < socketMax && socketList[sid] == sp) { + sp->currentEvents = 0; + } + } + return 1; +} + +/******************************************************************************/ +/* + * Set the socket blocking mode + */ + +int socketSetBlock(int sid, int on) +{ + socket_t *sp; + unsigned long flag; + int iflag; + int oldBlock; + + flag = iflag = !on; + + if ((sp = socketPtr(sid)) == NULL) { + a_assert(0); + return 0; + } + oldBlock = (sp->flags & SOCKET_BLOCK); + sp->flags &= ~(SOCKET_BLOCK); + if (on) { + sp->flags |= SOCKET_BLOCK; + } + +/* + * Put the socket into block / non-blocking mode + */ + if (sp->flags & SOCKET_BLOCK) { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int off; + off = 0; + ioctl(sp->sock, FIONBIO, &off); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) & ~O_NONBLOCK); +#endif + + } else { +#if (defined (CE) || defined (WIN)) + ioctlsocket(sp->sock, FIONBIO, &flag); +#elif (defined (ECOS)) + int on; + on = 1; + ioctl(sp->sock, FIONBIO, &on); +#elif (defined (VXWORKS) || defined (NW)) + ioctl(sp->sock, FIONBIO, (int)&iflag); +#else + fcntl(sp->sock, F_SETFL, fcntl(sp->sock, F_GETFL) | O_NONBLOCK); +#endif + } + return oldBlock; +} + +/******************************************************************************/ +/* + * Return true if a readable socket has buffered data. - not public + */ + +int socketDontBlock() +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || + (sp->handlerMask & SOCKET_READABLE) == 0) { + continue; + } + if (socketInputBuffered(i) > 0) { + return 1; + } + } + return 0; +} + +/******************************************************************************/ +/* + * Return true if a particular socket buffered data. - not public + */ + +int socketSockBuffered(int sock) +{ + socket_t *sp; + int i; + + for (i = 0; i < socketMax; i++) { + if ((sp = socketList[i]) == NULL || sp->sock != sock) { + continue; + } + return socketInputBuffered(i); + } + return 0; +} + +#endif /* (!WIN) | LITTLEFOOT | WEBS */ + +/******************************************************************************/ + diff --git a/c/src/libnetworking/rtems_webserver/umui.c b/c/src/libnetworking/rtems_webserver/umui.c new file mode 100644 index 0000000000..971b059bc7 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/umui.c @@ -0,0 +1,641 @@ +/* + * umui.c -- User Management GoForm Processing + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ + +/* + * This module provides GoForm functions for User management + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" +#include "um.h" + +/********************************* Defines ************************************/ + +#define NONE_OPTION T("<NONE>") +#define MSG_START T("<body><h2>") +#define MSG_END T("</h2></body>") + +/**************************** Forward Declarations ****************************/ + +static void formAddUser(webs_t wp, char_t *path, char_t *query); +static void formDeleteUser(webs_t wp, char_t *path, char_t *query); +static void formDisplayUser(webs_t wp, char_t *path, char_t *query); +static int aspGenerateUserList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formAddGroup(webs_t wp, char_t *path, char_t *query); +static void formDeleteGroup(webs_t wp, char_t *path, char_t *query); +static int aspGenerateGroupList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formAddAccessLimit(webs_t wp, char_t *path, char_t *query); +static void formDeleteAccessLimit(webs_t wp, char_t *path, char_t *query); +static int aspGenerateAccessLimitList(int eid, webs_t wp, + int argc, char_t **argv); + +static int aspGenerateAccessMethodList(int eid, webs_t wp, + int argc, char_t **argv); +static int aspGeneratePrivilegeList(int eid, webs_t wp, + int argc, char_t **argv); + +static void formSaveUserManagement(webs_t wp, char_t *path, char_t *query); +static void formLoadUserManagement(webs_t wp, char_t *path, char_t *query); + +static void websMsgStart(webs_t wp); +static void websMsgEnd(webs_t wp); + +/*********************************** Code *************************************/ +/* + * Set up the User Management form handlers + */ + +void formDefineUserMgmt(void) +{ + websAspDefine(T("MakeGroupList"), aspGenerateGroupList); + websAspDefine(T("MakeUserList"), aspGenerateUserList); + websAspDefine(T("MakeAccessLimitList"), aspGenerateAccessLimitList); + websAspDefine(T("MakeAccessMethodList"), aspGenerateAccessMethodList); + websAspDefine(T("MakePrivilegeList"), aspGeneratePrivilegeList); + + websFormDefine(T("AddUser"), formAddUser); + websFormDefine(T("DeleteUser"), formDeleteUser); + websFormDefine(T("DisplayUser"), formDisplayUser); + websFormDefine(T("AddGroup"), formAddGroup); + websFormDefine(T("DeleteGroup"), formDeleteGroup); + websFormDefine(T("AddAccessLimit"), formAddAccessLimit); + websFormDefine(T("DeleteAccessLimit"), formDeleteAccessLimit); + + websFormDefine(T("SaveUserManagement"), formSaveUserManagement); + websFormDefine(T("LoadUserManagement"), formLoadUserManagement); +} + +/******************************************************************************/ +/* + * Add a user + */ + +static void formAddUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *pass1, *pass2, *group, *enabled, *ok; + bool_t bDisable; + int nCheck; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + pass1 = websGetVar(wp, T("password"), T("")); + pass2 = websGetVar(wp, T("passconf"), T("")); + group = websGetVar(wp, T("group"), T("")); + enabled = websGetVar(wp, T("enabled"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Add User Cancelled")); + } else if (gstrcmp(pass1, pass2) != 0) { + websWrite(wp, T("Confirmation Password did not match.")); + } else { + if (enabled && *enabled && (gstrcmp(enabled, T("on")) == 0)) { + bDisable = FALSE; + } else { + bDisable = TRUE; + } + + nCheck = umAddUser(userid, pass1, group, 0, bDisable); + if (nCheck != 0) { + char_t * strError; + + switch (nCheck) { + case UM_ERR_DUPLICATE: + strError = T("User already exists."); + break; + + case UM_ERR_BAD_NAME: + strError = T("Invalid user name."); + break; + + case UM_ERR_BAD_PASSWORD: + strError = T("Invalid password."); + break; + + case UM_ERR_NOT_FOUND: + strError = T("Invalid or unselected group."); + break; + + default: + strError = T("Error writing user record."); + break; + } + + websWrite(wp, T("Unable to add user, \"%s\". %s"), + userid, strError); + } else { + websWrite(wp, T("User, \"%s\" was successfully added."), + userid); + } + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Delete a user + */ + +static void formDeleteUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *ok; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Delete User Cancelled")); + } else if (umUserExists(userid) == FALSE) { + websWrite(wp, T("ERROR: User \"%s\" not found"), userid); + } else if (umGetUserProtected(userid)) { + websWrite(wp, T("ERROR: User, \"%s\" is delete-protected."), userid); + } else if (umDeleteUser(userid) != 0) { + websWrite(wp, T("ERROR: Unable to delete user, \"%s\" "), userid); + } else { + websWrite(wp, T("User, \"%s\" was successfully deleted."), userid); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Display the user info + */ + +static void formDisplayUser(webs_t wp, char_t *path, char_t *query) +{ + char_t *userid, *ok, *temp; + bool_t enabled; + + a_assert(wp); + + userid = websGetVar(wp, T("user"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websWrite(wp, T("<body>")); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Display User Cancelled")); + } else if (umUserExists(userid) == FALSE) { + websWrite(wp, T("ERROR: User <b>%s</b> not found.\n"), userid); + } else { + websWrite(wp, T("<h2>User ID: <b>%s</b></h2>\n"), userid); + temp = umGetUserGroup(userid); + websWrite(wp, T("<h3>User Group: <b>%s</b></h3>\n"), temp); + enabled = umGetUserEnabled(userid); + websWrite(wp, T("<h3>Enabled: <b>%d</b></h3>\n"), enabled); + } + + websWrite(wp, T("</body>\n")); + websFooter(wp); + websDone(wp, 200); +} + + +/******************************************************************************/ +/* + * Generate HTML to create a list box containing the users + */ + +static int aspGenerateUserList(int eid, webs_t wp, int argc, char_t **argv) +{ + char_t *userid; + int row, nBytesSent, nBytes; + + a_assert(wp); + + nBytes = websWrite(wp, + T("<SELECT NAME=\"user\" SIZE=\"3\" TITLE=\"Select a User\">")); + row = 0; + userid = umGetFirstUser(); + nBytesSent = 0; + + while (userid && (nBytes > 0)) { + nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"), + userid, userid); + userid = umGetNextUser(userid); + nBytesSent += nBytes; + } + + nBytesSent += websWrite(wp, T("</SELECT>")); + + return nBytesSent; +} + +/******************************************************************************/ +/* + * Add a group + */ + +static void formAddGroup(webs_t wp, char_t *path, char_t *query) +{ + char_t *group, *enabled, *privilege, *method, *ok, *pChar; + int nCheck; + short priv; + accessMeth_t am; + bool_t bDisable; + + a_assert(wp); + + group = websGetVar(wp, T("group"), T("")); + method = websGetVar(wp, T("method"), T("")); + enabled = websGetVar(wp, T("enabled"), T("")); + privilege = websGetVar(wp, T("privilege"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Add Group Cancelled.")); + } else if ((group == NULL) || (*group == 0)) { + websWrite(wp, T("No Group Name was entered.")); + } else if (umGroupExists(group)) { + websWrite(wp, T("ERROR: Group, \"%s\" already exists."), group); + } else { + if (privilege && *privilege) { +/* + * privilege is a mulitple <SELECT> var, and must be parsed. + * Values for these variables are space delimited. + */ + priv = 0; + for (pChar = privilege; *pChar; pChar++) { + if (*pChar == ' ') { + *pChar = '\0'; + priv |= gatoi(privilege); + *pChar = ' '; + privilege = pChar + 1; + } + } + priv |= gatoi(privilege); + } else { + priv = 0; + } + + if (method && *method) { + am = (accessMeth_t) gatoi(method); + } else { + am = AM_FULL; + } + + if (enabled && *enabled && (gstrcmp(enabled, T("on")) == 0)) { + bDisable = FALSE; + } else { + bDisable = TRUE; + } + + nCheck = umAddGroup(group, priv, am, 0, bDisable); + if (nCheck != 0) { + websWrite(wp, T("Unable to add group, \"%s\", code: %d "), + group, nCheck); + } else { + websWrite(wp, T("Group, \"%s\" was successfully added."), + group); + } + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Delete a group + */ + +static void formDeleteGroup(webs_t wp, char_t *path, char_t *query) +{ + char_t *group, *ok; + + a_assert(wp); + + group = websGetVar(wp, T("group"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Delete Group Cancelled.")); + } else if ((group == NULL) || (*group == '\0')) { + websWrite(wp, T("ERROR: No group was selected.")); + } else if (umGetGroupProtected(group)) { + websWrite(wp, T("ERROR: Group, \"%s\" is delete-protected."), group); + } else if (umGetGroupInUse(group)) { + websWrite(wp, T("ERROR: Group, \"%s\" is being used."), group); + } else if (umDeleteGroup(group) != 0) { + websWrite(wp, T("ERROR: Unable to delete group, \"%s\" "), group); + } else { + websWrite(wp, T("Group, \"%s\" was successfully deleted."), group); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Generate HTML to create a list box containing the groups + */ + +static int aspGenerateGroupList(int eid, webs_t wp, int argc, char_t **argv) +{ + char_t *group; + int row, nBytesSent, nBytes; + + a_assert(wp); + + row = 0; + nBytesSent = 0; + nBytes = websWrite(wp, + T("<SELECT NAME=\"group\" SIZE=\"3\" TITLE=\"Select a Group\">")); +/* + * Add a special "<NONE>" element to allow de-selection + */ + nBytes = websWrite(wp, T("<OPTION VALUE=\"\">[NONE]\n")); + + group = umGetFirstGroup(); + while (group && (nBytes > 0)) { + nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"), group, group); + group = umGetNextGroup(group); + nBytesSent += nBytes; + } + + nBytesSent += websWrite(wp, T("</SELECT>")); + + return nBytesSent; +} + +/******************************************************************************/ +/* + * Add an access limit + */ + +static void formAddAccessLimit(webs_t wp, char_t *path, char_t *query) +{ + char_t *url, *method, *group, *secure, *ok; + int nCheck; + accessMeth_t am; + short nSecure; + + a_assert(wp); + + url = websGetVar(wp, T("url"), T("")); + group = websGetVar(wp, T("group"), T("")); + method = websGetVar(wp, T("method"), T("")); + secure = websGetVar(wp, T("secure"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Add Access Limit Cancelled.")); + } else if ((url == NULL) || (*url == 0)) { + websWrite(wp, T("ERROR: No URL was entered.")); + } else if (umAccessLimitExists(url)) { + websWrite(wp, T("ERROR: An Access Limit for [%s] already exists."), + url); + } else { + if (method && *method) { + am = (accessMeth_t) gatoi(method); + } else { + am = AM_FULL; + } + + if (secure && *secure) { + nSecure = (short) gatoi(secure); + } else { + nSecure = 0; + } + + nCheck = umAddAccessLimit(url, am, nSecure, group); + if (nCheck != 0) { + websWrite(wp, T("Unable to add Access Limit for [%s]"), url); + } else { + websWrite(wp, T("Access limit for [%s], was successfully added."), + url); + } + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Delete an Access Limit + */ + +static void formDeleteAccessLimit(webs_t wp, char_t *path, char_t *query) +{ + char_t *url, *ok; + + a_assert(wp); + + url = websGetVar(wp, T("url"), T("")); + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Delete Access Limit Cancelled")); + } else if (umDeleteAccessLimit(url) != 0) { + websWrite(wp, T("ERROR: Unable to delete Access Limit for [%s]"), + url); + } else { + websWrite(wp, T("Access Limit for [%s], was successfully deleted."), + url); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Generate HTML to create a list box containing the access limits + */ + +static int aspGenerateAccessLimitList(int eid, webs_t wp, + int argc, char_t **argv) +{ + char_t *url; + int row, nBytesSent, nBytes; + + a_assert(wp); + + row = nBytesSent = 0; + url = umGetFirstAccessLimit(); + nBytes = websWrite(wp, + T("<SELECT NAME=\"url\" SIZE=\"3\" TITLE=\"Select a URL\">")); + + while (url && (nBytes > 0)) { + nBytes = websWrite(wp, T("<OPTION VALUE=\"%s\">%s\n"), url, url); + url = umGetNextAccessLimit(url); + nBytesSent += nBytes; + } + + nBytesSent += websWrite(wp, T("</SELECT>")); + + return nBytesSent; +} + +/******************************************************************************/ +/* + * Generate HTML to create a list box containing the access methods + */ + +static int aspGenerateAccessMethodList(int eid, webs_t wp, + int argc, char_t **argv) +{ + int nBytes; + + a_assert(wp); + + nBytes = websWrite(wp, + T("<SELECT NAME=\"method\" SIZE=\"3\" TITLE=\"Select a Method\">")); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">FULL ACCESS\n"), + AM_FULL); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">BASIC ACCESS\n"), + AM_BASIC); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\" SELECTED>DIGEST ACCESS\n"), + AM_DIGEST); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">NO ACCESS\n"), + AM_NONE); + nBytes += websWrite(wp, T("</SELECT>")); + + return nBytes; +} +/******************************************************************************/ +/* + * Generate HTML to create a list box containing privileges + */ + +static int aspGeneratePrivilegeList(int eid, webs_t wp, + int argc, char_t **argv) +{ + int nBytes; + + a_assert(wp); + + nBytes = websWrite(wp, T("<SELECT NAME=\"privilege\" SIZE=\"3\" ")); + nBytes += websWrite(wp, T("MULTIPLE TITLE=\"Choose Privileges\">")); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">READ\n"), PRIV_READ); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">EXECUTE\n"), PRIV_WRITE); + nBytes += websWrite(wp, T("<OPTION VALUE=\"%d\">ADMINISTRATE\n"), + PRIV_ADMIN); + nBytes += websWrite(wp, T("</SELECT>")); + + return nBytes; +} + +/******************************************************************************/ +/* + * Save the user management configuration to a file + */ + +static void formSaveUserManagement(webs_t wp, char_t *path, char_t *query) +{ + char_t *ok; + + a_assert(wp); + + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Save Cancelled.")); + } else if (umCommit(NULL) != 0) { + websWrite(wp, T("ERROR: Unable to save user configuration.")); + } else { + websWrite(wp, T("User configuration was saved successfully.")); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Load the user management configuration from a file + */ + +static void formLoadUserManagement(webs_t wp, char_t *path, char_t *query) +{ + char_t *ok; + + a_assert(wp); + + ok = websGetVar(wp, T("ok"), T("")); + + websHeader(wp); + websMsgStart(wp); + + if (gstricmp(ok, T("ok")) != 0) { + websWrite(wp, T("Load Cancelled.")); + } else if (umRestore(NULL) != 0) { + websWrite(wp, T("ERROR: Unable to load user configuration.")); + } else { + websWrite(wp, T("User configuration was re-loaded successfully.")); + } + + websMsgEnd(wp); + websFooter(wp); + websDone(wp, 200); +} + +/******************************************************************************/ +/* + * Message start and end convenience functions + */ + +static void websMsgStart(webs_t wp) +{ + websWrite(wp, MSG_START); +} + +static void websMsgEnd(webs_t wp) +{ + websWrite(wp, MSG_END); +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/websSSL.c b/c/src/libnetworking/rtems_webserver/websSSL.c new file mode 100644 index 0000000000..6c2e4ce5da --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/websSSL.c @@ -0,0 +1,706 @@ +/* + * websSSL.c -- SSL envrionment creation + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ + +/* + * This module implements a patch into SSL implementations for the webs + * module. + */ + +/********************************* Includes ***********************************/ + +#include "wsIntrn.h" +#include "webs.h" +#include "websSSL.h" + +/******************************* Definitions **********************************/ + +#define DEFAULT_CERT_FILE "./server.pem" +#define DEFAULT_KEY_FILE "./certs/cakey.pem" +#define DEFAULT_CA_FILE "./certs/cacert.pem" +#define DEFAULT_CA_PATH "./certs/" +#define SSL_PORT 443 + +/* + * Define the components of the apps_startup() macro + */ + +#ifdef SIGPIPE +#define do_pipe_sig() signal(SIGPIPE,SIG_IGN) +#else +#define do_pipe_sig() +#endif + +#ifdef OPENSSL +#define SSLC_add_all_algorithms() SSLeay_add_all_algorithms() +#else +extern void SSLC_add_all_algorithms(void); +#endif + +/* + * Define the apps_startup() macro + */ + +# if defined(MSDOS) || defined(WIN16) || defined(WIN32) +# ifdef _O_BINARY +# define apps_startup() \ + _fmode=_O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \ + SSLC_add_all_algorithms() +# else +# define apps_startup() \ + _fmode=O_BINARY; do_pipe_sig(); CRYPTO_malloc_init(); \ + SSLC_add_all_algorithms() +# endif +# else +# define apps_startup() do_pipe_sig(); SSLC_add_all_algorithms(); +# endif + +/*************************** Forward Declarations *****************************/ + +static int websSSLSetCertStuff(SSL_CTX *ctx, + char *cert_file, + char *key_file); +static int websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx); +static RSA *websSSLTempRSACallback(SSL *s, int is_export, int keylength); + +static int websSSLReadEvent (webs_t wp); +static int websSSLAccept(int sid, char *ipaddr, int port, int listenSid); +static void websSSLSocketEvent(int sid, int mask, int data); + +/*********************************** Locals ***********************************/ + +static int sslListenSock = -1; /* Listen socket */ +static SSL_CTX *sslctx = NULL; + +/******************************************************************************/ +/* + * Start up the SSL Context for the application, and start a listen on the + * SSL port (usually 443, and defined by SSL_PORT) + * Return 0 on success, -1 on failure. + */ + +int websSSLOpen() +{ + char *certFile, *keyFile, *CApath, *CAfile; + SSL_METHOD *meth; + +/* + * Install and initialize the SSL library + */ + apps_startup(); + trace(7, T("SSL: Initializing SSL\n")); + +#ifdef SSLC + SSL_library_init(); +#endif + + SSL_load_error_strings(); + +#ifdef OPENSSL + SSLeay_add_ssl_algorithms(); +#endif + +/* + * Important! Enable both SSL versions 2 and 3 + */ + meth = SSLv23_server_method(); + sslctx = SSL_CTX_new(meth); + + a_assert(sslctx); + + if (sslctx == NULL) { + trace(2, T("SSL: Unable to create SSL context!\n")); + return -1; + } + +/* + * Adjust some SSL Context variables + */ + SSL_CTX_set_quiet_shutdown(sslctx, 1); + SSL_CTX_set_options(sslctx, 0); + SSL_CTX_sess_set_cache_size(sslctx, 128); + +/* + * Set the certificate verification locations + */ + CApath = DEFAULT_CA_PATH; + CAfile = DEFAULT_CA_FILE; + if ((!SSL_CTX_load_verify_locations(sslctx, CAfile, CApath)) || + (!SSL_CTX_set_default_verify_paths(sslctx))) { + trace(2, T("SSL: Unable to set cert verification locations!\n")); + websSSLClose(); + return -1; + } + +/* + * Set the certificate and key files for the SSL context + */ + certFile = DEFAULT_CERT_FILE; + keyFile = NULL; + if (websSSLSetCertStuff(sslctx, certFile, keyFile) != 0) { + websSSLClose(); + return -1; + } + +/* + * Set the RSA callback for the SSL context + */ + SSL_CTX_set_tmp_rsa_callback(sslctx, websSSLTempRSACallback); + +/* + * Set the verification callback for the SSL context + */ + SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, websSSLVerifyCallback); + +/* + * Set the certificate authority list for the client + */ + SSL_CTX_set_client_CA_list(sslctx, SSL_load_client_CA_file(CAfile)); + +/* + * Open the socket + */ + sslListenSock = socketOpenConnection(NULL, SSL_PORT, + websSSLAccept, SOCKET_BLOCK); + + if (sslListenSock < 0) { + trace(2, T("SSL: Unable to open SSL socket on port <%d>!\n"), + SSL_PORT); + return -1; + } + + return 0; +} + +/******************************************************************************/ +/* + * Return TRUE if websSSL has been opened + */ + +int websSSLIsOpen() +{ + return (sslListenSock != -1); +} + +/******************************************************************************/ +/* + * Stops the SSL + */ + +void websSSLClose() +{ + trace(7, T("SSL: Closing SSL\n")); + + if (sslctx != NULL) { + SSL_CTX_free(sslctx); + sslctx = NULL; + } + + if (sslListenSock != -1) { + socketCloseConnection(sslListenSock); + sslListenSock = -1; + } + +#ifdef SSLC + SSL_library_cleanup(); +#endif +} + +/******************************************************************************/ +/* + * Accept a connection + */ + +int websSSLAccept(int sid, char *ipaddr, int port, int listenSid) +{ + webs_t wp; + int wid; + + a_assert(ipaddr && *ipaddr); + a_assert(sid >= 0); + a_assert(port >= 0); + +/* + * Allocate a new handle for this accepted connection. This will allocate + * a webs_t structure in the webs[] list + */ + if ((wid = websAlloc(sid)) < 0) { + return -1; + } + wp = webs[wid]; + a_assert(wp); + wp->listenSid = listenSid; + + ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr)+1)); + +/* + * Check if this is a request from a browser on this system. This is useful + * to know for permitting administrative operations only for local access + */ + if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 || + gstrcmp(wp->ipaddr, websIpaddr) == 0 || + gstrcmp(wp->ipaddr, websHost) == 0) { + wp->flags |= WEBS_LOCAL_REQUEST; + } +/* + * Since the acceptance came in on this channel, it must be secure + */ + wp->flags |= WEBS_SECURE; + +/* + * Arrange for websSocketEvent to be called when read data is available + */ + socketCreateHandler(sid, SOCKET_READABLE, websSSLSocketEvent, (int) wp); + +/* + * Arrange for a timeout to kill hung requests + */ + wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp); + trace(8, T("webs: accept request\n")); + return 0; +} + +/******************************************************************************/ +/* + * The webs socket handler. Called in response to I/O. We just pass control + * to the relevant read or write handler. A pointer to the webs structure + * is passed as an (int) in iwp. + */ + +static void websSSLSocketEvent(int sid, int mask, int iwp) +{ + webs_t wp; + + wp = (webs_t) iwp; + a_assert(wp); + + if (! websValid(wp)) { + return; + } + + if (mask & SOCKET_READABLE) { + websSSLReadEvent(wp); + } + if (mask & SOCKET_WRITABLE) { + if (wp->writeSocket) { + (*wp->writeSocket)(wp); + } + } +} + +/******************************************************************************/ +/* + * Handler for SSL Read Events + */ + +static int websSSLReadEvent (webs_t wp) +{ + int ret, sock; + socket_t *sptr; + SSL *ssl; + BIO *bio, *bioSSL, *bioSock; +#ifdef DEV + const char *ciphers; +#endif + + a_assert (wp); + a_assert(websValid(wp)); + + sptr = socketPtr(wp->sid); + a_assert(sptr); + + sock = sptr->sock; + +/* + * Create a new BIO and SSL session for this web request + */ + bio = BIO_new(BIO_f_buffer()); + a_assert(bio); + + if (!BIO_set_write_buffer_size(bio, 128)) { + return -1; + } + + ssl = (SSL *) SSL_new(sslctx); + a_assert(ssl); + + if (ssl == NULL) { + return -1; + } + + SSL_set_session(ssl, NULL); + + bioSSL = BIO_new(BIO_f_ssl()); + a_assert(bioSSL); + + bioSock = BIO_new_socket(sock, BIO_NOCLOSE); + a_assert(bioSock); + + SSL_set_bio(ssl, bioSock, bioSock); + SSL_set_accept_state(ssl); + + ret = BIO_set_ssl(bioSSL, ssl, BIO_CLOSE); + BIO_push(bio, bioSSL); + +#ifdef DEV + ciphers = SSL_get_cipher_list(ssl, 10); +#endif + +/* + * Create the SSL data structure in the wp. + */ +#ifdef WEBS_SSL_SUPPORT + wp->wsp = balloc(B_L, sizeof(websSSL_t)); + a_assert (wp->wsp); + (wp->wsp)->bio = bio; + (wp->wsp)->ssl = ssl; +#endif + +/* + * Call the default Read Event + */ + websReadEvent(wp); + + return ret; +} + + +/******************************************************************************/ +/* + * SSL Verification Callback + */ + +static int sslVerifyDepth = 0; +static int sslVerifyError = X509_V_OK; + +int websSSLVerifyCallback(int ok, X509_STORE_CTX *ctx) +{ + char buf[256]; + X509 *errCert; + int err; + int depth; + + errCert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + X509_NAME_oneline(X509_get_subject_name(errCert), buf, 256); + + if (!ok) { + if (sslVerifyDepth >= depth) { + ok = 1; + sslVerifyError = X509_V_OK; + } else { + ok=0; + sslVerifyError = X509_V_ERR_CERT_CHAIN_TOO_LONG; + } + } + + switch (err) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: +#ifdef OPENSSL + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); +#endif + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + break; + } + + return ok; +} + +/******************************************************************************/ +/* + * Set the SSL certificate and key for the SSL context + */ + +int websSSLSetCertStuff(SSL_CTX *ctx, char *certFile, char *keyFile) +{ + a_assert (ctx); + a_assert (certFile); + + if (certFile != NULL) { + if (SSL_CTX_use_certificate_file(ctx, certFile, + SSL_FILETYPE_PEM) <= 0) { + trace(2, T("SSL: Unable to set certificate file <%s>\n"), + certFile); + return -1; + } + + if (keyFile == NULL) { + keyFile = certFile; + } + + if (SSL_CTX_use_PrivateKey_file(ctx, keyFile, SSL_FILETYPE_PEM) <= 0) { + trace(2, T("SSL: Unable to set private key file <%s>\n"), + keyFile); + return -1; + } + +/* + * Now we know that a key and cert have been set against + * the SSL context + */ + if (!SSL_CTX_check_private_key(ctx)) { + trace(2, T("SSL: Check of private key file <%s> FAILED!\n"), + keyFile); + return -1; + } + } + + return 0; +} + +/******************************************************************************/ +/* + * Set certificate file for SSL context + */ + +int websSSLSetCertFile(char_t *certFile) +{ + a_assert (sslctx); + a_assert (certFile); + + if (sslctx == NULL) { + return -1; + } + + if (SSL_CTX_use_certificate_file(sslctx, certFile, + SSL_FILETYPE_PEM) <= 0) { + return -1; + } +/* + * Confirm that the certificate and the private key jive. + */ + if (!SSL_CTX_check_private_key(sslctx)) { + return -1; + } + + return 0; +} + +/******************************************************************************/ +/* + * Set key file for SSL context + */ + +int websSSLSetKeyFile(char_t *keyFile) +{ + a_assert (sslctx); + a_assert (keyFile); + + if (sslctx == NULL) { + return -1; + } + + if (SSL_CTX_use_PrivateKey_file(sslctx, keyFile, SSL_FILETYPE_PEM) <= 0) { + return -1; + } +/* + * Confirm that the certificate and the private key jive. + */ + if (!SSL_CTX_check_private_key(sslctx)) { + return -1; + } + + return 0; +} + +#ifdef SSLC +extern RSA *RSA_new(void); +#endif + +/******************************************************************************/ +/* + * the Temporary RSA callback + */ + +static RSA *websSSLTempRSACallback(SSL *ssl, int isExport, int keyLength) +{ + static RSA *rsaTemp = NULL; + + if (rsaTemp == NULL) { + +#ifdef OPENSSL + rsaTemp = RSA_generate_key(keyLength, RSA_F4, NULL, NULL); +#endif + +#ifdef SSLC + rsaTemp = RSA_new(); +#endif + + } + + return rsaTemp; +} + +/******************************************************************************/ +/* + * Free SSL resources + */ + +int websSSLFree(websSSL_t *wsp) +{ + if (wsp == NULL) { + return -1; + } + +/* + * Make sure we re-use sessions + */ + if (wsp->ssl != NULL) { + SSL_set_shutdown(wsp->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + } + + if (wsp->bio != NULL) { + BIO_free_all(wsp->bio); + } + + bfree(B_L, wsp); + + return 0; +} + +/******************************************************************************/ +/* + * Return Eof for the SSL BIO + */ + +int websSSLEof(websSSL_t *wsp) +{ + a_assert(wsp); + + if ((wsp == NULL) || (wsp->bio == NULL)) { + return -1; + } + + return BIO_eof(wsp->bio); +} + +/******************************************************************************/ +/* + * Perform a read of the SSL BIO + */ + +int websSSLRead(websSSL_t *wsp, char_t *buf, int len) +{ + a_assert(wsp); + a_assert(buf); + + if ((wsp == NULL) || (wsp->bio == NULL)) { + return -1; + } + + return BIO_read(wsp->bio, buf, len); +} + +/******************************************************************************/ +/* + * Perform a gets of the SSL BIO, returning an balloc'ed string + */ + +#define BUF_BLOCK 256 + +int websSSLGets(websSSL_t *wsp, char_t **buf) +{ + int rc, len, lenBuf; + char c; + + a_assert(wsp); + a_assert(buf); + + lenBuf = 0; + len = 0; + + if ((wsp == NULL) || (wsp->bio == NULL)) { + return -1; + } + + while (1) { + + if ((rc = BIO_read(wsp->bio, &c, 1)) < 0) { + return rc; + } + + if (rc == 0) { +/* + * If there is a partial line and we are at EOF, pretend we saw a '\n' + */ + if (len > 0 && BIO_eof(wsp->bio)) { + c = '\n'; + } else { + return -1; + } + } +/* + * If a newline is seen, return the data excluding the new line to the + * caller. If carriage return is seen, just eat it. + */ + if (c == '\n') { + if ((len > 0) && (len < lenBuf)) { + (*buf)[len] = 0; + } + return len; + } else if (c == '\r') { + continue; + } +/* + * Append character to buf + */ + if (len >= lenBuf) { + lenBuf += BUF_BLOCK; + *buf = brealloc(B_L, *buf, lenBuf); + } + + a_assert(*buf); + (*buf)[len] = c; + len++; + } +} + +/******************************************************************************/ +/* + * Perform a write to the SSL BIO + */ + +int websSSLWrite(websSSL_t *wsp, char_t *buf, int len) +{ + a_assert(wsp); + a_assert(buf); + + if ((wsp == NULL) || (wsp->bio == NULL)) { + return -1; + } + + return BIO_write(wsp->bio, buf, len); +} + +/******************************************************************************/ +/* + * Perform a flush of the SSL BIO + */ + +int websSSLFlush(websSSL_t *wsp) +{ + a_assert(wsp); + + if ((wsp == NULL) || (wsp->bio == NULL)) { + return -1; + } + + return BIO_flush(wsp->bio); +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/websSSL.h b/c/src/libnetworking/rtems_webserver/websSSL.h new file mode 100644 index 0000000000..62bcc44930 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/websSSL.h @@ -0,0 +1,67 @@ +/* + * websSSL.h -- SSL Patch header + * + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id$ + */ + +#ifndef _h_websSSL +#define _h_websSSL 1 + +/******************************** Description *********************************/ + +/* + * Header file for the GoAhead Patch for SSL. This defines the interface to + * integrate SSL into the GoAhead Webserver. + */ + +/********************************* Includes ***********************************/ + + +#ifdef OPENSSL +#define SSLEAY /* turn off a few special case MONOLITH macros */ +#define USE_SOCKETS /* needed for the _O_BINARY defs in the MS world */ +#include <openssl/ssl.h> +#else +#include <sslc.h> +#endif + +#ifndef UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + +/********************************** Defines ***********************************/ + +typedef struct { + SSL *ssl; + BIO *bio; +} websSSL_t; + + +/******************************** Prototypes **********************************/ + +extern int websSSLOpen(); +extern int websSSLIsOpen(); +extern void websSSLClose(); + +extern int websSSLWrite(websSSL_t *wsp, char_t *buf, int nChars); +extern int websSSLGets(websSSL_t *wsp, char_t **buf); +extern int websSSLRead(websSSL_t *wsp, char_t *buf, int nChars); +extern int websSSLEof(websSSL_t *wsp); + +extern int websSSLFree(websSSL_t *wsp); +extern int websSSLFlush(websSSL_t *wsp); + +extern int websSSLSetKeyFile(char_t *keyFile); +extern int websSSLSetCertFile(char_t *certFile); + + +#endif /* _h_websSSL */ + +/*****************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/websda.c b/c/src/libnetworking/rtems_webserver/websda.c new file mode 100644 index 0000000000..a4a95775b8 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/websda.c @@ -0,0 +1,244 @@ +/* + * websda.c -- Digest Access Authentication routines + * + * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved. + * + * See the file "license.txt" for usage and redistribution license requirements + * + * $Id$ + */ + +/******************************** Description *********************************/ + +/* + * Routines for generating DAA data. The module uses the + * "RSA Data Security, Inc. MD5 Message-Digest Algorithm" found in md5c.c + */ + +/********************************* Includes ***********************************/ + +#ifndef CE +#include <time.h> +#endif +#include "websda.h" +#include "md5.h" + +/******************************** Local Data **********************************/ + +#define RANDOMKEY T("onceuponatimeinparadise") +#define NONCE_SIZE 34 +#define HASH_SIZE 16 + +/*********************************** Code *************************************/ +/* + * websMD5binary returns the MD5 hash + */ + +char *websMD5binary(unsigned char *buf, int length) +{ + const char *hex = "0123456789abcdef"; + MD5_CONTEXT md5ctx; + unsigned char hash[HASH_SIZE]; + char *r, *strReturn; + char result[(HASH_SIZE * 2) + 1]; + int i; + +/* + * Take the MD5 hash of the string argument. + */ + MD5Init(&md5ctx); + MD5Update(&md5ctx, buf, (unsigned int)length); + MD5Final(hash, &md5ctx); + +/* + * Prepare the resulting hash string + */ + for (i = 0, r = result; i < 16; i++) { + *r++ = hex[hash[i] >> 4]; + *r++ = hex[hash[i] & 0xF]; + } + +/* + * Zero terminate the hash string + */ + *r = '\0'; + +/* + * Allocate a new copy of the hash string + */ + strReturn = balloc(B_L, sizeof(result)); + strcpy(strReturn, result); + + return strReturn; +} + +/*****************************************************************************/ +/* + * Convenience call to websMD5binary + * (Performs char_t to char conversion and back) + */ + +char_t *websMD5(char_t *string) +{ + char_t *strReturn; + + a_assert(string && *string); + + if (string && *string) { + char *strTemp, *strHash; + int nLen; +/* + * Convert input char_t string to char string + */ + nLen = gstrlen(string); + strTemp = ballocUniToAsc(string, nLen + 1); +/* + * Execute the digest calculation + */ + strHash = websMD5binary((unsigned char *)strTemp, nLen); +/* + * Convert the returned char string digest to a char_t string + */ + nLen = strlen(strHash); + strReturn = ballocAscToUni(strHash, nLen); +/* + * Free up the temporary allocated resources + */ + bfree(B_L, strTemp); + bfree(B_L, strHash); + } else { + strReturn = NULL; + } + + return strReturn; +} + +/******************************************************************************/ +/* + * Get a Nonce value for passing along to the client. This function + * composes the string "RANDOMKEY:timestamp:myrealm" and + * calculates the MD5 digest placing it in output. + */ + +char_t *websCalcNonce(webs_t wp) +{ + char_t *nonce, *prenonce; + struct tm *newtime; + time_t longTime; + + a_assert(wp); +/* + * Get time as long integer. + */ + time(&longTime); +/* + * Convert to local time. + */ + newtime = localtime(&longTime); +/* + * Create prenonce string. + */ + prenonce = NULL; +#ifdef DIGEST_ACCESS_SUPPORT + fmtAlloc(&prenonce, 256, T("%s:%s:%s"), RANDOMKEY, gasctime(newtime), + wp->realm); +#else + fmtAlloc(&prenonce, 256, T("%s:%s:%s"), RANDOMKEY, gasctime(newtime), + RANDOMKEY); +#endif + a_assert(prenonce); +/* + * Create the nonce + */ + nonce = websMD5(prenonce); +/* + * Cleanup + */ + bfreeSafe(B_L, prenonce); + + return nonce; +} + +/******************************************************************************/ +/* + * Get an Opaque value for passing along to the client + */ + +char_t *websCalcOpaque(webs_t wp) +{ + char_t *opaque; + a_assert(wp); +/* + * Temporary stub! + */ + opaque = bstrdup(B_L, T("5ccc069c403ebaf9f0171e9517f40e41")); + + return opaque; +} + +/******************************************************************************/ +/* + * Get a Digest value using the MD5 algorithm + */ + +char_t *websCalcDigest(webs_t wp) +{ +#ifdef DIGEST_ACCESS_SUPPORT + char_t *digest, *a1, *a1prime, *a2, *a2prime, *preDigest, *method; + + a_assert(wp); + digest = NULL; + +/* + * Calculate first portion of digest H(A1) + */ + a1 = NULL; + fmtAlloc(&a1, 255, T("%s:%s:%s"), wp->userName, wp->realm, wp->password); + a_assert(a1); + a1prime = websMD5(a1); + bfreeSafe(B_L, a1); +/* + * Calculate second portion of digest H(A2) + */ + method = websGetVar(wp, T("REQUEST_METHOD"), NULL); + a_assert(method); + a2 = NULL; + fmtAlloc(&a2, 255, T("%s:%s"), method, wp->uri); + a_assert(a2); + a2prime = websMD5(a2); + bfreeSafe(B_L, a2); +/* + * Construct final digest KD(H(A1):nonce:H(A2)) + */ + a_assert(a1prime); + a_assert(a2prime); + a_assert(wp->nonce); + + preDigest = NULL; + if (!wp->qop) { + fmtAlloc(&preDigest, 255, T("%s:%s:%s"), a1prime, wp->nonce, a2prime); + } else { + fmtAlloc(&preDigest, 255, T("%s:%s:%s:%s:%s:%s"), + a1prime, + wp->nonce, + wp->nc, + wp->cnonce, + wp->qop, + a2prime); + } + + a_assert(preDigest); + digest = websMD5(preDigest); +/* + * Now clean up + */ + bfreeSafe(B_L, a1prime); + bfreeSafe(B_L, a2prime); + bfreeSafe(B_L, preDigest); + return digest; +#else + return NULL; +#endif /* DIGEST_ACCESS_SUPPORT */ +} + +/******************************************************************************/ diff --git a/c/src/libnetworking/rtems_webserver/websda.h b/c/src/libnetworking/rtems_webserver/websda.h new file mode 100644 index 0000000000..3852ab8f53 --- /dev/null +++ b/c/src/libnetworking/rtems_webserver/websda.h @@ -0,0 +1,41 @@ +/* + * websda.h -- GoAhead Digest Access Authentication public header + * + * Copyright (c) GoAhead Software Inc., 1992-2000. All Rights Reserved. + * + * See the file "license.txt" for information on usage and redistribution + * + * $Id$ + */ + +#ifndef _h_WEBSDA +#define _h_WEBSDA 1 + +/******************************** Description *********************************/ + +/* + * GoAhead Digest Access Authentication header. This defines the Digest + * access authentication public APIs. Include this header for files that + * use DAA functions + */ + +/********************************* Includes ***********************************/ + +#ifndef UEMF + #include "basic/basic.h" + #include "emf/emf.h" +#else + #include "uemf.h" +#endif + +#include "webs.h" + +/****************************** Definitions ***********************************/ + +extern char_t *websCalcNonce(webs_t wp); +extern char_t *websCalcOpaque(webs_t wp); +extern char_t *websCalcDigest(webs_t wp); + +#endif /* _h_WEBSDA */ + +/******************************************************************************/ |