From f9d6b218be3bdbb2d3c544849b2ab92348d0e394 Mon Sep 17 00:00:00 2001 From: elijah Date: Sun, 19 May 2013 22:28:01 -0700 Subject: many related changes -- allow command line configs, validate addresses, overhaul bootstrap. --- README.md | 28 +++++++++++++++++++++++----- bin/nickserver | 27 ++++++++++----------------- config/default.yml | 26 ++++++++++++++++++++------ lib/nickserver.rb | 1 + lib/nickserver/config.rb | 2 ++ lib/nickserver/daemon.rb | 15 ++++++++++++--- lib/nickserver/email_address.rb | 25 +++++++++++++++++++++++++ lib/nickserver/server.rb | 32 ++++++++++++++++++++++---------- lib/nickserver/version.rb | 2 +- test/unit/nickserver_test.rb | 4 +--- 10 files changed, 117 insertions(+), 45 deletions(-) create mode 100644 lib/nickserver/email_address.rb diff --git a/README.md b/README.md index 1546d57..edeae59 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ Nickserver ================================== -Nickserver is a server running the Nicknym protocol. This daemon can be run by service providers in order to support Nicknym. +Nickserver is a server running the Nicknym protocol. This daemon can be run by +service providers in order to support Nicknym. -Nicknym is a protocol to map user nicknames to public keys. With Nicknym, the user is able to think solely in terms of nickname, while still being able to communicate with a high degree of security (confidentiality, integrity, and authenticity). Essentially, Nicknym is a system for binding human-memorable nicknames to a cryptographic key via automatic discovery and automatic validation. +Nicknym is a protocol to map user nicknames to public keys. With Nicknym, the +user is able to think solely in terms of nickname, while still being able to +communicate with a high degree of security (confidentiality, integrity, and +authenticity). Essentially, Nicknym is a system for binding human-memorable +nicknames to a cryptographic key via automatic discovery and automatic +validation. For more information, see https://leap.se/nicknym @@ -20,7 +26,8 @@ You query the nickserver via HTTP. The API is very minimal: curl -X POST -d address=alice@domain.org https://nicknym.domain.org:6425 -The response consists of a signed JSON document with fields for the available public keys corresponding to the address. +The response consists of a signed JSON document with fields for the available +public keys corresponding to the address. For more details, see https://leap.se/nicknym @@ -35,6 +42,13 @@ Installation You have three fine options for installing nickserver: +Install prerequisites + + $ sudo apt-get install ruby1.9.1-dev libssl-dev + +Note: libssl-dev must be installed before installing the gem EventMachine, +otherwise the gem will get built without TLS support. + Install from source: $ git clone git://leap.se/nickserver @@ -52,9 +66,13 @@ Install for development: Configuration ================================== -Nickserver loads the configuration files `config/default.yml` and `/etc/leap/nickserver.yml`, if it exists. See `config/default.yml` for the available options. +Nickserver loads the configuration files `config/default.yml` and +`/etc/leap/nickserver.yml`, if it exists. See `config/default.yml` for the +available options. -The default HKP host is set to https://hkps.pool.sks-keyservers.net. The CA for this pool is available here https://sks-keyservers.net/sks-keyservers.netCA.pem +The default HKP host is set to https://hkps.pool.sks-keyservers.net. The CA +for this pool is available here https://sks-keyservers.net/sks- +keyservers.netCA.pem Usage ================================== diff --git a/bin/nickserver b/bin/nickserver index 8843dde..062684f 100755 --- a/bin/nickserver +++ b/bin/nickserver @@ -4,27 +4,20 @@ # Nickserver key discovery daemon # -def load_library(name) - begin # try without rubygems (might be already loaded or not present) - require name - rescue LoadError - begin # try it as a gem - require 'rubygems' - require name - rescue LoadError # try manually, requiring gems in Gemfile if it exists. - base_directory = File.expand_path('../..', File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__) - if File.exists?("#{base_directory}/Gemfile.lock") - ENV['BUNDLE_GEMFILE'] ||= "#{base_directory}/Gemfile" - require 'bundler' - Bundler.require(:default) - end - $LOAD_PATH << "#{base_directory}/lib" - require name +def load_local_gem(dir_path='../..') + base_directory = File.expand_path(dir_path, File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__) + if !$LOAD_PATH.include? "#{base_directory}/lib" + if File.exists?("#{base_directory}/Gemfile.lock") + ENV['BUNDLE_GEMFILE'] ||= "#{base_directory}/Gemfile" + require 'bundler' + Bundler.require(:default) end + $LOAD_PATH << "#{base_directory}/lib" end end -load_library('nickserver') +load_local_gem +require 'nickserver' Nickserver::Daemon.run('nickserver') do EventMachine.run do diff --git a/config/default.yml b/config/default.yml index 4328cbd..c31b504 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1,10 +1,24 @@ +# +# nickserver daemon +# +port: 6425 +pid_file: '/tmp/nickserver.pid' +log_file: '/tmp/nickserver.log' +user: ~ +domain: ~ +domains: ~ + +# +# couch +# couch_host: 'localhost' couch_port: 5984 couch_database: 'users' -#couch_user: 'nickserver' -#couch_password: 'blahblah' +couch_user: ~ +couch_password: ~ + +# +# HKP +# hkp_url: 'https://hkps.pool.sks-keyservers.net:/pks/lookup' -port: 6425 -pid_file: '/var/run/nickserver' -user: 'nobody' -log_file: '/var/log/nickserver.log' + diff --git a/lib/nickserver.rb b/lib/nickserver.rb index 9e6464e..951dae9 100644 --- a/lib/nickserver.rb +++ b/lib/nickserver.rb @@ -1,6 +1,7 @@ require "nickserver/version" require "nickserver/config" +require "nickserver/email_address" require "nickserver/couch/fetch_key" diff --git a/lib/nickserver/config.rb b/lib/nickserver/config.rb index b283d8b..be401f5 100644 --- a/lib/nickserver/config.rb +++ b/lib/nickserver/config.rb @@ -18,6 +18,8 @@ module Nickserver attr_accessor :pid_file attr_accessor :user attr_accessor :log_file + attr_accessor :domain + attr_accessor :domains attr_accessor :loaded attr_accessor :verbose diff --git a/lib/nickserver/daemon.rb b/lib/nickserver/daemon.rb index e0bd527..651c6ce 100644 --- a/lib/nickserver/daemon.rb +++ b/lib/nickserver/daemon.rb @@ -67,7 +67,7 @@ module Nickserver create_pid_file(Config.pid_file, Config.user) catch_interrupt redirect_output - drop_permissions_to(Config.user) + drop_permissions_to(Config.user) if Config.user File.umask 0000 yield end @@ -77,7 +77,7 @@ module Nickserver File.open file, 'w' do |f| f.write("#{Process.pid}\n") end - FileUtils.chown(user, nil, file) + FileUtils.chown(user, nil, file) if Process::Sys.getuid == 0 rescue Errno::EACCES bail "insufficient permission to create to pid file `#{file}`" rescue Errno::ENOENT @@ -207,13 +207,22 @@ module Nickserver when 'status' then ARGV.shift; @command = :status when 'version' then ARGV.shift; @command = :version when '--verbose' then ARGV.shift; Config.versbose = true - when /^-/ then usage("Unknown option: #{ARGV[0].inspect}") + when /^-/ then override_default_config(ARGV.shift, ARGV.shift) else break end end usage("Missing command") unless @command end + def override_default_config(flag, value) + flag = flag.sub /^--/, '' + if Config.respond_to?("#{flag}=") + Config.send("#{flag}=", value) + else + usage("Unknown option: --#{flag}") + end + end + # # COMMANDS # diff --git a/lib/nickserver/email_address.rb b/lib/nickserver/email_address.rb new file mode 100644 index 0000000..26053a2 --- /dev/null +++ b/lib/nickserver/email_address.rb @@ -0,0 +1,25 @@ +# +# This rather crazy regexp is from here: http://code.iamcal.com/php/rfc822/ +# Licensed GPLv3 +# +# It is too liberal, allowing "!@x" as a valid address, for example, but it does +# follow the specification rather closely. +# + +module Nickserver + EmailAddress = begin + qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' + dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' + atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' + quoted_pair = '\\x5c[\\x00-\\x7f]' + domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d" + quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22" + domain_ref = atom + sub_domain = "(?:#{domain_ref}|#{domain_literal})" + word = "(?:#{atom}|#{quoted_string})" + domain = "#{sub_domain}(?:\\x2e#{sub_domain})*" + local_part = "#{word}(?:\\x2e#{word})*" + addr_spec = "#{local_part}\\x40#{domain}" + /\A#{addr_spec}\z/n + end +end diff --git a/lib/nickserver/server.rb b/lib/nickserver/server.rb index 0bda4f1..2f4d4f0 100644 --- a/lib/nickserver/server.rb +++ b/lib/nickserver/server.rb @@ -21,7 +21,7 @@ module Nickserver # def self.start(opts={}) Nickserver::Config.load - options = {:host => '0.0.0.0', :port => Nickserver::Config.port}.merge(opts) + options = {:host => '0.0.0.0', :port => Nickserver::Config.port.to_i}.merge(opts) EM.start_server options[:host], options[:port], Nickserver::Server end @@ -34,6 +34,8 @@ module Nickserver uid = get_uid_from_request if uid.nil? send_not_found + elsif uid !~ EmailAddress + send_error("Not a valid address") else send_key(uid) end @@ -42,11 +44,11 @@ module Nickserver private def send_error(msg = "not supported") - send_response(:status => 500, :content => msg) + send_response(:status => 500, :content => "500 #{msg}\n") end - def send_not_found(msg = "404 Not Found") - send_response(:status => 404, :content => msg) + def send_not_found(msg = "Not Found") + send_response(:status => 404, :content => "404 #{msg}\n") end def send_response(opts = {}) @@ -100,14 +102,24 @@ module Nickserver # Return true if the user address is for a user of this service provider. # e.g. if the provider is example.org, then alice@example.org returns true. # - # Currently, we rely on whatever hostname the client voluntarily specifies - # in the headers of the http request. + # If 'domain' is not configured, we rely on the Host header of the HTTP request. # def local_address?(uid) - hostname = @http_headers.split(/\0/).grep(/^Host: /).first.split(':')[1].strip.sub(/^nicknym\./, '') - return uid =~ /^.*@#{Regexp.escape(hostname)}$/ - #rescue - # false + uid_domain = uid.sub(/^.*@(.*)$/, "\\1") + if Config.domain + return uid_domain == Config.domain + else + # no domain configured, use Host header + host_header = @http_headers.split(/\0/).grep(/^Host: /).first + if host_header.nil? + send_error("HTTP request must include a Host header.") + else + host = host_header.split(':')[1].strip.sub(/^nicknym\./, '') + return uid_domain == host + end + end + rescue + return false end end end \ No newline at end of file diff --git a/lib/nickserver/version.rb b/lib/nickserver/version.rb index 6e86d39..1910fe1 100644 --- a/lib/nickserver/version.rb +++ b/lib/nickserver/version.rb @@ -1,3 +1,3 @@ module Nickserver - VERSION = "0.2.0" + VERSION = "0.2.1" end diff --git a/test/unit/nickserver_test.rb b/test/unit/nickserver_test.rb index c746ef5..3a286fa 100644 --- a/test/unit/nickserver_test.rb +++ b/test/unit/nickserver_test.rb @@ -8,9 +8,7 @@ require 'json' # # (2) All requests to nickserver are to localhost. # -# (3) the "Host" header for requests to nickserver must be set, because this -# is how it decides if a request is local. I am not happy about this design, -# but that is how it works for now. +# (3) the "Host" header for requests to nickserver must be set (or Config.domain set) # # (4) When stubbing requests to couchdb, the couchdb host is changed from the # default (localhost) to a dummy value (notlocalhost). -- cgit v1.2.3