From: root Date: Wed, 9 Mar 2011 00:30:40 +0000 (-0500) Subject: initial commit X-Git-Url: http://www.project-moonshot.org/gitweb/?p=devwiki.git;a=commitdiff_plain;h=70a0924eff9735c55118faf433e06869c4923376 initial commit --- 70a0924eff9735c55118faf433e06869c4923376 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b84c806 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.ikiwiki +/recentchanges diff --git a/wiki/.ikiwiki/indexdb b/wiki/.ikiwiki/indexdb new file mode 100644 index 0000000..5d262ac Binary files /dev/null and b/wiki/.ikiwiki/indexdb differ diff --git a/wiki/.ikiwiki/lockfile b/wiki/.ikiwiki/lockfile new file mode 100644 index 0000000..e69de29 diff --git a/wiki/building.mdwn b/wiki/building.mdwn new file mode 100644 index 0000000..d890693 --- /dev/null +++ b/wiki/building.mdwn @@ -0,0 +1,43 @@ +# Building moonshot +First, make sure you have the following dependencies installed: + +1. [OpenSSL](http://www.openssl.org/) +1. [Curl](http://curl.haxx.se) +1. [XML Security](http://xml.apache.org/security/) +1. Xerces XML parser +1. [MIT Kerberos](http://web.mit.edu/kerberos/) at least version 1.9 +1. log4cpp or log4shibb +1. [libevent](http://www.monkey.org/~provos/libevent/) at least +version 2.0 + +On a Debian or Ubuntu system you can get a lot of these dependencies +with: + + aptitude install libxml-security-c-dev libxerces-c-dev \ + libcurl4-openssl-dev liblog4cpp5-dev + +A sufficiently new version of libevent is in the libevent-dev package +in Debian experimental. The libkrb5-dev in Debian experimental is also +sufficiently new. [Sam Hartman](http://www.launchpad.net/~hartmans) +maintains sufficiently new krb5 for Ubuntu. + +Follow the +[Repository](http://www.project-moonshot.org/developers/repository) +instructions for checking out a copy of Moonshot At this writing, the +following should be sufficient: + + git clone http://www.project-moonshot.org/git/moonshot.git + cd moonshot + git submodule init + git submodule update + + + +Create some directory that you have permission to write to. We'll +assume this is /usr/local/moonshot. Run + + ./builder --prefix=/usr/local/moonshot + +The builder script takes other options to permit modifying what is +passed to configure. The script will build and install the Moonshot +projects. Now [[Prepare]] to use the software. diff --git a/wiki/design.mdwn b/wiki/design.mdwn new file mode 100644 index 0000000..c87f170 --- /dev/null +++ b/wiki/design.mdwn @@ -0,0 +1,4 @@ +# Design Discussions +[[!toc startlevel=2]] + +[[!inline pages=design/* ]] diff --git a/wiki/design/tlv.mdwn b/wiki/design/tlv.mdwn new file mode 100644 index 0000000..ec1be93 --- /dev/null +++ b/wiki/design/tlv.mdwn @@ -0,0 +1,178 @@ +## Rationale for the TLV abstraction + +hartmans: Luke, do you have opinions on what we want to do to the tokens for the target name stuff and SNI? +lukeh: will we be sending a bunch of arbitrary attributes like the acceptor does? like GSS-Asserted-Acceptor-Service-Name +lukeh: what's SNI? +hartmans: I think it means that the first token from initiator to acceptor (which I guess is useless today) probably wants to be a TLV token. +lukeh: yeah, I followed that on the mailing list but didn't implement as I was busy with other stuff +lukeh: that sounds reasonable, I guess it would require some reorganisation +lukeh: why do we want to send the target name? +hartmans: If you're getting a target name back then you probably want a TLV token for the first acceptor to initiator. But today that also includes an eap identity request, right? +lukeh: yes +hartmans: The question is do we want to TLV all the EAP tokens at that point? +lukeh: hmm. +lukeh: well either way I guess has merit +hartmans: The down side of TLVing everything is that then you can have news ways of having mallformed tokens. +lukeh: but I probably need to understand things better +lukeh: why does the acceptor send the target name back? +lukeh: I'm starting from a point of not understanding what is wrong with the existing protocol :) +hartmans: OK, so there are two different usages. +lukeh: as my understanding is we get the acceptor name back anyway in the EAP CB attributes +hartmans: 1) server name indication: the initiator knows the target name, but the acceptor may server multiple names. +hartmans: Here, the acceptor wants to know what name the initiator is calling. +***hartmans needs to think through the gss implications of what names the server accepts with Nico +hartmans: The other usage is null target name: the initiator doesn't know the target name but will look at the target name in the established context and see if it likes it. +hartmans: However in our mechanism, the initiator needs to learn the target name early so it can channel bind to it in EAP so it actually can believe the result. +hartmans: No, we don't get the acceptor name back in the eap CB. +hartmans: We *send* it in the eap cb and get back an indication about whether the server considered it in the CB result. +lukeh: ah. +hartmans: We don't get the value back. +hartmans: The other thing I'm wondering. +hartmans: Is there any actual information in the eap identity request? +hartmans: Could we just fake that packet and save a round trip? +lukeh: hmm, good point +Josh: we need the realm for routing +lukeh: that's in the response, not the request. +hartmans: That's in the identity response, right? +Josh: oh I mis-read +lukeh: yeah, Sam, that's an interesting point +lukeh: it didn't occur to me to do that +Josh: that would be a violation of the EAP SM, no? +lukeh: possibly +lukeh: I mean, the initiator would generate it itself rather than getting it from the acceptor +lukeh: certainly it's easier to understand in the current protocol +lukeh: but whether it's not worth optimising away is another question. +hartmans: Josh, it all depends on where you draw the layers. +Josh: take your point +hartmans: Note that on the radius server, the EAP SM never generates that packet... The NAS generates that one, right? +Josh: right +hartmans: I'm arguing we're basically doing the same thing. Rather than having the NAs generate that, we're asking the client to generate that and feed it to its own state machine. +hartmans: That only works if the packet is content free. +Josh: what's the advantage to that? +lukeh: save a roundtrip +lukeh: because currently the initiator sends an empty token +lukeh: just to poke things into action +Josh: IIRC, the data field can be used in the EAP-Identity/Request, but rarely is. +lukeh: hmm, if we are optimising something away that might be useful +hartmans: If it is used, what's it used for. +lukeh: then that may be bad. +hartmans: Sadly, Jari is not online. (He's the obvious eap expert in my buddy list) +Josh: I think we have previously floated the idea of using that data field for federation selection +lukeh: heh, can we shove the acceptor name in there +hartmans: Josh, I think that the changes we're talking about for target name null and for sni will give us the rope we need for federation selection. +***hartmans will investigate what that's useful for. +lukeh: please define SNI +lukeh: server name indication +lukeh: ? +Josh: True, and cleaner I believe. +hartmans: Luke if you're going to have an opinion on what tokens should be TLVed, please do so preferably before IETF. +hartmans: yes + +## Actual Design of resulting TLV implementation + +lukeh: OK, so the TLV code runs on a table of ( input token, output token, valid states, flags, callback ) +lukeh: there are still a few too many special cases to be completely in love with it +lukeh: but hopefully after a few more days of hacking it will get there +lukeh: I've added some debug tokens in the initial leg to test that that works +lukeh: the previous extension tokens (GSS CB, reauth) are now just, obviously, TLVs in the last leg +lukeh: there is now only one *GSS* token for context establishment, TOK_TYPE_ESTABLISH_CONTEXT +lukeh: and a bunch of "inner" token types +lukeh: #define ITOK_TYPE_CONTEXT_ERR 0x00000001 + #define ITOK_TYPE_ACCEPTOR_NAME_REQ 0x00000002 + #define ITOK_TYPE_ACCEPTOR_NAME_RESP 0x00000003 + #define ITOK_TYPE_EAP_RESP 0x00000004 + #define ITOK_TYPE_EAP_REQ 0x00000005 + #define ITOK_TYPE_GSS_CHANNEL_BINDINGS 0x00000006 + #define ITOK_TYPE_REAUTH_CREDS 0x00000007 + #define ITOK_TYPE_REAUTH_REQ 0x00000008 + #define ITOK_TYPE_REAUTH_RESP 0x00000009 + #define ITOK_TYPE_VERSION_INFO 0x0000000A + #define ITOK_TYPE_VENDOR_INFO 0x0000000B + #define ITOK_FLAG_CRITICAL 0x80000000 /* critical, wire flag */ + +lukeh: s/last leg/last round trip/ +hartmans: What do you mean by token types in the table? +hartmans: Do you mean that a given callback can only produce one output token type, or something more than that? +lukeh: a given callback can only produce one output token type +lukeh: true, a callback could have returned a set of tokens +lukeh: perhaps that would be desirable +lukeh: but as long as the state can be passed through the context or credential object +lukeh: the same effect can be achieved +hartmans: I think that simplicity is good for now. +hartmans: OK, that implementation makes sense. +lukeh: the trunk is simplicity :-) +lukeh: although one nice thing is that the state machine walker is now shared between initiator and acceptor. +lukeh: so I may have reduced the line count actually. +lukeh: meh, about the same. +hartmans: So, can a callback return success and no token? +lukeh: yeah. +hartmans: good. +lukeh: (well, CONTINUE, or COMPLETE) +lukeh: I needed that for a couple of cases. +lukeh: first, if you transit states and don't emit a token +hartmans: and anyone returning continue means overall result is continue? +hartmans: anyone returning error means overall result is error? +lukeh: (hang on) +lukeh: e.g. going from completing the EAP stage (if no token emitted) to the extension stage +hartmans: Does that produce a round-trip or does it simply re-run the packet in the new state combining the output? +lukeh: (I'm going to answer your questions in order asked) +hartmans: That (state transition) was the case that was making me think you'd need the no token logic +lukeh: also, if you have a set of optional tokens (e.g. extensions) you need something to mark the end and transit the state. +hartmans: Yes, answering in order asked is fine. +lukeh: The very final callback that marks the context established returns COMPLETE +lukeh: Others return CONTINUE on success or an error. +lukeh: Returning an error means the overall result is an error and, on the acceptor side, an error token may be generated (this is done at the state machine layer) +lukeh: In case of a state transition when there is no output token, then the next set of callbacks are called with no input token +lukeh: to avoid regurgitating the input token from the previous state +hartmans: OK that last is non-obvious to me (calling nwith no input token) +lukeh: so, basically, tokens are emitted to the peer +hartmans: Can I see the table? It might be more clear there. +lukeh: when we've run out of callbacks or we transit states and have a token to send +lukeh: (more or less, there are a couple of exceptions to handle things like sending no tokens on the initial context token in order to poke the acceptor into sending us an EAP request) +lukeh: (rather than sending an empty EAP response from the initiator, as semantically that did not seem correct) +lukeh: (although perhaps it is, who knows.) +lukeh: if you checkout tlv +lukeh: look for eapGssInitiatorSm in init_sec_context.c +lukeh: and the corresponding one for the acceptor. +lukeh: note: the states are bitmasks. +lukeh: there's some verbose commenting in init_sm.c too. +hartmans: Ah, that makes so much more sense from the table +hartmans: This is quite clever. +lukeh: Hmm, it still doesn't feel *quite* right. Too many exceptions. I suspect if that if I was doing it from scratch rather than refactoring it might look different. But, it seems to work for now. Will revise over coming days. +lukeh: I think it is ugly because it collapses the state and token dimensions into a single one. +lukeh: However it does make it easier to have tokens that support multiple states. +lukeh: Although there are some limitations with that (the dispatch table is not retraversed after a state change so it effectively only works for exception tokens; of course, that's easily fixed) +hartmans: So, we depend on the EAP machine keeping us in sync between the initiator and acceptor? +hartmans: Not a problem, just confirming I understand. +hartmans: What do you mean state and token are combined? They are separate columns in the table as far as I can tell +lukeh: it's not a 2 dimensional array. +lukeh: they are separate columns yes +lukeh: re: depending on the EAP machine +hartmans: Ah. +lukeh: yes, I guess we do, what else could one do? +lukeh: it's a black box +lukeh: I don't know what happens yet if the EAP machine emits a token on success, I don't think that's possible though +lukeh: I need to check +***hartmans has too much of a relational database mindset to think of that as more than an efficiency issue +lukeh: yeah, I never used relational databases, so I never had that mindset +hartmans: I think depending on EAP to be consistent is fine. +hartmans: We could actually echo the eap state in some sort of market token. That would be far far worse. +lukeh: So, adding a new token that doesn't change the state is fairly easy. +lukeh: You just need to be careful where you put it in the table :) +lukeh: It needs to be before the state changing entry. +lukeh: You can set the critical/required flags as desired. +lukeh: Some care is likely necessary to avoid colliding with the reauthentication path. But I think that's fairly easy. +lukeh: In my initial implementation I had overloaded the GSS status codes to mean various things, but in the end that got quite ugly. +lukeh: So I went with the callbacks performing the state changes themselves plus a flag to indicate a few exceptional things (e.g. get-out now) +hartmans: Presumably error always means get out now? +lukeh: yes +lukeh: if you want to get out now on CONTINUE, you need to either +lukeh: a) change state and emit a token +lukeh: b) change state and set FORCE_SEND_TOKEN (this is used to handle the initial case where we poke the acceptor without sending an inner token) +lukeh: c) set STOP_EVAL - this is not used yet +lukeh: the above applies to COMPLETE as well as CONTINUE +lukeh: there are some sanity checks to make sure COMPLETE only happens when state becomes ESTABLISHED +lukeh: see SM_ASSERT_VALID +hartmans: OK, and we can remove an exception if we decide it's OK for us to fake the eap request identity +lukeh: ah yes +hartmans: This is really cool. diff --git a/wiki/index.mdwn b/wiki/index.mdwn new file mode 100644 index 0000000..b2d2e81 --- /dev/null +++ b/wiki/index.mdwn @@ -0,0 +1,6 @@ +# Moonshot Developer Wiki + +This is a wiki for those working on or using the Moonshot code base. + +* [[Building]] Moonshot +* [[Design]] Discussions diff --git a/wiki/prepare.mdwn b/wiki/prepare.mdwn new file mode 100644 index 0000000..a239733 --- /dev/null +++ b/wiki/prepare.mdwn @@ -0,0 +1,10 @@ +# Preparing to use Moonshot + +First, look at the mech file in the mech_eap directory of the source +tree. Copy this file to /etc/gss/mech and change the location of +mech_eap.so to be correct for your installation. + +Todo: +* configure libradsec +* Set up RADIUS + diff --git a/wiki/templates/page.tmpl b/wiki/templates/page.tmpl new file mode 100644 index 0000000..0682a74 --- /dev/null +++ b/wiki/templates/page.tmpl @@ -0,0 +1,197 @@ + + + + + + + + + + + + +<TMPL_VAR TITLE> + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +
+ +
+ +
+ + + +
+ + + + +
Comments on this page are closed.
+
+
+
+
+ + + +
+ + + + + + + +
+ +License: +
+
+ +
+Last edited + +
+ + + + + +
+ +
+ + + diff --git a/wiki/wiki.conf b/wiki/wiki.conf new file mode 100644 index 0000000..638a96f --- /dev/null +++ b/wiki/wiki.conf @@ -0,0 +1,310 @@ +#!/usr/bin/perl +# +# Setup file for ikiwiki. +# +# Passing this to ikiwiki --setup will make ikiwiki generate +# wrappers and build the wiki. +# +# Remember to re-run ikiwiki --setup any time you edit this file. +use IkiWiki::Setup::Standard { + # name of the wiki + wikiname => 'Moonshot Developer Wiki', + # contact email for wiki + adminemail => 'moonshot-community@jiscmail.ac.uk', + # users who are wiki admins + adminuser => [], + # users who are banned from the wiki + banned_users => [], + # where the source of the wiki is located + srcdir => '.', + # where to build the wiki + destdir => '../wiki-out', + # base url to the wiki + url => '', + # url to the ikiwiki.cgi + cgiurl => '', + # filename of cgi wrapper to generate + cgi_wrapper => '', + # mode for cgi_wrapper (can safely be made suid) + cgi_wrappermode => '06755', + # rcs backend to use + rcs => 'git', + # plugins to add to the default configuration + add_plugins => ['toc'], + # plugins to disable + disable_plugins => [], + # additional directory to search for template files + templatedir => '/usr/share/ikiwiki/templates', + # base wiki source location + underlaydir => '/usr/share/ikiwiki/basewiki', + # display verbose messages? + #verbose => 1, + # log to syslog? + #syslog => 1, + # create output files named page/index.html? + usedirs => 1, + # use '!'-prefixed preprocessor directives? + prefix_directives => 1, + # use page/index.mdwn source files + indexpages => 0, + # enable Discussion pages? + discussion => 1, + # name of Discussion pages + discussionpage => 'Discussion', + # generate HTML5? (experimental) + html5 => 0, + # only send cookies over SSL connections? + sslcookie => 0, + # extension to use for new pages + default_pageext => 'mdwn', + # extension to use for html files + htmlext => 'html', + # strftime format string to display date + timeformat => '%c', + # UTF-8 locale to use + #locale => 'en_US.UTF-8', + # put user pages below specified page + userdir => '', + # how many backlinks to show before hiding excess (0 to show all) + numbacklinks => 10, + # attempt to hardlink source files? (optimisation for large files) + hardlink => 0, + # force ikiwiki to use a particular umask + #umask => 022, + # group for wrappers to run in + #wrappergroup => 'ikiwiki', + # extra library and plugin directory + libdir => '', + # environment variables + ENV => {}, + # regexp of normally excluded files to include + #include => '^\\.htaccess$', + # regexp of files that should be skipped + #exclude => '^(*\\.private|Makefile)$', + # specifies the characters that are allowed in source filenames + wiki_file_chars => '-[:alnum:]+/.:_', + # allow symlinks in the path leading to the srcdir (potentially insecure) + allow_symlinks_before_srcdir => 0, + + ###################################################################### + # core plugins + # (editpage, htmlscrubber, inline, link, meta, parentlinks) + ###################################################################### + + # htmlscrubber plugin + # PageSpec specifying pages not to scrub + #htmlscrubber_skip => '!*/Discussion', + + # inline plugin + # enable rss feeds by default? + #rss => 0, + # enable atom feeds by default? + atom => 1, + # allow rss feeds to be used? + #allowrss => 0, + # allow atom feeds to be used? + allowatom => 1, + # urls to ping (using XML-RPC) on feed update + pingurl => [], + + ###################################################################### + # auth plugins + # (anonok, blogspam, httpauth, lockedit, moderatedcomments, + # opendiscussion, openid, passwordauth, signinedit) + ###################################################################### + + # anonok plugin + # PageSpec to limit which pages anonymous users can edit + #anonok_pagespec => '*/discussion', + + # blogspam plugin + # PageSpec of pages to check for spam + #blogspam_pagespec => 'postcomment(*)', + # options to send to blogspam server + #blogspam_options => 'blacklist=1.2.3.4,blacklist=8.7.6.5,max-links=10', + # blogspam server XML-RPC url + #blogspam_server => '', + + # httpauth plugin + # url to redirect to when authentication is needed + #cgiauthurl => 'http://example.com/wiki/auth/ikiwiki.cgi', + # PageSpec of pages where only httpauth will be used for authentication + #httpauth_pagespec => '!*/Discussion', + + # lockedit plugin + # PageSpec controlling which pages are locked + #locked_pages => '!*/Discussion', + + # moderatedcomments plugin + # PageSpec matching users or comment locations to moderate + #moderate_pagespec => '*', + + # openid plugin + # url pattern of openid realm (default is cgiurl) + #openid_realm => '', + # url to ikiwiki cgi to use for openid authentication (default is cgiurl) + #openid_cgiurl => '', + + # passwordauth plugin + # a password that must be entered when signing up for an account + #account_creation_password => 's3cr1t', + # cost of generating a password using Authen::Passphrase::BlowfishCrypt + #password_cost => 8, + + ###################################################################### + # format plugins + # (creole, highlight, hnb, html, mdwn, otl, rawhtml, textile, txt) + ###################################################################### + + # highlight plugin + # types of source files to syntax highlight + #tohighlight => '.c .h .cpp .pl .py Makefile:make', + + # mdwn plugin + # enable multimarkdown features? + #multimarkdown => 0, + + ###################################################################### + # misc plugins + # (filecheck) + ###################################################################### + + ###################################################################### + # web plugins + # (404, attachment, comments, editdiff, edittemplate, getsource, + # google, goto, mirrorlist, remove, rename, repolist, search, + # theme, websetup, wmd) + ###################################################################### + + # attachment plugin + # enhanced PageSpec specifying what attachments are allowed + #allowed_attachments => 'virusfree() and mimetype(image/*) and maxsize(50kb)', + # virus checker program (reads STDIN, returns nonzero if virus found) + #virus_checker => 'clamdscan -', + + # comments plugin + # PageSpec of pages where comments are allowed + #comments_pagespec => 'blog/* and !*/Discussion', + # PageSpec of pages where posting new comments is not allowed + #comments_closed_pagespec => 'blog/controversial or blog/flamewar', + # Base name for comments, e.g. "comment_" for pages like "sandbox/comment_12" + #comments_pagename => '', + # Interpret directives in comments? + #comments_allowdirectives => 0, + # Allow anonymous commenters to set an author name? + #comments_allowauthor => 0, + # commit comments to the VCS + #comments_commit => 1, + + # getsource plugin + # Mime type for returned source. + #getsource_mimetype => 'text/plain; charset=utf-8', + + # mirrorlist plugin + # list of mirrors + #mirrorlist => {}, + + # repolist plugin + # URIs of repositories containing the wiki's source + #repositories => [qw{svn://svn.example.org/wiki/trunk}], + + # search plugin + # path to the omega cgi program + #omega_cgi => '/usr/lib/cgi-bin/omega/omega', + + # theme plugin + # name of theme to enable + #theme => 'actiontabs', + + # websetup plugin + # list of plugins that cannot be enabled/disabled via the web interface + #websetup_force_plugins => [], + # list of additional setup field keys to treat as unsafe + #websetup_unsafe => [], + # show unsafe settings, read-only, in web interface? + #websetup_show_unsafe => 1, + + ###################################################################### + # widget plugins + # (calendar, color, conditional, cutpaste, date, format, fortune, + # graphviz, haiku, img, linkmap, listdirectives, map, more, + # orphans, pagecount, pagestats, poll, polygen, postsparkline, + # progress, shortcut, sparkline, table, template, teximg, toc, + # toggle, version) + ###################################################################### + + # calendar plugin + # base of the archives hierarchy + #archivebase => 'archives', + # PageSpec of pages to include in the archives; used by ikiwiki-calendar command + #archive_pagespec => 'page(posts/*) and !*/Discussion', + + # listdirectives plugin + # directory in srcdir that contains directive descriptions + #directive_description_dir => 'ikiwiki/directive', + + # teximg plugin + # Should teximg use dvipng to render, or dvips and convert? + #teximg_dvipng => '', + # LaTeX prefix for teximg plugin + #teximg_prefix => '\\documentclass{article} + #\\usepackage{amsmath} + #\\usepackage{amsfonts} + #\\usepackage{amssymb} + #\\pagestyle{empty} + #\\begin{document} + #', + # LaTeX postfix for teximg plugin + #teximg_postfix => '\\end{document}', + + ###################################################################### + # other plugins + # (aggregate, autoindex, brokenlinks, camelcase, ddate, embed, + # favicon, goodstuff, htmlbalance, localstyle, pagetemplate, + # pingee, pinger, prettydate, recentchanges, recentchangesdiff, + # relativedate, rsync, sidebar, smiley, sortnaturally, tag, + # testpagespec, underlay) + ###################################################################### + + # aggregate plugin + # enable aggregation to internal pages? + #aggregateinternal => 1, + # allow aggregation to be triggered via the web? + #aggregate_webtrigger => 0, + + # camelcase plugin + # list of words to not turn into links + #camelcase_ignore => [], + + # pinger plugin + # how many seconds to try pinging before timing out + #pinger_timeout => 15, + + # prettydate plugin + # format to use to display date + #prettydateformat => '%X, %B %o, %Y', + + # recentchanges plugin + # name of the recentchanges page + recentchangespage => 'recentchanges', + # number of changes to track + recentchangesnum => 100, + + # rsync plugin + # command to run to sync updated pages + #rsync_command => 'rsync -qa --delete . user@host:/path/to/docroot/', + + # sidebar plugin + # show sidebar page on all pages? + #global_sidebars => 1, + + # tag plugin + # parent page tags are located under + #tagbase => 'tag', + # autocreate new tag pages? + #tag_autocreate => 1, + + # underlay plugin + # extra underlay directories to add + #add_underlays => [qw{/home/hartmans/wiki.underlay}], +}