Commit 1f7b228c authored by Stewart Brodie's avatar Stewart Brodie
Browse files

* https: support infrastructure has been added. The API calls to the

  Internet module are now vectored through a look up table so that the
  calls for an https request are sent via the AcornSSL module.   The HTTP
  module will only register https: protocol capability with the URL
  module if the AcornSSL module is present, thus allowing transparent
  addition of SSL support without requiring an update for the HTTP
  module.

* There is no encryption code in the HTTP module at all.  It is all
  confined to the AcornSSL module.
parent 5ef992fa
......@@ -43,12 +43,13 @@
#include "generic.h"
#include "connect.h"
int make_sock_nonblocking(int fd)
static int make_sock_nonblocking(Session *ses, int fd)
{
int one = 1;
int (*s_ioctl)(int, unsigned long, ...) = ses->op->s_ioctl;
if (socketioctl(fd, FIONBIO, &one) < 0) return 0;
if (socketioctl(fd, FIOASYNC, &one) < 0) return 0; /* Causes Internet events */
if (s_ioctl(fd, FIONBIO, &one) < 0) return 0;
if (s_ioctl(fd, FIOASYNC, &one) < 0) return 0; /* Causes Internet events */
return 1;
}
......@@ -77,7 +78,7 @@ int make_sock_nonblocking(int fd)
/* an unknown port and unknown protocol. This is normal and correct. */
/************************************************************************/
int opensock(char *name, int port, char *sername, int *state, int fd)
int opensock(char *name, int port, char *sername, int *state, int fd, Session *ses)
{
static int can_nonblock = 1;
......@@ -92,10 +93,17 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
}
else {
struct servent *service = NULL;
if (strcmp(sername, CONNECT_DEFAULT_PROTOCOL_NAME) != 0) {
short port = CONNECT_DEFAULT_PORT_NUMBER;
if (strcmp(sername, CONNECT_DEFAULT_PROTOCOL_NAME) == 0) {
port = CONNECT_DEFAULT_PORT_NUMBER;
}
else if (strcmp(sername, CONNECT_DEFAULT_PROTOCOL_NAME2) == 0) {
port = CONNECT_DEFAULT_PORT_NUMBER2;
}
else {
service = getservbyname(sername, "tcp");
}
addr.sin_port = service ? service->s_port : htons(CONNECT_DEFAULT_PORT_NUMBER);
addr.sin_port = service ? service->s_port : htons(port);
}
/* Jump back here if we start a nonblocking connect and then discover
......@@ -116,7 +124,7 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
#ifdef TRACE
protocol_debug("Creating socket ...\n");
#endif
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
if ((fd = ses->op->s_creat(PF_INET, SOCK_STREAM, 0)) < 0) {
#ifdef TRACE
protocol_debug(".. error = %d\n", errno);
#endif
......@@ -128,7 +136,7 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
#endif
if (can_nonblock) {
if (!make_sock_nonblocking(fd)) can_nonblock=0;
if (!make_sock_nonblocking(ses, fd)) can_nonblock=0;
#ifdef TML
Printf("opensock> nonblock=%x (errno=%d)\n",can_nonblock,errno);
#endif
......@@ -147,7 +155,7 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
#ifdef TRACE
protocol_debug("dns_find_ip_address(%s) failed (%d)\n", name, status);
#endif
close_socket(&fd);
close_socket(ses, &fd);
*state = CONNECT_CONNECTED_STATE;
return -status;
}
......@@ -160,7 +168,7 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
/* Set the state */
*state = CONNECT_CONNECTED_STATE;
status = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
status = ses->op->s_connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (status == 0) {
/* The connection completed successfully. */
......@@ -187,7 +195,7 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
#ifdef TRACE
protocol_debug("EAGAIN/WOULDBLOCK closing %d\n", fd);
#endif
close_socket(&fd);
close_socket(ses, &fd);
can_nonblock = 0;
goto retry; /* try again */
}
......@@ -199,6 +207,6 @@ int opensock(char *name, int port, char *sername, int *state, int fd)
status, errno);
#endif
status = errno;
close_socket(&fd);
close_socket(ses, &fd);
return -status;
}
......@@ -32,6 +32,8 @@
#include "netinet/in.h"
#include "generic.h"
#ifdef POOLING_ENABLED
/* Maximum number of connections to hold open at once */
#define CONNPOOL_SIZE 8
......@@ -69,7 +71,7 @@ static void connpool_delete(connpool *p)
DEBUG_ENTRY(p);
#endif
if (p->sock > -1) {
close_socket(&p->sock);
close_socket(NULL, &p->sock);
}
if (p->host) {
free(p->host);
......@@ -219,3 +221,9 @@ int connpool_find_http(int *s, char *host, int port, int *protocol)
#endif
return 0;
}
#else /* POOLING_ENABLED */
void connpool_init(void) { }
void connpool_exit(void) { }
void connpool_poll(void) { }
#endif /* POOLING_ENABLED */
......@@ -614,6 +614,17 @@ static char *cookie_look_for_cookies(char *domain, char *path, Session *ses)
/* Only send if the cookie has not already expired */
if (cookie->expires < current_time) continue;
if (cookie->secure && !(ses->flags & flags_USING_HTTPS)) {
#ifdef TRACE
cookie_debug("Secure cookie `%s' requires secure comms channel\n",
cookie->name);
#endif
if (!getenv("AcornHTTP$Secure")) continue;
#ifdef TRACE
cookie_debug("Allowing cookie anyway (AcornHTTP$Secure was set)\n");
#endif
}
size = strlen(cookie->name) + strlen(cookie->value) + sizeof("; = ");
if (num_cookies > 0) size += sizeof("; ");
if (cookie->version > 0) {
......
......@@ -24,8 +24,9 @@
#include "sys/socket.h"
#include "sys/errno.h"
#include "socklib.h"
#include "generic.h"
#include "module.h"
#include "generic.h"
#include "security.h"
/* Function to compare two strings case insensitively
......@@ -120,7 +121,7 @@ char *Strndup(const char *s1, size_t size)
return memcpy(s2,s1,size);
}
int close_socket(int *psd)
int close_socket(Session *ses, int *psd)
{
const int sd = *psd;
if (sd == -1) {
......@@ -128,8 +129,15 @@ int close_socket(int *psd)
return -1;
}
*psd = -1;
(void) shutdown(sd, 2);
return socketclose(sd);
if (ses == NULL) {
const sock_vtbl *vtbl = security_get_normal_vtable();
vtbl->s_shutdown(sd, 2);
return vtbl->s_close(sd);
}
else {
ses->op->s_shutdown(sd, 2);
return ses->op->s_close(sd);
}
}
......
......@@ -34,6 +34,7 @@
#include "utils.h"
#include "dns.h"
#include "config.h"
#include "security.h"
volatile static int callback_pending_flag = 0;
......@@ -52,6 +53,10 @@ static _kernel_oserror *__ROM;
#define URL_PROTOCOL_REGISTER (0x83e20)
#define URL_PROTOCOL_DEREGISTER (0x83e21)
#define URL_00 (0x83e00)
#define URL_SSL_AVAILABLE (0x83e02)
#ifndef ROM
extern int messages_file(void);
#endif
......@@ -81,7 +86,8 @@ typedef enum {
HTTP_METHOD_UNSUPPORTED = HTTP_ERROR_NUM + 6, /* Unknown method in R2 */
HTTP_METHOD_INIT_ERR = HTTP_ERROR_NUM + 7, /* Failed to start a fetch completely */
HTTP_BAD_URL_PARSE = HTTP_ERROR_NUM + 8, /* Unable to parse URL (URL module too old?) */
HTTP_PROXY_NOT_FOUND = HTTP_ERROR_NUM + 9 /* Unable to contact the proxy */
HTTP_PROXY_NOT_FOUND = HTTP_ERROR_NUM + 9, /* Unable to contact the proxy */
HTTP_NO_SECURITY = HTTP_ERROR_NUM + 10 /* No SSL support present */
} http_internal_error_codes;
......@@ -95,28 +101,64 @@ static void module_check_os_version(void)
clibrary_realloc_routine_is_buggy = (os <= 0xA4);
}
static int registered;
static int registered_http;
static int registered_https;
static _kernel_oserror *try_to_register_for_https(void)
{
if (!registered_https) {
int ver = security_ssl_available();
if (ver > 0) {
_kernel_oserror *e;
e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0, HTTP_SecureGetData, "https:",
ver, Module_Help " (SSL) Acorn 1998 (Built: "
Module_Date ")");
if (e == NULL) {
registered_https = 1;
}
}
}
return NULL;
}
static _kernel_oserror *try_to_register(void)
{
_kernel_oserror *e;
_kernel_oserror *e = NULL;
if (registered) return NULL;
e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0, HTTP_00, "http:",
Module_VersionNumber, Module_Help " Acorn 1997-8 (Built: " Module_Date ")");
if (e == NULL) {
registered = 1;
if (!registered_http) {
e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0, HTTP_00, "http:",
Module_VersionNumber, Module_Help " Acorn 1997-8 (Built: " Module_Date ")");
if (e != NULL) return e;
registered_http = 1;
}
if (registered_http) {
(void) try_to_register_for_https();
}
return e;
}
static _kernel_oserror *try_to_deregister_for_https(void)
{
if (!registered_https) {
return NULL;
}
else {
registered_https = 0;
return _swix(URL_PROTOCOL_DEREGISTER, _INR(0,1), 0, HTTP_SecureGetData);
}
}
static _kernel_oserror *try_to_deregister(void)
{
if (!registered) {
(void) try_to_deregister_for_https();
if (!registered_http) {
return NULL;
}
else {
registered = 0;
registered_http = 0;
return _swix(URL_PROTOCOL_DEREGISTER, _INR(0,1), 0, HTTP_00);
}
}
......@@ -152,8 +194,10 @@ _kernel_oserror *module_init(CMHG_CONST char *cmd_tail, int podule_base, void *p
__ROM = _swix(0xa2c43, _IN(0), pw);
#endif
registered = 0;
registered_http = 0;
registered_https = 0;
module_check_os_version();
security_init();
config_init();
session_init();
hosttrack_init();
......@@ -209,17 +253,35 @@ void service_handler(int service_number, _kernel_swi_regs *r, void *pw)
protocol_debug("Service &%X: R0 = %d for URL version %03d\n", service_number, r->r[0], r->r[2]);
#endif
(void) pw;
(void) service_number;
switch (r->r[0]) {
case 0:
(void) try_to_register();
break;
case 1:
(void) try_to_deregister();
break;
default:
break;
if (service_number == URL_SSL_AVAILABLE) {
switch (r->r[0]) {
case 0:
if (security_ssl_arrived(r->r[2])) {
(void) try_to_register();
}
break;
case 1:
(void) security_ssl_gone();
if (registered_https) {
try_to_deregister_for_https();
}
break;
default:
break;
}
}
else {
switch (r->r[0]) {
case 0:
(void) try_to_register();
break;
case 1:
(void) try_to_deregister();
break;
default:
break;
}
}
}
......@@ -290,6 +352,18 @@ static _kernel_oserror *swi_handler_2(int swi_offset, _kernel_swi_regs *r, void
case HTTP_Stop - HTTP_00:
return(http_stop(r));
/* Generic security protocol module SWIs */
case HTTP_SecureGetData - HTTP_00:
return(https_start(r));
case HTTP_SecureStatus - HTTP_00:
return(https_status(r));
case HTTP_SecureReadData - HTTP_00:
return(https_readdata(r));
case HTTP_SecureStop - HTTP_00:
return(https_stop(r));
/* HTTP specific SWIs (at the upper end of the SWI range */
#ifdef COOKIE
......@@ -315,6 +389,7 @@ _kernel_oserror *swi_handler(int swi_offset, _kernel_swi_regs *r, void *pw)
static const char *swi[] = {"GetData", "Status", "ReadData", "Stop"};
protocol_debug(">>Handling SWI &%02X (HTTP_%s) for R1=%08x\n", swi_offset,
swi_offset < (sizeof(swi)/sizeof(char*)) ? swi[swi_offset] :
(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? swi[swi_offset-16] :
swi_offset == HTTP_EnumerateCookies ? "EnumerateCookies" :
swi_offset == HTTP_ConsumeCookie ? "ConsumeCookie" :
swi_offset == HTTP_AddCookie ? "AddCookie" : "UNKNOWN!",
......@@ -325,6 +400,7 @@ _kernel_oserror *swi_handler(int swi_offset, _kernel_swi_regs *r, void *pw)
protocol_debug("<<Exiting SWI &%02X (HTTP_%s) for R1=%08x %s "
"R0=%s; R2=%d (=&%p); R3=%d; R4=%d; R5=%d\n\n", swi_offset,
swi_offset < (sizeof(swi)/sizeof(char*)) ? swi[swi_offset] :
(swi_offset >= 16 && (swi_offset-16) < (sizeof(swi)/sizeof(char*))) ? swi[swi_offset-16] :
swi_offset == HTTP_EnumerateCookies ? "EnumerateCookies" :
swi_offset == HTTP_ConsumeCookie ? "ConsumeCookie" :
swi_offset == HTTP_AddCookie ? "AddCookie" : "UNKNOWN!",
......@@ -388,6 +464,9 @@ _kernel_oserror *return_error(int status_code)
case HTTP_ERROR_NO_PROXY:
return make_error(HTTP_PROXY_NOT_FOUND,0);
case HTTP_ERROR_NO_SECURITY:
return make_error(HTTP_NO_SECURITY,0);
default:
return make_error(HTTP_GENERAL_ERROR,0);
}
......
......@@ -36,6 +36,11 @@ static void http_set_return(_kernel_swi_regs *r, int flags, int size, int left,
static void http_continue_dns(_kernel_swi_regs *r, Session *ses);
static _kernel_oserror *http_exec_readdata(_kernel_swi_regs *r);
_kernel_oserror *https_readdata(_kernel_swi_regs *r)
{
return http_readdata(r);
}
/*************************************************************/
/* _kernel_oserror *http_readdata(_kernel_swi_regs *r) */
/*************************************************************/
......@@ -117,7 +122,7 @@ static int http_read_more_header(_kernel_swi_regs *r, Session *ses)
*/
ses->bufptr = 0;
space = ses->bufsize;
dataread = recv(ses->sd, ses->buffer, space, MSG_PEEK);
dataread = ses->op->s_recv(ses->sd, ses->buffer, space, MSG_PEEK);
/* Right. We only need to cope with this situation as the later code
* for the body reader will pick up the same situation when it attempts
......@@ -145,7 +150,7 @@ static int http_read_more_header(_kernel_swi_regs *r, Session *ses)
protocol_debug("Consuming %d - only had %d to discard in first place!\n", consumed, dataread);
}
#endif
(void) recv(ses->sd, ses->buffer, consumed, 0);
(void) ses->op->s_recv(ses->sd, ses->buffer, consumed, 0);
#ifdef TRACE
protocol_debug("receiving_header: discarding data from socket (size=%d):\n", consumed);
protocol_dump(ses->buffer, consumed);
......@@ -280,7 +285,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
*/
}
dataread = recv(ses->sd, buffer, bufsize, 0);
dataread = ses->op->s_recv(ses->sd, buffer, bufsize, 0);
#ifdef TRACE
protocol_debug("receiving_body state: recv returns %d (errno now %d)\n", dataread, errno);
......@@ -311,7 +316,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
#ifdef TRACE
protocol_debug("socketclose(%d) in body recv state\n", ses->sd);
#endif
close_socket(&ses->sd);
close_socket(ses, &ses->sd);
}
}
else if ((errno == 0) || (errno==EWOULDBLOCK) || (errno==EINPROGRESS)) {
......@@ -324,7 +329,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
#ifdef TRACE
protocol_debug("socketclose(%d) in error read state\n", ses->sd);
#endif
close_socket(&ses->sd);
close_socket(ses, &ses->sd);
}
}
}
......@@ -410,7 +415,7 @@ static _kernel_oserror *http_exec_readdata(_kernel_swi_regs *r)
#ifdef TRACE
protocol_debug("socketclose(%d) in received/done state\n", ses->sd);
#endif
close_socket(&ses->sd);
close_socket(ses, &ses->sd);
}
break;
......@@ -478,7 +483,7 @@ static void http_continue_dns(_kernel_swi_regs *r, Session *ses)
r->r[4] = 0;
r->r[5] = -1;
ses->sd = opensock(ses->host, ses->port, NULL, &ses->done, ses->sd);
ses->sd = opensock(ses->host, ses->port, NULL, &ses->done, ses->sd, ses);
/* We failed to create the connecting socket! */
if (ses->sd < 0) {
if ((ses->sd == -ENETUNREACH) || (ses->sd == -EWOULDBLOCK) || (ses->sd == -EHOSTUNREACH)) {
......
/* Copyright 1998 Acorn Computers Ltd
*
* 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.
*/
/*
* HTTP (c.config)
*
* Acorn Computers Ltd. 1998
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "kernel.h"
#include "swis.h"
#include "sys/errno.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "module.h"
#include "socklib.h"
#include "security.h"
static int ssl_module_present;
static int sec_socketioctl(int, unsigned long, ...);
static int sec_socket(int domain, int type, int protocol);
static int sec_connect(int s, const struct sockaddr *name, int namelen);
static int sec_shutdown(int s, int how);
static int sec_socketclose(int d);
static int sec_getsockopt(int s, int level, int optname,
void *optval, int *optlen);
static int sec_socketwrite(int s, const void *buf, unsigned int len);
static int sec_recv(int s, void *data, size_t size, int flags);
int security_ssl_available(void)
{
return ssl_module_present;
}
void security_init(void)
{
ssl_module_present = 0;
}
int security_ssl_arrived(int ver)
{
ssl_module_present = ver;
return ssl_module_present;
}
int security_ssl_gone(void)
{
ssl_module_present = 0;
return ssl_module_present;
}
const sock_vtbl *security_get_ssl_vtable(void)
{
static const sock_vtbl vtable = {
sec_socketioctl,
sec_socket,
sec_connect,
sec_shutdown,
sec_socketclose,
sec_getsockopt,
sec_socketwrite,
sec_recv
};
return &vtable;
}
const sock_vtbl *security_get_normal_vtable(void)
{
static const sock_vtbl vtable = {
socketioctl,
socket,
connect,
shutdown,
socketclose,
getsockopt,
socketwrite,
recv
};
return &vtable;
}
/* ---- implementation veneers to the secure socket stuff */
#ifdef FAKE_SSL_MODULE
/* Dummy implementation */
static int sec_socketioctl(int s, unsigned long op, ...)
{
void *data;
va_list ap;
va_start(ap, op);
data = va_arg(ap, void *);
va_end(ap);
return socketioctl(s, op, data);
}
static int sec_socket(int domain, int type, int protocol)
{
return socket(domain, type, protocol);
}
static int sec_connect(int s, const struct sockaddr *name, int namelen)
{
return connect(s, name, namelen);
}
static int sec_shutdown(int s, int how)
{
return shutdown(s, how);
}
static int sec_socketclose(int d)
{
return socketclose(d);
}
static int sec_getsockopt(int s, int level, int optname,
void *optval, int *optlen)
{
return getsockopt(s, level, optname, optval, optlen);
}
static int sec_socketwrite(int s, const void *buf, unsigned int len)
{
return socketwrite(s, buf, len);
}
static int sec_recv(int s, void *data, size_t size, int flags)
{
return recv(s, data, size, flags);
}
#else
#include "^.SSL.h.SSLHdr"
static int sec_socketioctl(int s, unsigned long op, ...)
{
_kernel_oserror *e;
int result;
void *data;
va_list ap;
va_start(ap, op);
data = va_arg(ap, void *);
va_end(ap);
e = _swix(HTTPS_Ioctl, _INR(0,2)|_OUT(0),
s, op, data, &result);
if (!e) return result;
errno = e->errnum;
return -1;
}
static int sec_socket(int domain, int type, int protocol)
{
_kernel_oserror *e;
int result;
e = _swix(HTTPS_Socket, _INR(0,2)|_OUT(0),