Roxen.git / server / base_server / roxen.pike

version» Context lines:

Roxen.git/server/base_server/roxen.pike:1:   // This file is part of Roxen WebServer.   // Copyright © 1996 - 2004, Roxen IS.   //   // The Roxen WebServer main program.   //   // Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others.   // ABS and suicide systems contributed freely by Francesco Chemolli    - constant cvs_version="$Id: roxen.pike,v 1.901 2005/05/23 09:47:15 anders Exp $"; + constant cvs_version="$Id: roxen.pike,v 1.902 2005/05/25 18:52:46 mast Exp $";      //! @appears roxen   //!   //! The Roxen WebServer main program.      // The argument cache. Used by the image cache.   ArgCache argcache;      // Some headerfiles   #define IN_ROXEN
Roxen.git/server/base_server/roxen.pike:45:      //<locale-token project="roxen_start"> LOC_S </locale-token>   //<locale-token project="roxen_message"> LOC_M </locale-token>   #define LOC_S(X,Y) _STR_LOCALE("roxen_start",X,Y)   #define LOC_M(X,Y) _STR_LOCALE("roxen_message",X,Y)   #define CALL_M(X,Y) _LOCALE_FUN("roxen_message",X,Y)      // --- Debug defines ---      #ifdef SSL3_DEBUG - # define SSL3_WERR(X) report_debug("SSL3: "+X+"\n") + # define SSL3_WERR(X) report_debug("TLS port %s: %s\n", get_url(), X)   #else   # define SSL3_WERR(X)   #endif      #ifdef THREAD_DEBUG   # define THREAD_WERR(X) report_debug("Thread: "+X+"\n")   #else   # define THREAD_WERR(X)   #endif   
Roxen.git/server/base_server/roxen.pike:1373:    {   // if(!urls[name]) // only unref once   // return;       m_delete(conf_data, urls[_name]->conf);    m_delete(urls, _name);    if (!path && sizeof (Array.uniq (values (urls)->path)) == 1)    path = values (urls)[0]->path;    sorted_urls -= ({_name});   #ifdef PORT_DEBUG -  report_debug("Protocol(%s://%s:%d/)->unref(%O): refs:%d\n", -  name, ip, port, _name, refs); +  report_debug("Protocol(%s)->unref(%O): refs:%d\n", +  get_url(), _name, refs);   #endif /* PORT_DEBUG */    if( !--refs ) {    if (retries) {    remove_call_out(bind);    }    if (port_obj) {    destruct(port_obj);    }    port_obj = 0;    m_delete(open_ports[name][ip], port);
Roxen.git/server/base_server/roxen.pike:1541:       mixed query_option( string x )    //! Query the port-option 'x' for this port.    {    return query( x );    }       string get_key()    //! Return the key used for this port (protocol:ip:portno)    { +  if (ip == "::") +  return name + ":0:" + port; +  else    return name+":"+ip+":"+port;    }    -  +  string get_url() +  //! Return the port on URL form. +  { +  return (string) name + "://" + +  (!ip ? "*" : has_value (ip, ":") ? "[" + ip + "]" : ip) + +  ":" + port + "/"; +  } +     void save()    //! Save all port options    {    set_port_options( get_key(),    mkmapping( indices(variables),    map(indices(variables),query)));    }       void restore()    //! Restore all port options from saved values    {    foreach( (array)get_port_options( get_key() ), array kv )    set( kv[0], kv[1] );    }       static int retries;    static void bind()    {    if (bound) return;    if (!port_obj) port_obj = Stdio.Port(); -  Privs privs = Privs (sprintf ("Binding %s://%s:%d/", -  (string) name, ip || "*", (int) port)); +  Privs privs = Privs (sprintf ("Binding %s", get_url()));    if (port_obj->bind(port, got_connection, ip))    {    privs = 0;    bound = 1;    return;    }    privs = 0;   #if constant(System.EAFNOSUPPORT)    if (port_obj->errno() == System.EAFNOSUPPORT) {    // Fail permanently.    error("Invalid address " + ip);    }   #endif /* System.EAFNOSUPPORT */ -  report_error(LOC_M(6, "Failed to bind %s://%s:%d/ (%s)")+"\n", -  (string)name, (ip||"*"), (int)port, -  strerror(port_obj->errno())); +  report_error(LOC_M(6, "Failed to bind %s (%s)")+"\n", +  get_url(), strerror(port_obj->errno())); + #if 0    werror (describe_backtrace (backtrace())); -  + #endif   #if constant(System.EADDRINUSE) || constant(system.EADDRINUSE)    if (   #if constant(System.EADDRINUSE)    (port_obj->errno() == System.EADDRINUSE) &&   #else /* !constant(System.EADDRINUSE) */    (port_obj->errno() == system.EADDRINUSE) &&   #endif /* constant(System.EADDRINUSE) */    (retries++ < 10)) {    // We may get spurious failures on rebinding ports on some OS'es    // (eg Linux, WIN32). See [bug 3031].
Roxen.git/server/base_server/roxen.pike:1708:    * work addresses as 128.net.host.    */    bytes = sprintf("%1c%1c%2c", @segments);    break;    }    if (bytes == "\0\0\0\0") return 0; // ANY.    return sprintf("%d.%d.%d.%d", @((array(int))bytes));    }    }    -  static void create( int pn, string i ) -  //! Constructor. Bind to the port 'pn' ip 'i' +  static void setup (int pn, string i)    {    port = pn;    ip = canonical_ip(i);       restore();    if( file_stat( "../local/"+requesthandlerfile ) )    rrhf = "../local/"+requesthandlerfile;    else    rrhf = requesthandlerfile;    DDUMP( rrhf );   #ifdef DEBUG    if( !requesthandler )    requesthandler = (program)(rrhf);   #endif    bound = 0;    port_obj = 0;    retries = 0; -  +  } +  +  static void create( int pn, string i ) +  //! Constructor. Bind to the port 'pn' ip 'i' +  { +  setup (pn, i);    bind();    }       static string _sprintf( )    { -  return "Protocol("+name+"://"+ip+":"+port+")"; +  return "Protocol(" + get_url() + ")";    }   }      #if constant(SSL.sslfile)   class SSLProtocol   //! Base protocol for SSL ports. Exactly like Port, but uses SSL.   {    inherit Protocol;       // SSL context    SSL.context ctx = SSL.context();    -  +  int cert_failure; +  +  static void cert_err_unbind() +  { +  if (bound) { +  port_obj->close(); +  report_warning ("TLS port %s closed.\n", get_url()); +  bound = 0; +  } +  } +  + #define CERT_WARNING(VAR, MSG, ARGS...) do { \ +  string msg = (MSG); \ +  array args = ({ARGS}); \ +  if (sizeof (args)) msg = sprintf (msg, @args); \ +  report_warning ("TLS port %s: %s", get_url(), msg); \ +  (VAR)->add_warning (msg); \ +  } while (0) +  + #define CERT_ERROR(VAR, MSG, ARGS...) do { \ +  string msg = (MSG); \ +  array args = ({ARGS}); \ +  if (sizeof (args)) msg = sprintf (msg, @args); \ +  report_error ("TLS port %s: %s", get_url(), msg); \ +  (VAR)->add_warning (msg); \ +  cert_err_unbind(); \ +  cert_failure = 1; \ +  return; \ +  } while (0) +     void certificates_changed(Variable|void ignored)    { -  +  int old_cert_failure = cert_failure; +     string raw_keydata;    array(string) certificates = ({}); -  +  array(object) decoded_certs = ({});    Variable Certificates = getvar("ssl_cert_file");       object privs = Privs("Reading cert file");       foreach(map(Certificates->query(), String.trim_whites), string cert_file) {    string raw_cert; -  +  SSL3_WERR (sprintf ("Reading cert file %O", cert_file));    if( catch{ raw_cert = lopen(cert_file, "r")->read(); } )    { -  Certificates->add_warning(sprintf(LOC_M(8,"SSL3: Reading cert-file '%s' failed!"), -  cert_file)); +  CERT_WARNING (Certificates, +  LOC_M(8, "Reading certificate file %O failed: %s\n"), +  cert_file, strerror (errno()));    continue;    }       object msg = Tools.PEM.pem_msg()->init( raw_cert );    object part = msg->parts["CERTIFICATE"] ||    msg->parts["X509 CERTIFICATE"];    string cert;       if (msg->parts["RSA PRIVATE KEY"] ||    msg->parts["DSA PRIVATE KEY"]) {    raw_keydata = raw_cert;    }       if (!part || !(cert = part->decoded_body()))    { -  Certificates->add_warning(LOC_M(10, "SSL3: No certificate found.")+"\n"); +  CERT_WARNING (Certificates, +  LOC_M(10, "No certificate found in %O.\n"), +  cert_file);    continue;    }    certificates += ({ cert }); -  +  +  // FIXME: Support PKCS7 +  object tbs = Tools.X509.decode_certificate (cert); +  if (!tbs) { +  CERT_WARNING (Certificates, +  LOC_M(13, "Certificate not valid (DER).\n")); +  continue;    } -  +  decoded_certs += ({tbs}); +  }    -  Variable KeyFile = getvar("ssl_key_file"); -  -  if( strlen(KeyFile->query()) && -  catch{ raw_keydata = lopen(KeyFile->query(), "r")->read(); } ) -  { -  KeyFile->add_warning(sprintf(LOC_M(9, "SSL3: Reading key-file '%s' failed!")+"\n", -  query_option("ssl_key_file"))); +  if (!sizeof(decoded_certs)) { +  report_error ("TLS port %s: %s", get_url(), +  LOC_M (0,"No certificates found.\n")); +  cert_err_unbind(); +  cert_failure = 1;    return;    }    -  privs = 0; +  Variable KeyFile = getvar("ssl_key_file");    -  if (!raw_keydata) { -  Certificates->add_warning(LOC_M(17,"SSL3: No private key found.")); -  return; +  if( strlen(KeyFile->query())) { +  SSL3_WERR (sprintf ("Reading key file %O", KeyFile->query())); +  if (catch{ raw_keydata = lopen(KeyFile->query(), "r")->read(); } ) +  CERT_ERROR (KeyFile, +  LOC_M(9, "Reading key file %O failed: %s\n"), +  KeyFile->query(), strerror (errno()));    } -  +  else +  KeyFile = Certificates;    -  if (!sizeof(certificates)) return; +  privs = 0;    -  +  if (!raw_keydata) +  CERT_ERROR (KeyFile, LOC_M (17,"No private key found.\n")); +     object msg = Tools.PEM.pem_msg()->init( raw_keydata );       SSL3_WERR(sprintf("key file contains: %O", indices(msg->parts)));       object part;    if (part = msg->parts["RSA PRIVATE KEY"])    {    string key;       if (!(key = part->decoded_body())) -  { -  KeyFile->add_warning(LOC_M(11,"SSL3: Private rsa key not valid")+" (PEM).\n"); -  return; -  } +  CERT_ERROR (KeyFile, +  LOC_M(11,"Private rsa key not valid")+" (PEM).\n");       object rsa = Standards.PKCS.RSA.parse_private_key(key);    if (!rsa) -  { -  KeyFile->add_warning(LOC_M(11, "SSL3: Private rsa key not valid")+" (DER).\n"); -  return; -  } +  CERT_ERROR (KeyFile, +  LOC_M(11,"Private rsa key not valid")+" (DER).\n");       ctx->rsa = rsa;       SSL3_WERR(sprintf("RSA key size: %d bits", rsa->rsa_size()));       if (rsa->rsa_size() > 512)    {    /* Too large for export */   #if constant(Crypto.RSA)    ctx->short_rsa = Crypto.RSA()->generate_key(512, ctx->random);   #else    ctx->short_rsa = Crypto.rsa()->generate_key(512, ctx->random);   #endif       // ctx->long_rsa = Crypto.rsa()->generate_key(rsa->rsa_size(), ctx->random);    }    ctx->rsa_mode();       array(int) key_matches = -  map(certificates, -  lambda(string cert, Variable Certificates) { -  // FIXME: Support PKCS7 -  object tbs = Tools.X509.decode_certificate (cert); -  if (!tbs) -  { -  Certificates->add_warning(LOC_M(13,"SSL3: Certificate not valid (DER).")); -  return 0; -  } +  map(decoded_certs, +  lambda (object tbs) {    return tbs->public_key->rsa->public_key_equal (rsa); -  }, Certificates); +  });       int num_key_matches;    // DWIM: Make sure the main cert comes first.    array(string) new_certificates = allocate(sizeof(certificates));    int i,j;    for (i=0; i < sizeof(certificates); i++) {    if (key_matches[i]) {    new_certificates[j++] = certificates[i];    num_key_matches++;    }    }    for (i=0; i < sizeof(certificates); i++) {    if (!key_matches[i]) {    new_certificates[j++] = certificates[i];    }    }    if( !num_key_matches ) -  { -  KeyFile->add_warning(LOC_M(14, "SSL3: Certificate and private key " -  "do not match.")); -  return; -  } +  CERT_ERROR (KeyFile, +  LOC_M(14, "Certificate and private key do not match.\n"));    ctx->certificates = new_certificates;    }    else if (part = msg->parts["DSA PRIVATE KEY"])    {    string key;       if (!(key = part->decoded_body())) -  { -  report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (PEM).\n"); -  return; -  } +  CERT_ERROR (KeyFile, +  LOC_M(15,"Private dsa key not valid")+" (PEM).\n");       object dsa = Standards.PKCS.DSA.parse_private_key(key);    if (!dsa) -  { -  report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (DER).\n"); -  return; -  } +  CERT_ERROR (KeyFile, +  LOC_M(15,"Private dsa key not valid")+" (DER).\n");       SSL3_WERR(sprintf("Using DSA key."));       //dsa->use_random(ctx->random);    ctx->dsa = dsa;    /* Use default DH parameters */   #if constant(SSL.Cipher)    ctx->dh_params = SSL.Cipher.DHParameters();   #else    ctx->dh_params = SSL.cipher()->dh_parameters();   #endif       ctx->dhe_dss_mode();       // FIXME: Add cert <-> private key check.       ctx->certificates = certificates;    }    else -  { -  KeyFile->add_warning(LOC_M(17,"SSL3: No private key found.")); -  return; -  } +  CERT_ERROR (KeyFile, LOC_M(17,"No private key found.\n"));      #if EXPORT    ctx->export_mode();   #endif -  +  +  if (!bound) { +  bind(); +  if (old_cert_failure && bound) +  report_notice (LOC_M (0, "TLS port %s opened.\n"), get_url());    } -  +  }       class CertificateListVariable    {    inherit Variable.FileList;       string doc()    {    return sprintf(::doc() + "\n",    combine_path(getcwd(), "../local"),    getcwd());    } -  -  static void create(mixed default_value, void|int flags, -  void|LocaleString std_name, void|LocaleString std_doc) -  { -  ::create(default_value, flags, std_name, std_doc); -  set_changed_callback(certificates_changed); +     } -  } +        class KeyFileVariable    {    inherit Variable.String;       string doc()    {    return sprintf(::doc() + "\n",    combine_path(getcwd(), "../local"),    getcwd());    } -  -  static void create(mixed default_value, void|int flags, -  void|LocaleString std_name, void|LocaleString std_doc) -  { -  ::create(default_value, flags, std_name, std_doc); -  set_changed_callback(certificates_changed); +     } -  } +        RoxenSSLFile accept()    {    Stdio.File q = ::accept();    if (q)    return RoxenSSLFile (q, ctx);    return 0;    }    -  +  static void bind() +  { +  // Don't bind if we don't have correct certs. +  if (!ctx->certificates) return; +  ::bind(); +  } +     void create(int pn, string i)    {   #if constant(Crypto.Random.random_string)    ctx->random = Crypto.Random.random_string;   #else    ctx->random = Crypto.randomness.reasonably_random()->read;   #endif       set_up_ssl_variables( this_object() );    -  ::create(pn, i); +  ::setup(pn, i);       certificates_changed(); -  +  +  // Install the change callbacks here to avoid duplicate calls +  // above. +  getvar ("ssl_cert_file")->set_changed_callback (certificates_changed); +  getvar ("ssl_key_file")->set_changed_callback (certificates_changed);    }       string _sprintf( )    { -  return "SSLProtocol("+name+"://"+ip+":"+port+")"; +  return "SSLProtocol(" + get_url() + ")";    }   }   #endif      mapping(string:Protocol) build_protocols_mapping()   {    mapping protocols = ([]);    int st = gethrtime();    report_debug("Protocol handlers ... \b");   #ifndef DEBUG