=== local/doc/CHANGES ================================================================== --- local/doc/CHANGES (revision 319) +++ local/doc/CHANGES (local) @@ -75,6 +75,7 @@ Clobber Wait [!] SanitizePath + RedirectStatus The following keywords have been removed:- @@ -230,6 +231,11 @@ paths. Thanks for Peter Pentchev for suggesting this and providing initial patches. + The 302 status text changed from "Moved" to "Found". [RFC2616] + + Mathopd can now return status codes other than 302 for a redirect. + See the RedirectStatus keyword for details. + stub.c: This is a new file that contains code to pass data from and to === local/doc/config.txt ================================================================== --- local/doc/config.txt (revision 319) +++ local/doc/config.txt (local) @@ -733,6 +733,16 @@ error responses. Its value may be displayed by a web browser in a login dialog. +Keyword: RedirectStatus +Where: Control +Type: Integer +Desc: Used in conjunction with the Location redirect syntax, this sets + the status code returned. The default is a "302", but can be set + to anything in the range 3xx. Valid codes are defined by RFC2616 + and should be adhered to. This can be useful, for example, to + prevent the "duplicate content" penalty imposed by some search + engines. + Keyword: Referer Where: LogFormat Desc: The value of the 'Referer:' header sent by the client. Sometimes === local/doc/syntax.txt ================================================================== --- local/doc/syntax.txt (revision 319) +++ local/doc/syntax.txt (local) @@ -90,6 +90,7 @@ "PathInfo" flag "AutoIndexCommand" string "SanitizePath" flag + "RedirectStatus" integer server-item: "Port" integer === local/src/Makefile ================================================================== --- local/src/Makefile (revision 319) +++ local/src/Makefile (local) @@ -33,6 +33,10 @@ # Unomment the following if your system has a working vfork() function # CPPFLAGS += -DHAVE_VFORK +# Uncomment the following to use GnuTLS +# CPPFLAGS += -DUSE_SSL_GNUTLS +# LDFLAGS += -lgnutls + # Currently, sendfile support is available in two flavours: Linux and FreeBSD # Uncomment one of the following two to enable sendfile() support # CPPFLAGS += -DLINUX_SENDFILE === local/src/config.c ================================================================== --- local/src/config.c (revision 319) +++ local/src/config.c (local) @@ -91,14 +91,19 @@ static const char c_buf_size[] = "BufSize"; static const char c_bytes_read[] = "BytesRead"; static const char c_bytes_written[] = "BytesWritten"; +static const char c_ca_cert_file[] = "CACertFile"; +static const char c_cert_file[] = "CertFile"; static const char c_child_log[] = "ChildLog"; static const char c_clients[] = "Clients"; static const char c_clobber[] = "Clobber"; static const char c_content_length[] = "ContentLength"; static const char c_control[] = "Control"; static const char c_core_directory[] = "CoreDirectory"; +static const char c_crl_file[] = "CRLFile"; static const char c_ctime[] = "Ctime"; static const char c_deny[] = "Deny"; +static const char c_dh_bits[] = "DHBits"; +static const char c_dh_params_file[] = "DHParamsFile"; static const char c_encrypted_user_file[] = "EncryptedUserFile"; static const char c_error_log[] = "ErrorLog"; static const char c_error_401_file[] = "Error401File"; @@ -110,6 +115,7 @@ static const char c_extra_headers[] = "ExtraHeaders"; static const char c_host[] = "Host"; static const char c_index_names[] = "IndexNames"; +static const char c_key_file[] = "KeyFile"; static const char c_input_buf_size[] = "InputBufSize"; static const char c_local_address[] = "LocalAddress"; static const char c_local_port[] = "LocalPort"; @@ -132,6 +138,7 @@ static const char c_putenv[] = "PutEnv"; static const char c_query_string[] = "QueryString"; static const char c_realm[] = "Realm"; +static const char c_redirect_status[] = "RedirectStatus"; static const char c_referer[] = "Referer"; static const char c_remote_address[] = "RemoteAddress"; static const char c_remote_port[] = "RemotePort"; @@ -145,6 +152,7 @@ static const char c_server[] = "Server"; static const char c_server_name[] = "ServerName"; static const char c_specials[] = "Specials"; +static const char c_tls_ssl[] = "TLS"; static const char c_status[] = "Status"; static const char c_stay_root[] = "StayRoot"; static const char c_timeout[] = "Timeout"; @@ -163,6 +171,7 @@ static const char e_bad_addr[] = "bad address"; static const char e_bad_alias[] = "alias without matching location"; +static const char e_bad_certfile[] = "tls block missing certfile"; static const char e_bad_mask[] = "mask does not match address"; static const char e_bad_network[] = "bad network"; static const char e_help[] = "unknown error (help)"; @@ -173,6 +182,7 @@ static const char e_noinput[] = "no input"; static const char e_user_invalid[] = "invalid user"; static const char e_user_unknown[] = "user unknown"; +static const char e_outside_range[] = "value outside allowed range"; static const char t_close[] = "unexpected closing brace"; static const char t_eof[] = "unexpected end of file"; @@ -346,6 +356,34 @@ return 0; } +static const char *config_smallint(struct configuration *p, int *i) +{ + unsigned long u; + const char *t; + + t = config_int(p, &u); + if (t) + return t; + if((u < INT_MIN) || (u > INT_MAX)) + return e_outside_range; + *i = u; + return 0; +} + +static const char *config_smallint_in_range(struct configuration *p, int *i, int min, int max) +{ + unsigned long u; + const char *t; + + t = config_int(p, &u); + if (t) + return t; + if((u < min) || (u > max)) + return e_outside_range; + *i = u; + return 0; +} + static const char *config_flag(struct configuration *p, int *i) { const char *t; @@ -671,6 +709,7 @@ a->path_info_ok = b->path_info_ok; a->auto_index_command = b->auto_index_command; a->sanitize_path = b->sanitize_path; + a->redirect_status = b->redirect_status; } else { a->index_names = 0; a->accesses = 0; @@ -694,6 +733,7 @@ a->path_info_ok = 1; a->auto_index_command = 0; a->sanitize_path = 0; + a->redirect_status = 0; } a->next = *as; *as = a; @@ -777,6 +817,8 @@ t = config_string(p, &a->auto_index_command); else if (!strcasecmp(p->tokbuf, c_sanitize_path)) t = config_flag(p, &a->sanitize_path); + else if (!strcasecmp(p->tokbuf, c_redirect_status)) + t = config_smallint_in_range(p, &a->redirect_status, 300, 399); else t = e_keyword; if (t) @@ -848,6 +890,49 @@ return 0; } +#ifdef USE_SSL_GNUTLS +static const char *config_tls_ssl(struct configuration *p, struct server *s) +{ + struct tls_info *tls; + const char *t; + + if ((tls = malloc(sizeof *tls)) == 0) + return e_memory; + tls->ca_cert_file = NULL; + tls->crl_file = NULL; + tls->cert_file = NULL; + tls->key_file = NULL; + tls->dh_bits = 1024; + if ((t = gettoken(p)) != t_open) + return t; + while ((t = gettoken(p)) != t_close) { + if (t != t_string) + return t; + if (!strcasecmp(p->tokbuf, c_ca_cert_file)) + t = config_string(p, &tls->ca_cert_file); + else if (!strcasecmp(p->tokbuf, c_cert_file)) + t = config_string(p, &tls->cert_file); + else if (!strcasecmp(p->tokbuf, c_crl_file)) + t = config_string(p, &tls->crl_file); + else if (!strcasecmp(p->tokbuf, c_dh_bits)) + t = config_smallint(p, &tls->dh_bits); + else if (!strcasecmp(p->tokbuf, c_dh_params_file)) + t = config_string(p, &tls->dh_params_file); + else if (!strcasecmp(p->tokbuf, c_key_file)) + t = config_string(p, &tls->key_file); + else + t = e_keyword; + if (t) + return t; + } + if(tls->cert_file == 0) + return e_bad_certfile; + s->tls_ssl = tls; + require_ssl = 1; + return 0; +} +#endif + static const char *config_server(struct configuration *p, struct server **ss) { struct server *s; @@ -860,7 +945,10 @@ s->children = virtuals; s->vservers = vservers; s->controls = controls; + s->read = (&read_socket); + s->write = (&write_socket); s->backlog = DEFAULT_BACKLOG; + s->tls_ssl = NULL; if ((t = gettoken(p)) != t_open) return t; while ((t = gettoken(p)) != t_close) { @@ -876,6 +964,10 @@ t = config_control(p, &s->controls); else if (!strcasecmp(p->tokbuf, c_backlog)) t = config_int(p, &s->backlog); +#ifdef USE_SSL_GNUTLS + else if (!strcasecmp(p->tokbuf, c_tls_ssl)) + t = config_tls_ssl(p, s); +#endif else t = e_keyword; if (t) === local/src/core.c ================================================================== --- local/src/core.c (revision 319) +++ local/src/core.c (local) @@ -203,6 +203,10 @@ while (s) { if (s->fd != -1) { close(s->fd); +#ifdef USE_SSL_GNUTLS + if(s->tls_ssl) + gnutls_certificate_free_credentials ((s->tls_ssl->credentials)); +#endif s->fd = -1; } s = s->next; @@ -248,18 +252,39 @@ return 0; } + +#ifdef USE_SSL_GNUTLS +int init_gnutls_session(struct server *s, struct connection *cn) +{ + if ((cn->gnutls_session = malloc(sizeof *(cn->gnutls_session))) == 0) { + lerror("malloc: init_gnutls_session"); + log_d("Malloc failed!"); + return -1; + } + gnutls_init (cn->gnutls_session, GNUTLS_SERVER); + gnutls_set_default_priority (*(cn->gnutls_session)); + gnutls_credentials_set (*(cn->gnutls_session), GNUTLS_CRD_CERTIFICATE, s->tls_ssl->credentials); + gnutls_certificate_server_set_request (*(cn->gnutls_session), GNUTLS_CERT_REQUEST); + gnutls_dh_set_prime_bits (*(cn->gnutls_session), s->tls_ssl->dh_bits); + return 0; +} + +#endif + static int accept_connection(struct server *s) { struct sockaddr_in sa_remote, sa_local; socklen_t l; int fd; struct connection *cn; + int ret; do { cn = find_connection(); if (cn == 0) return 0; l = sizeof sa_remote; + fd = accept(s->fd, (struct sockaddr *) &sa_remote, &l); if (fd == -1) switch (errno) { @@ -293,6 +318,30 @@ log_d("clobbering connection to %s[%hu]", inet_ntoa(cn->peer.sin_addr), ntohs(cn->peer.sin_port)); close_connection(cn); } +#ifdef USE_SSL_GNUTLS + if(s->tls_ssl){ + ret = init_gnutls_session(s, cn); + if (ret < 0) { + lerror("init_gnutls_session"); + close(fd); + log_d("Init GnuTLS session failed"); + break; + } + gnutls_transport_set_ptr (*(cn->gnutls_session), (gnutls_transport_ptr_t) fd); + do { + ret = gnutls_handshake(*(cn->gnutls_session)); + if (debug) + log_d("gnutls_handshake returned %02d: %s", ret, gnutls_strerror(ret)); + } while ( (ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)); + if (ret < 0) { + lerror("gnutls_handshake"); + close(fd); + gnutls_deinit(*(cn->gnutls_session)); + log_d("GnuTLS handshake failed: %s", gnutls_strerror(ret)); + break; + } + } +#endif cn->s = s; cn->fd = fd; cn->rfd = -1; @@ -360,6 +409,28 @@ close_connection(cn); } +ssize_t read_socket(struct connection* cn, void * data, size_t sizeofdata) +{ + return read(cn->fd, data, sizeofdata); +} + +ssize_t write_socket(struct connection* cn, const void * data, size_t sizeofdata) +{ + return write(cn->fd, data, sizeofdata); +} + +#ifdef USE_SSL_GNUTLS +ssize_t read_gnutls_socket(struct connection* cn, void * data, size_t sizeofdata) +{ + return gnutls_record_recv(*(cn->gnutls_session), data, sizeofdata); +} + +ssize_t write_gnutls_socket(struct connection* cn, const void * data, size_t sizeofdata) +{ + return gnutls_record_send(*(cn->gnutls_session), data, sizeofdata); +} +#endif + static void write_connection(struct connection *cn) { struct pool *p; @@ -390,7 +461,7 @@ } #endif } - m = write(cn->fd, p->start, n); + m = cn->s->write(cn, p->start, n); if (debug) log_d("write_connection: %d %zd %d %d", cn->fd, p->start - p->floor, n, m); if (m == -1) { @@ -427,7 +498,7 @@ cn->header_input.end -= offset; bytestoread = cn->header_input.ceiling - cn->header_input.end; } - nr = read(cn->fd, cn->header_input.end, bytestoread); + nr = cn->s->read(cn, cn->header_input.end, bytestoread); if (debug) log_d("read_connection: %d %zd %zu %zd", cn->fd, cn->header_input.end - cn->header_input.floor, bytestoread, nr); if (nr == -1) { @@ -926,6 +997,7 @@ } } log_d("*** shutting down"); + close_servers(); } int init_pollfds(size_t n) === local/src/main.c ================================================================== --- local/src/main.c (revision 319) +++ local/src/main.c (local) @@ -69,6 +69,9 @@ int amroot; volatile int my_pid; int nfiles; +#ifdef USE_SSL_GNUTLS +int require_ssl = 0; +#endif static int am_daemon; static char *progname; @@ -105,6 +108,14 @@ { int onoff; struct sockaddr_in sa; +#ifdef USE_SSL_GNUTLS + int ret; + gnutls_datum_t datum; + int dh_params_fd; + char tmpdata[DEFAULT_BUF_SIZE]; + unsigned char * params_start; + unsigned char * params_end; +#endif s->fd = socket(AF_INET, SOCK_STREAM, 0); if (s->fd == -1) @@ -118,6 +129,61 @@ sa.sin_family = AF_INET; sa.sin_addr = s->addr; sa.sin_port = htons(s->port); +#ifdef USE_SSL_GNUTLS + if(s->tls_ssl) { + gnutls_certificate_allocate_credentials (&s->tls_ssl->credentials); + if(s->tls_ssl->ca_cert_file){ + ret = gnutls_certificate_set_x509_trust_file ((s->tls_ssl->credentials), s->tls_ssl->ca_cert_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + die("ca_cert_file", "GnuTLS initialisation failed attempting to read CA Cert File %s: %s", s->tls_ssl->ca_cert_file, gnutls_strerror(ret)); + } + if(s->tls_ssl->crl_file) { + ret = gnutls_certificate_set_x509_crl_file ((s->tls_ssl->credentials), s->tls_ssl->crl_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + die("crl_file", "GnuTLS initialisation failed attempting to read CRL File %s: %s", s->tls_ssl->ca_cert_file, gnutls_strerror(ret)); + } + if(s->tls_ssl->dh_params_file) { + dh_params_fd = open(s->tls_ssl->dh_params_file, O_RDONLY); + if (dh_params_fd < 0) + die("dh_params", "Cannot open DH params file %s\n", s->tls_ssl->dh_params_file); + ret = read(dh_params_fd, tmpdata, DEFAULT_BUF_SIZE-1); + if (ret < 0) + die("dh_params", "GnuTLS initialisation failed attempting to read DH params File %s", s->tls_ssl->ca_cert_file); + params_start = strstr(tmpdata, "-----BEGIN DH PARAMETERS-----"); + if (params_start == NULL) + die("dh_params", "GnuTLS initialisation failed attempting to parse DH params File %s", s->tls_ssl->ca_cert_file); + params_end = strstr(params_start, "-----END DH PARAMETERS-----") ; + if (params_end == NULL) + die("dh_params", "GnuTLS initialisation failed attempting to parse DH params File %s", s->tls_ssl->ca_cert_file); + params_end += sizeof("-----END DH PARAMETERS-----"); + datum.size = (params_end - params_start); + datum.data = params_start; + + gnutls_dh_params_init(&s->tls_ssl->dh_params); + ret = gnutls_dh_params_import_pkcs3 (s->tls_ssl->dh_params, &datum, GNUTLS_X509_FMT_PEM); + if (ret < 0) + die("dh_params", "GnuTLS initialisation failed attempting to import DH params File %s", s->tls_ssl->ca_cert_file); + gnutls_certificate_set_dh_params((s->tls_ssl->credentials), s->tls_ssl->dh_params); + + } else { + gnutls_dh_params_init(&s->tls_ssl->dh_params); + gnutls_dh_params_generate2 (s->tls_ssl->dh_params, s->tls_ssl->dh_bits); + gnutls_certificate_set_dh_params((s->tls_ssl->credentials), s->tls_ssl->dh_params); + } + if(s->tls_ssl->key_file) { + ret = gnutls_certificate_set_x509_key_file ((s->tls_ssl->credentials), s->tls_ssl->cert_file, s->tls_ssl->key_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + die("key_file", "GnuTLS initialisation failed attempting to read Cert File %s (with Key File %s): %s", s->tls_ssl->cert_file, s->tls_ssl->key_file, gnutls_strerror(ret)); + } + else { + ret = gnutls_certificate_set_x509_key_file ((s->tls_ssl->credentials), s->tls_ssl->cert_file, s->tls_ssl->cert_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + die("cert_file", "GnuTLS initialisation failed attempting to read Cert File %s: %s", s->tls_ssl->cert_file, gnutls_strerror(ret)); + } + s->read = (&read_gnutls_socket); + s->write = (&write_gnutls_socket); + } +#endif if (bind(s->fd, (struct sockaddr *) &sa, sizeof sa) == -1) die("bind", "cannot start up server at %s port %lu", inet_ntoa(s->addr), s->port); if (listen(s->fd, s->backlog) == -1) @@ -224,6 +290,10 @@ message = config(config_filename); if (message) die(0, "%s", message); +#ifdef USE_SSL_GNUTLS + if(require_ssl) + gnutls_global_init(); +#endif s = servers; while (s) { startup_server(s); @@ -304,6 +374,10 @@ if (init_buffers() == -1) return 1; httpd_main(); +#ifdef USE_SSL_GNUTLS + if(require_ssl) + gnutls_global_deinit(); +#endif return 0; } === local/src/mathopd.h ================================================================== --- local/src/mathopd.h (revision 319) +++ local/src/mathopd.h (local) @@ -45,6 +45,11 @@ #include #include /* (u)intmax_t */ + +#ifdef USE_SSL_GNUTLS +#include +#endif + #ifndef __GNUC__ #define __attribute__(x) #endif @@ -182,6 +187,7 @@ int path_info_ok; char *auto_index_command; int sanitize_path; + int redirect_status; }; struct virtual { @@ -196,17 +202,6 @@ struct vserver *next; }; -struct server { - int fd; - unsigned long port; - struct in_addr addr; - struct virtual *children; - struct control *controls; - struct server *next; - int pollno; - struct vserver *vservers; - unsigned long backlog; -}; struct request_header { const char *rh_name; @@ -310,8 +305,41 @@ struct pipe_params pipe_params; off_t file_offset; int havefile; +#ifdef USE_SSL_GNUTLS + gnutls_session_t *gnutls_session; +#endif }; +#ifdef USE_SSL_GNUTLS +struct tls_info { + gnutls_certificate_credentials_t credentials; + char *ca_cert_file; + char *cert_file; + char *crl_file; + char *dh_params_file; + char *key_file; + int dh_bits; + gnutls_dh_params_t dh_params; +}; +#endif + +struct server { + int fd; + unsigned long port; + struct in_addr addr; + struct virtual *children; + struct control *controls; + struct server *next; + int pollno; + struct vserver *vservers; + unsigned long backlog; + ssize_t (*read)(struct connection *cn, void * data, size_t sizeofdata ); + ssize_t (*write)(struct connection *cn, const void * data, size_t sizeofdata ); +#ifdef USE_SSL_GNUTLS + struct tls_info *tls_ssl; +#endif +}; + struct connection_list { struct connection *head; struct connection *tail; @@ -357,6 +385,9 @@ extern volatile int my_pid; extern int nfiles; extern pid_t spawn(const char *, char *const[], char *const[], int, int, uid_t, gid_t, const char *); +#ifdef USE_SSL_GNUTLS +extern int require_ssl; +#endif /* config */ @@ -389,6 +420,14 @@ extern int init_pollfds(size_t); extern int init_connections(size_t); +extern ssize_t read_socket(struct connection* cn, void * data, size_t sizeofdata); +extern ssize_t write_socket(struct connection* cn, const void * data, size_t sizeofdata); + +#ifdef USE_SSL_GNUTLS +extern ssize_t read_gnutls_socket(struct connection* cn, void * data, size_t sizeofdata); +extern ssize_t write_gnutls_socket(struct connection* cn, const void * data, size_t sizeofdata); +#endif + /* request */ extern char *rfctime(time_t, char *); === local/src/request.c ================================================================== --- local/src/request.c (revision 319) +++ local/src/request.c (local) @@ -925,7 +925,10 @@ r->location = r->path_translated; if (debug) log_d("redirecting"); - r->status = 302; + if(r->c && r->c->redirect_status) + r->status = r->c->redirect_status; + else + r->status = 302; return 0; } if (get_path_info(r) == -1) { @@ -1409,10 +1412,16 @@ return "204 No Content"; case 206: return "206 Partial Content"; + case 301: + return "301 Moved Permanently"; case 302: - return "302 Moved"; + return "302 Found"; + case 303: + return "303 See Other"; case 304: return "304 Not Modified"; + case 307: + return "307 Temporary Redirect"; case 400: return "400 Bad Request"; case 401: @@ -1496,10 +1505,19 @@ if (pool_print(p, "Content-Range: bytes %ju-%ju/%ju\r\n", r->range_floor, r->range_ceiling, r->range_total) == -1) return -1; break; + case 301: case 302: - if (r->location) - if (pool_print(p, "Location: %s\r\n", r->location) == -1) - return -1; + case 303: + case 307: + if (r->location) { + if (r->args) { + if (pool_print(p, "Location: %s?%s\r\n", r->location, r->args) == -1) + return -1; + } else { + if (pool_print(p, "Location: %s\r\n", r->location) == -1) + return -1; + } + } break; case 401: if (r->c && r->c->realm) @@ -1551,9 +1569,17 @@ if (pool_print(p, "%s\n

%s

\n", status_line, status_line) == -1) return -1; switch (r->status) { + case 301: case 302: - if (pool_print(p, "This document has moved to URL %s.\n", r->location, r->location) == -1) - return -1; + case 303: + case 307: + if (r->args) { + if (pool_print(p, "This document has moved to URL %s?%s.\n", r->location, r->args, r->location, r->args) == -1) + return -1; + } else { + if (pool_print(p, "This document has moved to URL %s.\n", r->location, r->location) == -1) + return -1; + } break; case 401: if (pool_print(p, "You need proper authorization to use this resource.\n") == -1) === local/src/stub.c ================================================================== --- local/src/stub.c (revision 319) +++ local/src/stub.c (local) @@ -384,7 +384,7 @@ log_d("writetoclient: bytestowrite is zero!"); return 0; } - r = write(p->fd, p->output.start, bytestowrite); + r = p->s->write(p, p->output.start, bytestowrite); if (debug) log_d("writetoclient: %d %zd %zu %zd", p->fd, p->output.start - p->output.floor, bytestowrite, r); switch (r) {