diff --git a/PhDbiblio-url.bst b/PhDbiblio-url.bst new file mode 100644 index 0000000..96dbe54 --- /dev/null +++ b/PhDbiblio-url.bst @@ -0,0 +1,1447 @@ +%%% Modification of BibTeX style file CUEDbiblio.bst +%%% ... by urlbst, version 0.6 (marked with "% urlbst") +%%% See +%%% Added webpage entry type, and url and lastchecked fields. +%%% Original headers follow... + +% BibTeX standard bibliography style `plain' + % version 0.99a for BibTeX versions 0.99a or later, LaTeX version 2.09. + % Copyright (C) 1985, all rights reserved. + % Copying of this file is authorized only if either + % (1) you make absolutely no changes to your copy, including name, or + % (2) if you do make changes, you name it something other than + % btxbst.doc, plain.bst, unsrt.bst, alpha.bst, and abbrv.bst. + % This restriction helps ensure that all standard styles are identical. + % The file btxbst.doc has the documentation for this style. + +ENTRY + { address + author + booktitle + chapter + edition + editor + howpublished + institution + journal + key + month + note + number + organization + pages + publisher + school + series + title + type + volume + year + eprint % urlbst + doi % urlbst + url % urlbst + lastchecked % urlbst + } + {} + { label } + +INTEGERS { output.state before.all mid.sentence after.sentence after.block } + +STRINGS { urlintro eprinturl eprintprefix doiprefix doiurl openinlinelink closeinlinelink } % urlbst... +INTEGERS { hrefform inlinelinks makeinlinelink addeprints adddoiresolver } +% Following constants may be adjusted by hand, if desired +FUNCTION {init.urlbst.variables} +{ + "Available from: " 'urlintro := % prefix before URL + "http://arxiv.org/abs/" 'eprinturl := % prefix to make URL from eprint ref + "arXiv:" 'eprintprefix := % text prefix printed before eprint ref + "http://dx.doi.org/" 'doiurl := % prefix to make URL from DOI + "doi:" 'doiprefix := % text prefix printed before DOI ref + #0 'addeprints := % 0=no eprints; 1=include eprints + #0 'adddoiresolver := % 0=no DOI resolver; 1=include it + #0 'hrefform := % 0=no crossrefs; 1=hypertex xrefs; 2=hyperref refs + #0 'inlinelinks := % 0=URLs explicit; 1=URLs attached to titles + % the following are internal state variables, not config constants + #0 'makeinlinelink := % state variable managed by setup.inlinelink + "" 'openinlinelink := % ditto + "" 'closeinlinelink := % ditto +} +INTEGERS { + bracket.state + outside.brackets + open.brackets + within.brackets + close.brackets +} +% ...urlbst to here +FUNCTION {init.state.consts} +{ #0 'outside.brackets := % urlbst + #1 'open.brackets := + #2 'within.brackets := + #3 'close.brackets := + + #0 'before.all := + #1 'mid.sentence := + #2 'after.sentence := + #3 'after.block := +} + +STRINGS { s t } + +% urlbst +FUNCTION {output.nonnull.original} +{ 's := + output.state mid.sentence = + { ", " * write$ } + { output.state after.block = + { add.period$ write$ + newline$ + "\newblock " write$ + } + { output.state before.all = + 'write$ + { add.period$ " " * write$ } + if$ + } + if$ + mid.sentence 'output.state := + } + if$ + s +} + +% urlbst... +% The following three functions are for handling inlinelink. They wrap +% a block of text which is potentially output with write$ by multiple +% other functions, so we don't know the content a priori. +% They communicate between each other using the variables makeinlinelink +% (which is true if a link should be made), and closeinlinelink (which holds +% the string which should close any current link. They can be called +% at any time, but start.inlinelink will be a no-op unless something has +% previously set makeinlinelink true, and the two ...end.inlinelink functions +% will only do their stuff if start.inlinelink has previously set +% closeinlinelink to be non-empty. +FUNCTION {setup.inlinelink} +{ makeinlinelink + { hrefform #1 = % hypertex + { "\special {html: }{" * 'openinlinelink := + "\special {html:}" 'closeinlinelink := + } + { hrefform #2 = % hyperref + { "\href{" url * "}{" * 'openinlinelink := + "}" 'closeinlinelink := + } + 'skip$ + if$ % hrefform #2 = + } + if$ % hrefform #1 = + #0 'makeinlinelink := + } + 'skip$ + if$ % makeinlinelink +} +FUNCTION {add.inlinelink} +{ openinlinelink empty$ + 'skip$ + { openinlinelink swap$ * closeinlinelink * + "" 'openinlinelink := + } + if$ +} +FUNCTION {output.nonnull} +{ % Save the thing we've been asked to output + 's := + % If the bracket-state is close.brackets, then add a close-bracket to + % what is currently at the top of the stack, and set bracket.state + % to outside.brackets + bracket.state close.brackets = + { "]" * + outside.brackets 'bracket.state := + } + 'skip$ + if$ + bracket.state outside.brackets = + { % We're outside all brackets -- this is the normal situation. + % Write out what's currently at the top of the stack, using the + % original output.nonnull function. + s + add.inlinelink + output.nonnull.original % invoke the original output.nonnull + } + { % Still in brackets. Add open-bracket or (continuation) comma, add the + % new text (in s) to the top of the stack, and move to the close-brackets + % state, ready for next time (unless inbrackets resets it). If we come + % into this branch, then output.state is carefully undisturbed. + bracket.state open.brackets = + { " [" * } + { ", " * } % bracket.state will be within.brackets + if$ + s * + close.brackets 'bracket.state := + } + if$ +} + +% Call this function just before adding something which should be presented in +% brackets. bracket.state is handled specially within output.nonnull. +FUNCTION {inbrackets} +{ bracket.state close.brackets = + { within.brackets 'bracket.state := } % reset the state: not open nor closed + { open.brackets 'bracket.state := } + if$ +} + +FUNCTION {format.lastchecked} +{ lastchecked empty$ + { "" } + { inbrackets "cited " lastchecked * } + if$ +} +% ...urlbst to here + +FUNCTION {output} +{ duplicate$ empty$ + 'pop$ + 'output.nonnull + if$ +} + +FUNCTION {output.check} +{ 't := + duplicate$ empty$ + { pop$ "empty " t * " in " * cite$ * warning$ } + 'output.nonnull + if$ +} + +FUNCTION {output.bibitem.original} +{ newline$ + "\bibitem{" write$ + cite$ write$ + "}" write$ + newline$ + "" + before.all 'output.state := +} + +FUNCTION {fin.entry.original} +{ add.period$ + write$ + newline$ +} + +FUNCTION {new.block} +{ output.state before.all = + 'skip$ + { after.block 'output.state := } + if$ +} + +FUNCTION {new.sentence} +{ output.state after.block = + 'skip$ + { output.state before.all = + 'skip$ + { after.sentence 'output.state := } + if$ + } + if$ +} + +FUNCTION {not} +{ { #0 } + { #1 } + if$ +} + +FUNCTION {and} +{ 'skip$ + { pop$ #0 } + if$ +} + +FUNCTION {or} +{ { pop$ #1 } + 'skip$ + if$ +} + +FUNCTION {new.block.checka} +{ empty$ + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.block.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.block + if$ +} + +FUNCTION {new.sentence.checka} +{ empty$ + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {new.sentence.checkb} +{ empty$ + swap$ empty$ + and + 'skip$ + 'new.sentence + if$ +} + +FUNCTION {field.or.null} +{ duplicate$ empty$ + { pop$ "" } + 'skip$ + if$ +} + +FUNCTION {emphasize} +{ duplicate$ empty$ + { pop$ "" } + { "{\em " swap$ * "}" * } + if$ +} + +FUNCTION {scapify} +{ duplicate$ empty$ + { pop$ "" } + { "{\sc " swap$ * "}" * } + if$ +} + +FUNCTION {boldify} +{ duplicate$ empty$ + { pop$ "" } + { "{\bf " swap$ * "}" * } + if$ +} + +% -------------------------------------------------------------- +% -------------------------------------------------------------- + +INTEGERS { nameptr namesleft numnames } + +FUNCTION {format.names} +{ 's := + #1 'nameptr := + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { s nameptr "{ff~}{vv~}{ll}{, jj}" format.name$ 't := + nameptr #1 > + { namesleft #1 > + { ", " * t * } + { numnames #2 > + { "," * } + 'skip$ + if$ + t "others" = + { " et~al." * } + { " and " * t * } + if$ + } + if$ + } + 't + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {format.authors} +{ author empty$ + { "" } + { author format.names scapify } + if$ +} + +FUNCTION {format.editors} +{ editor empty$ + { "" } + { editor format.names scapify + editor num.names$ #1 > + { ", editors" * } + { ", editor" * } + if$ + } + if$ +} + +% this changes the case; very bad for abbreviations like DNA, RNA +%FUNCTION {format.title} +%{ title empty$ +% { "" } +% { title "t" change.case$ } +% if$ +%} + +FUNCTION {format.title} +{ title boldify +} + + +FUNCTION {n.dashify} +{ 't := + "" + { t empty$ not } + { t #1 #1 substring$ "-" = + { t #1 #2 substring$ "--" = not + { "--" * + t #2 global.max$ substring$ 't := + } + { { t #1 #1 substring$ "-" = } + { "-" * + t #2 global.max$ substring$ 't := + } + while$ + } + if$ + } + { t #1 #1 substring$ * + t #2 global.max$ substring$ 't := + } + if$ + } + while$ +} + +FUNCTION {format.date} +{ year empty$ + { month empty$ + { "" } + { "there's a month but no year in " cite$ * warning$ + month + } + if$ + } + { month empty$ + 'year + { month " " * year * } + if$ + } + if$ +} + +FUNCTION {format.btitle} +{ title emphasize +} + +FUNCTION {tie.or.space.connect} +{ duplicate$ text.length$ #3 < + { "~" } + { " " } + if$ + swap$ * * +} + +FUNCTION {either.or.check} +{ empty$ + 'pop$ + { "can't use both " swap$ * " fields in " * cite$ * warning$ } + if$ +} + +FUNCTION {format.bvolume} +{ volume empty$ + { "" } + { "" volume boldify tie.or.space.connect + series empty$ + 'skip$ + { " of " * series emphasize * } + if$ + "volume and number" number either.or.check + } + if$ +} + +FUNCTION {format.number.series} +{ volume empty$ + { number empty$ + { series field.or.null } + { output.state mid.sentence = + { "number" } + { "Number" } + if$ + number tie.or.space.connect + series empty$ + { "there's a number but no series in " cite$ * warning$ } + { " in " * series * } + if$ + } + if$ + } + { "" } + if$ +} + +FUNCTION {format.edition} +{ edition empty$ + { "" } + { output.state mid.sentence = + { edition "l" change.case$ " edition" * } + { edition "t" change.case$ " edition" * } + if$ + } + if$ +} + +INTEGERS { multiresult } + +FUNCTION {multi.page.check} +{ 't := + #0 'multiresult := + { multiresult not + t empty$ not + and + } + { t #1 #1 substring$ + duplicate$ "-" = + swap$ duplicate$ "," = + swap$ "+" = + or or + { #1 'multiresult := } + { t #2 global.max$ substring$ 't := } + if$ + } + while$ + multiresult +} + +FUNCTION {format.pages} +{ pages empty$ + { "" } + { pages multi.page.check + { "pages" pages n.dashify tie.or.space.connect } + { "page" pages tie.or.space.connect } + if$ + } + if$ +} + +FUNCTION {format.vol.num.pages} +{ volume field.or.null boldify + number empty$ + 'skip$ + { "(" number * ")" * * + volume empty$ + { "there's a number but no volume in " cite$ * warning$ } + 'skip$ + if$ + } + if$ + pages empty$ + 'skip$ + { duplicate$ empty$ + { pop$ format.pages } + { ":" * pages n.dashify * } + if$ + } + if$ +} + +FUNCTION {format.chapter.pages} +{ chapter empty$ + 'format.pages + { type empty$ + { "chapter" } + { type "l" change.case$ } + if$ + chapter tie.or.space.connect + pages empty$ + 'skip$ + { ", " * format.pages * } + if$ + } + if$ +} + +FUNCTION {format.in.ed.booktitle} +{ booktitle empty$ + { "" } + { editor empty$ + { "In " booktitle emphasize * } + { "In " format.editors * ", " * booktitle emphasize * } + if$ + } + if$ +} + +FUNCTION {empty.misc.check} +{ author empty$ title empty$ howpublished empty$ + month empty$ year empty$ note empty$ + and and and and and + key empty$ not and + { "all relevant fields are empty in " cite$ * warning$ } + 'skip$ + if$ +} + +FUNCTION {format.thesis.type} +{ type empty$ + 'skip$ + { pop$ + type "t" change.case$ + } + if$ +} + +FUNCTION {format.tr.number} +{ type empty$ + { "Technical Report" } + 'type + if$ + number empty$ + { "t" change.case$ } + { number tie.or.space.connect } + if$ +} + +FUNCTION {format.article.crossref} +{ key empty$ + { journal empty$ + { "need key or journal for " cite$ * " to crossref " * crossref * + warning$ + "" + } + { "In {\em " journal * "\/}" * } + if$ + } + { "In " key * } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {format.crossref.editor} +{ editor #1 "{vv~}{ll}" format.name$ + editor num.names$ duplicate$ + #2 > + { pop$ " et~al." * } + { #2 < + 'skip$ + { editor #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = + { " et~al." * } + { " and " * editor #2 "{vv~}{ll}" format.name$ * } + if$ + } + if$ + } + if$ +} + +FUNCTION {format.book.crossref} +{ volume empty$ + { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ + "In " + } + { "Volume" volume tie.or.space.connect + " of " * + } + if$ + editor empty$ + editor field.or.null author field.or.null = + or + { key empty$ + { series empty$ + { "need editor, key, or series for " cite$ * " to crossref " * + crossref * warning$ + "" * + } + { "{\em " * series * "\/}" * } + if$ + } + { key * } + if$ + } + { format.crossref.editor * } + if$ + " \cite{" * crossref * "}" * +} + +FUNCTION {format.incoll.inproc.crossref} +{ editor empty$ + editor field.or.null author field.or.null = + or + { key empty$ + { booktitle empty$ + { "need editor, key, or booktitle for " cite$ * " to crossref " * + crossref * warning$ + "" + } + { "In {\em " booktitle * "\/}" * } + if$ + } + { "In " key * } + if$ + } + { "In " format.crossref.editor * } + if$ + " \cite{" * crossref * "}" * +} + +% -------------------------------------------------------------- +% -------------------------------------------------------------- + +% urlbst... +% Functions for making hypertext links. +% In all cases, the stack has (link-text href-url) +% +% make 'null' specials +FUNCTION {make.href.null} +{ + pop$ +} +% make hypertex specials +FUNCTION {make.href.hypertex} +{ + "\special {html: }" * swap$ * + "\special {html:}" * +} +% make hyperref specials +FUNCTION {make.href.hyperref} +{ + "\href {" swap$ * "} {\path{" * swap$ * "}}" * +} +FUNCTION {make.href} +{ hrefform #2 = + 'make.href.hyperref % hrefform = 2 + { hrefform #1 = + 'make.href.hypertex % hrefform = 1 + 'make.href.null % hrefform = 0 (or anything else) + if$ + } + if$ +} + +% If inlinelinks is true, then format.url should be a no-op, since it's +% (a) redundant, and (b) could end up as a link-within-a-link. +FUNCTION {format.url} +{ inlinelinks #1 = url empty$ or + { "" } + { hrefform #1 = + { % special case -- add HyperTeX specials + urlintro "\url{" url * "}" * url make.href.hypertex * } + { urlintro "\url{" * url * "}" * } + if$ + } + if$ +} + +FUNCTION {format.eprint} +{ eprint empty$ + { "" } + { eprintprefix eprint * eprinturl eprint * make.href } + if$ +} + +FUNCTION {format.doi} +{ doi empty$ + { "" } + { doiprefix doi * doiurl doi * make.href } + if$ +} + +% Output a URL. We can't use the more normal idiom (something like +% `format.url output'), because the `inbrackets' within +% format.lastchecked applies to everything between calls to `output', +% so that `format.url format.lastchecked * output' ends up with both +% the URL and the lastchecked in brackets. +FUNCTION {output.url} +{ url empty$ + 'skip$ + { new.block + format.url output + format.lastchecked output + } + if$ +} + +FUNCTION {output.web.refs} +{ + new.block + output.url + addeprints eprint empty$ not and + { format.eprint output.nonnull } + 'skip$ + if$ + adddoiresolver doi empty$ not and + { format.doi output.nonnull } + 'skip$ + if$ +} + +% Wrapper for output.bibitem.original. +% If the URL field is not empty, set makeinlinelink to be true, +% so that an inline link will be started at the next opportunity +FUNCTION {output.bibitem} +{ outside.brackets 'bracket.state := + output.bibitem.original + inlinelinks url empty$ not and + { #1 'makeinlinelink := } + { #0 'makeinlinelink := } + if$ +} + +% Wrapper for fin.entry.original +FUNCTION {fin.entry} +{ output.web.refs % urlbst + makeinlinelink % ooops, it appears we didn't have a title for inlinelink + { setup.inlinelink % add some artificial link text here, as a fallback + "[link]" output.nonnull } + 'skip$ + if$ + bracket.state close.brackets = % urlbst + { "]" * } + 'skip$ + if$ + fin.entry.original +} + +% Webpage entry type. +% Title and url fields required; +% author, note, year, month, and lastchecked fields optional +% See references +% ISO 690-2 http://www.nlc-bnc.ca/iso/tc46sc9/standard/690-2e.htm +% http://www.classroom.net/classroom/CitingNetResources.html +% http://neal.ctstateu.edu/history/cite.html +% http://www.cas.usf.edu/english/walker/mla.html +% for citation formats for web pages. +FUNCTION {webpage} +{ output.bibitem + author empty$ + { editor empty$ + 'skip$ % author and editor both optional + { format.editors output.nonnull } + if$ + } + { editor empty$ + { format.authors output.nonnull } + { "can't use both author and editor fields in " cite$ * warning$ } + if$ + } + if$ + new.block + title empty$ 'skip$ 'setup.inlinelink if$ + format.title "title" output.check + inbrackets "online" output + new.block + year empty$ + 'skip$ + { format.date "year" output.check } + if$ + % We don't need to output the URL details ('lastchecked' and 'url'), + % because fin.entry does that for us, using output.web.refs. The only + % reason we would want to put them here is if we were to decide that + % they should go in front of the rather miscellaneous information in 'note'. + new.block + note output + fin.entry +} +% ...urlbst to here + + +FUNCTION {article} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + new.block + crossref missing$ + { journal emphasize "journal" output.check + format.vol.num.pages output + format.date "year" output.check + } + { format.article.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {book} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + crossref missing$ + { format.bvolume output + new.block + format.number.series output + new.sentence + publisher "publisher" output.check + address output + } + { new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {booklet} +{ output.bibitem + format.authors output + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + howpublished address new.block.checkb + howpublished output + address output + format.date output + new.block + note output + fin.entry +} + +FUNCTION {inbook} +{ output.bibitem + author empty$ + { format.editors "author and editor" output.check } + { format.authors output.nonnull + crossref missing$ + { "author and editor" editor either.or.check } + 'skip$ + if$ + } + if$ + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + crossref missing$ + { format.bvolume output + format.chapter.pages "chapter and pages" output.check + new.block + format.number.series output + new.sentence + publisher "publisher" output.check + address output + } + { format.chapter.pages "chapter and pages" output.check + new.block + format.book.crossref output.nonnull + } + if$ + format.edition output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {incollection} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + format.chapter.pages output + new.sentence + publisher "publisher" output.check + address output + format.edition output + format.date "year" output.check + } + { format.incoll.inproc.crossref output.nonnull + format.chapter.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {inproceedings} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + new.block + crossref missing$ + { format.in.ed.booktitle "booktitle" output.check + format.bvolume output + format.number.series output + format.pages output + address empty$ + { organization publisher new.sentence.checkb + organization output + publisher output + format.date "year" output.check + } + { address output.nonnull + format.date "year" output.check + new.sentence + organization output + publisher output + } + if$ + } + { format.incoll.inproc.crossref output.nonnull + format.pages output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {conference} { inproceedings } + +FUNCTION {manual} +{ output.bibitem + author empty$ + { organization empty$ + 'skip$ + { organization output.nonnull + address output + } + if$ + } + { format.authors output.nonnull } + if$ + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + author empty$ + { organization empty$ + { address new.block.checka + address output + } + 'skip$ + if$ + } + { organization address new.block.checkb + organization output + address output + } + if$ + format.edition output + format.date output + new.block + note output + fin.entry +} + +FUNCTION {mastersthesis} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + new.block + "Master's thesis" format.thesis.type output.nonnull + school "school" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {misc} +{ output.bibitem + format.authors output + title howpublished new.block.checkb + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title output + howpublished new.block.checka + howpublished output + format.date output + new.block + note output + fin.entry + empty.misc.check +} + +FUNCTION {phdthesis} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + new.block + "PhD thesis" format.thesis.type output.nonnull + school "school" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {dphilthesis} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + new.block + "DPhil thesis" format.thesis.type output.nonnull + school "school" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {proceedings} +{ output.bibitem + editor empty$ + { organization output } + { format.editors output.nonnull } + if$ + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.btitle "title" output.check + format.bvolume output + format.number.series output + address empty$ + { editor empty$ + { publisher new.sentence.checka } + { organization publisher new.sentence.checkb + organization output + } + if$ + publisher output + format.date "year" output.check + } + { address output.nonnull + format.date "year" output.check + new.sentence + editor empty$ + 'skip$ + { organization output } + if$ + publisher output + } + if$ + new.block + note output + fin.entry +} + +FUNCTION {techreport} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + new.block + format.tr.number output.nonnull + institution "institution" output.check + address output + format.date "year" output.check + new.block + note output + fin.entry +} + +FUNCTION {unpublished} +{ output.bibitem + format.authors "author" output.check + new.block + title empty$ 'skip$ 'setup.inlinelink if$ % urlbst + format.title "title" output.check + new.block + note "note" output.check + format.date output + fin.entry +} + +% -------------------------------------------------------------- +% -------------------------------------------------------------- + +FUNCTION {default.type} { misc } + +MACRO {jan} {"January"} + +MACRO {feb} {"February"} + +MACRO {mar} {"March"} + +MACRO {apr} {"April"} + +MACRO {may} {"May"} + +MACRO {jun} {"June"} + +MACRO {jul} {"July"} + +MACRO {aug} {"August"} + +MACRO {sep} {"September"} + +MACRO {oct} {"October"} + +MACRO {nov} {"November"} + +MACRO {dec} {"December"} + +MACRO {acmcs} {"ACM Computing Surveys"} + +MACRO {acta} {"Acta Informatica"} + +MACRO {cacm} {"Communications of the ACM"} + +MACRO {ibmjrd} {"IBM Journal of Research and Development"} + +MACRO {ibmsj} {"IBM Systems Journal"} + +MACRO {ieeese} {"IEEE Transactions on Software Engineering"} + +MACRO {ieeetc} {"IEEE Transactions on Computers"} + +MACRO {ieeetcad} + {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"} + +MACRO {ipl} {"Information Processing Letters"} + +MACRO {jacm} {"Journal of the ACM"} + +MACRO {jcss} {"Journal of Computer and System Sciences"} + +MACRO {scp} {"Science of Computer Programming"} + +MACRO {sicomp} {"SIAM Journal on Computing"} + +MACRO {tocs} {"ACM Transactions on Computer Systems"} + +MACRO {tods} {"ACM Transactions on Database Systems"} + +MACRO {tog} {"ACM Transactions on Graphics"} + +MACRO {toms} {"ACM Transactions on Mathematical Software"} + +MACRO {toois} {"ACM Transactions on Office Information Systems"} + +MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"} + +MACRO {tcs} {"Theoretical Computer Science"} + +READ + +FUNCTION {sortify} +{ purify$ + "l" change.case$ +} + +INTEGERS { len } + +FUNCTION {chop.word} +{ 's := + 'len := + s #1 len substring$ = + { s len #1 + global.max$ substring$ } + 's + if$ +} + +FUNCTION {sort.format.names} +{ 's := + #1 'nameptr := + "" + s num.names$ 'numnames := + numnames 'namesleft := + { namesleft #0 > } + { nameptr #1 > + { " " * } + 'skip$ + if$ + s nameptr "{vv{ } }{ll{ }}{ ff{ }}{ jj{ }}" format.name$ 't := + nameptr numnames = t "others" = and + { "et al" * } + { t sortify * } + if$ + nameptr #1 + 'nameptr := + namesleft #1 - 'namesleft := + } + while$ +} + +FUNCTION {sort.format.title} +{ 't := + "A " #2 + "An " #3 + "The " #4 t chop.word + chop.word + chop.word + sortify + #1 global.max$ substring$ +} + +FUNCTION {author.sort} +{ author empty$ + { key empty$ + { "to sort, need author or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.editor.sort} +{ author empty$ + { editor empty$ + { key empty$ + { "to sort, need author, editor, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { editor sort.format.names } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {author.organization.sort} +{ author empty$ + { organization empty$ + { key empty$ + { "to sort, need author, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { author sort.format.names } + if$ +} + +FUNCTION {editor.organization.sort} +{ editor empty$ + { organization empty$ + { key empty$ + { "to sort, need editor, organization, or key in " cite$ * warning$ + "" + } + { key sortify } + if$ + } + { "The " #4 organization chop.word sortify } + if$ + } + { editor sort.format.names } + if$ +} + +FUNCTION {presort} +{ type$ "book" = + type$ "inbook" = + or + 'author.editor.sort + { type$ "proceedings" = + 'editor.organization.sort + { type$ "manual" = + 'author.organization.sort + 'author.sort + if$ + } + if$ + } + if$ + " " + * + year field.or.null sortify + * + " " + * + title field.or.null + sort.format.title + * + #1 entry.max$ substring$ + 'sort.key$ := +} + +ITERATE {presort} + +SORT + +STRINGS { longest.label } + +INTEGERS { number.label longest.label.width } + +FUNCTION {initialize.longest.label} +{ "" 'longest.label := + #1 'number.label := + #0 'longest.label.width := +} + +FUNCTION {longest.label.pass} +{ number.label int.to.str$ 'label := + number.label #1 + 'number.label := + label width$ longest.label.width > + { label 'longest.label := + label width$ 'longest.label.width := + } + 'skip$ + if$ +} + +EXECUTE {initialize.longest.label} + +ITERATE {longest.label.pass} + +FUNCTION {begin.bib} +{ preamble$ empty$ + 'skip$ + { preamble$ write$ newline$ } + if$ + "\begin{thebibliography}{" longest.label * "}" * write$ newline$ +} + +EXECUTE {begin.bib} + +EXECUTE {init.urlbst.variables} +EXECUTE {init.state.consts} + +ITERATE {call.type$} + +FUNCTION {end.bib} +{ newline$ + "\end{thebibliography}" write$ newline$ +} + +EXECUTE {end.bib} diff --git a/dissertation.tex b/dissertation.tex index 6ca7384..684a7b8 100644 --- a/dissertation.tex +++ b/dissertation.tex @@ -28,11 +28,11 @@ \usepackage{graphicx} \usepackage{listings} \usepackage{multirow} -\usepackage{natbib} +\usepackage[square,numbers]{natbib} \usepackage{parskip} \usepackage{setspace} \usepackage{tabularx} -\usepackage{hyperref} +\usepackage[hidelinks]{hyperref} \usepackage{verbatim} \usepackage{xspace} @@ -102,31 +102,31 @@ The question of what makes an operating system has been asked many times. There \texttt{ipc} & Oct 2006 & \citep{korotaev_patch_2006} - & 2.6.19 & \citep{noauthor_linux_2006} + & 2.6.19 & \citep{linux_kernel_newbies_editors_linux_2006} & & \\ \texttt{uts} & Oct 2006 & \citep{hallyn_patch_2006} - & 2.6.19 & \citep{noauthor_linux_2006} + & 2.6.19 & \citep{linux_kernel_newbies_editors_linux_2006} & & \\ \texttt{user} & Jul 2007 & \citep{le_goater_user_2007} - & 2.6.23 & \citep{noauthor_linux_2007} + & 2.6.23 & \citep{linux_kernel_newbies_editors_linux_2007} & 2021-21284 & \\ \texttt{network} & Oct 2007 & \citep{biederman_net_2007} - & 2.6.24 & \citep{noauthor_linux_2008} + & 2.6.24 & \citep{linux_kernel_newbies_editors_linux_2008} & 2011-2189 & \\ \texttt{pid} & Oct 2006 & \citep{bhattiprolu_patch_2006} - & 2.6.24 & \citep{noauthor_linux_2008} + & 2.6.24 & \citep{linux_kernel_newbies_editors_linux_2008} & 2019-20794 & \\ @@ -138,7 +138,7 @@ The question of what makes an operating system has been asked many times. There \texttt{time} & Nov 2019 & \citep{vagin_ns_2020} - & 5.6 & \citep{noauthor_linux_2020} + & 5.6 & \citep{linux_kernel_newbies_editors_linux_2020} & & @@ -183,7 +183,7 @@ The previous methods shown each suffer from knowing what their initial privilege While the methods looked at until now of privilege separation by process and time are supported by all Unices, object capabilities are a more niche system. Capsicum added object capabilities and was included in FreeBSD 10, released in January 2014 \citep{watson_capsicum_2010}. These capabilities may be shared between processes as with file descriptors. Capability mode removes access to all global namespaces from a process, allowing only operations on capabilities to continue. These capabilities are commonly those opened before the switch to capability mode, but they can also be sent and received (as file descriptors) or converted from a capability with more privilege to a capability with less. -Although capabilities still require some additional work to ensure that only intentional capabilities remain accessible when entering capability mode, they come a lot closer to easy deprivileging than the previous solutions. However, their adoption remains limited at this point. They are unavailable in the latest Linux kernel release (5.17.7) at the time of writing. +Although object capabilities still require some additional work to ensure that only intentional capabilities remain accessible when entering capability mode, they come a lot closer to easy deprivileging than the previous solutions. However, their adoption remains limited at this point. They are unavailable in the latest Linux kernel release (5.17.7) at the time of writing, and there are no plans for their adoption. \section{Privilege separation by perspective} @@ -197,39 +197,49 @@ This work focuses on the application of namespaces to more conventional privileg \chapter{Entering the Void} \label{chap:entering-the-void} -Isolating parts of a Linux system from the view of certain processes is achieved by using namespaces. Namespaces are commonly used to provide isolation in the context of containers, which provide the appearance of an isolated complete Linux environment to contained processes. Instead, with Void Processes, we target complete isolation. Rather than using namespaces to provide a view of an alternate full Linux system, they are used to provide a view of a system that is as minimal as possible, while still sitting atop the Linux kernel. In this section each namespace available in Linux is detailed, including how to take a fresh namespace of each kind and completely empty it for a Void Process. Chapter \ref{chap:filling-the-void} goes on to explain how necessary features for applications are added back in. +Isolating parts of a Linux system from the view of certain processes is achieved using namespaces. Namespaces are commonly used to provide isolation in the context of containers, which provide the appearance of an isolated Linux system to contained processes. Instead, with Void Processes, we use namespaces to provide a view of a system that is as minimal as possible, while still sitting atop the Linux kernel. In this chapter each namespace available in Linux 5.15 LTS is discussed. The objects each namespace protects are presented and security vulnerabilities discussed. Then the method for entering a void with each namespace is given along with a discussion of the difficulties associated with this in current Linux. Chapter \ref{chap:filling-the-void} goes on to explain how necessary features for applications are added back in. The full set of namespaces are represented in Table \ref{tab:namespaces}, in chronological order. The chronology of these is important in understanding the thought process behind some of the design decisions. The ease of creating an empty namespace varies massively, as although adding namespaces shared the goal of containerisation, they were completed by many different teams of people over a number of years. Some namespaces maintain strong connections to their parent, while others are created with absolute separation. We start with those that are most trivial to add, working up to the namespaces most intensely linked to their parents. \section{ipc namespaces} \label{sec:voiding-ipc} -IPC namespaces isolate two mechanisms that Linux provides for IPC which aren't controlled by the filesystem. System V IPC and POSIX message queues are each accessed in a global namespace of keys. This has created issues in the past with attempting to run multiple instances of PostgreSQL on a single machine, as both instances tried to create a System V IPC entry with the same key [CN]. IPC namespaces solve this effectively for containers by creating a new scoped namespace. Processes are a member of one and only one IPC namespace, allowing the familiar global key APIs. IPC namespaces are optimal for creating Void Processes. From the manual page \citep{free_software_foundation_ipc_namespaces7_2021}: +IPC namespaces isolate two mechanisms that Linux provides for IPC which aren't controlled by the filesystem. System V IPC and POSIX message queues are each accessed in a global namespace of keys. This has created issues in the past with attempting to run multiple instances of PostgreSQL on a single machine, as both instances use System V IPC objects which collide \citep[§4.3]{barham_xen_2003}. IPC namespaces solve this effectively for containers by creating a new scoped namespace. Processes are a member of one and only one IPC namespace, allowing the familiar global key APIs. + +IPC namespaces are optimal for creating Void Processes. From the manual page \citep{free_software_foundation_ipc_namespaces7_2021}: \say{Objects created in an IPC namespace are visible to all other processes that are members of that namespace, but are not visible to processes in other IPC namespaces.} This provides exactly the correct semantics for a Void Process. IPC objects are visible within a namespace if and only if they are created within that namespace. Therefore, a new namespace is entirely empty, and no more work need be done. +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} + \section{uts namespaces} \label{sec:voiding-uts} -UTS namespaces provide isolation of the hostname and domain name of a system between processes. Similarly to IPC namespaces, all processes in the same namespace see the same results for each of these values. This is useful when creating containers. If unable to hide the hostname, each container would look like the same machine. Unlike IPC namespaces, UTS namespaces are copy-on-write. Each of these values in the child is initialised as the same as the parent. +UTS namespaces provide isolation of the hostname and domain name of a system between processes. Similarly to IPC namespaces, all processes in the same namespace see the same results for each of these values. This is useful when creating containers. If unable to hide the hostname, each container would look like the same machine. Unlike IPC namespaces, UTS namespaces are inherit their values. Each of the hostname and domain name in the new namespace is initialised to the values of the parent namespace. -As the copied value does give information about the world outside of the Void Process, slightly more must be done than placing the process in a new namespace. Fortunately this is easy for UTS namespaces, as the host name and domain name can be set to constants, removing any link to the parent. +As the inherited value does give information about the world outside of the Void Process, slightly more must be done than placing the process in a new namespace. Fortunately this is easy for UTS namespaces, as the host name and domain name can be set to constants, removing any link to the parent. Although the implementation of this is trivial, it highlights how easy the information passing elements of each namespace are to miss if manually implementing isolation with namespaces. + +Searching the list of released CVEs for both "clock`` and "time linux`` (time itself revealed significantly too many results to parse) shows no vulnerabilities in the time subsystem on Linux, or the time namespaces themselves. This supports not including time namespaces at this stage, as their range is very limited, particularly in terms of isolation from vulnerabilities. \section{time namespaces} \label{sec:voiding-time} -Time namespaces are the final namespace added at the time of writing, added in kernel version 5.6 \citep{noauthor_linux_2020}. The motivation for adding time namespaces is given in the manual page \citep{free_software_foundation_time_namespaces7_2021}: +Time namespaces are the final namespace added at the time of writing, added in kernel version 5.6 \citep{linux_kernel_newbies_editors_linux_2020}. The motivation for adding time namespaces is given in the manual page \citep{free_software_foundation_time_namespaces7_2021}: \say{The motivation for adding time namespaces was to allow the monotonic and boot-time clocks to maintain consistent values during container migration and checkpoint/restore.} -That is, time namespaces virtualise the appearance of system uptime to processes, rather than attempting to virtualise the wall clock time. This is important for processes that depend on time in primarily one situation: migration. If an uptime dependent process is migrated from a machine that has been up for a week to a machine that was booted a minute ago, the guarantees provided by the clocks \texttt{CLOCK\_MONOTONIC} and \texttt{CLOCK\_BOOTTIME} no longer hold. This results in time namespaces having very limited usefulness in a system that does not support migration, such as the one presented here. Perhaps randomised offsets would hide some information about the system, but the usefulness is limited. Time namespaces are thus avoided in this implementation. +That is, time namespaces virtualise the appearance of system uptime to processes. They do not attempt to virtualise wall clock time. This is important for processes that depend on time in primarily one situation: migration. If an uptime dependent process is migrated from a machine that has been up for a week to a machine that was booted a minute ago, the guarantees provided by the clocks \texttt{CLOCK\_MONOTONIC} and \texttt{CLOCK\_BOOTTIME} no longer hold. This results in time namespaces having very limited usefulness in a system that does not support migration, such as the one presented here. Perhaps randomised offsets would hide some information about the system, but the usefulness is limited. Time namespaces are thus avoided in this implementation. + +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} \section{network namespaces} \label{sec:voiding-net} -Similarly to IPC, they present the optimal namespace for running a Void Process. Creating a new network namespace immediately creates a namespace containing only a local loopback adapter. This means that the new network namespace has no link whatsoever to the creating network namespace, only supporting internal communication. To add a link, one can create a virtual Ethernet pair with one adapter in each namespace (see Figure \ref{fig:virtual-ethernet}). Alternatively, one can create a Wireguard adapter with sending and receiving sockets in one namespace and the VPN adapter in another \citep[§7.3]{donenfeld_wireguard_2017}. These methods allow for very high levels of separation while still maintaining access to the primary resource - the Internet or wider network. +Network namespaces on Linux isolate the system resources related to networking. These include network interfaces themselves, IP routing tables, firewall rules and the \texttt{/proc/net} directory. This level of isolation allows a network stack that operates completely independently to exist on a single kernel. + +Similarly to IPC, network namespaces present the optimal namespace for running a Void Process. Creating a new network namespace immediately creates a namespace containing only a local loopback adapter. This means that the new network namespace has no link whatsoever to the creating network namespace, only supporting internal communication. To add a link, one can create a virtual Ethernet pair with one adapter in each namespace (Figure \ref{fig:virtual-ethernet}). Alternatively, one can create a Wireguard adapter with sending and receiving sockets in one namespace and the VPN adapter in another \citep[§7.3]{donenfeld_wireguard_2017}. These methods allow for very high levels of separation while still maintaining access to the primary resource - the Internet or wider network. Further, this design places the management of how connected a namespace is to the parent in user-space. This is a significant difference compared to some of the namespaces discussed later in this chapter. \begin{figure} \begin{minipage}{.45\textwidth} @@ -269,34 +279,35 @@ PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data. \label{fig:virtual-ethernet} \end{figure} +Network namespaces are also the first mentioned to control access to \texttt{procfs}. \texttt{/proc} holds a pseudo-filesystem which controls access to many of the kernel data structures that aren't accessed by system calls. Seeing the intended behaviour here requires remounting \texttt{/proc}, which must be done with extreme care so as not to overwrite it for every other process. In a Void Process this is handled by automatically voiding the mount namespace, meaning that this does not need to be intentionally taken care of. + +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} \section{pid namespaces} \label{sec:voiding-pid} -PID namespaces create a mapping from the process IDs inside the namespace to process IDs in the parent namespace. This continues until processes reach the top-level PID namespace. This isolation behaviour is different to that of some other namespaces, as each process within the namespace represents a process in the parent namespace too, albeit with different identifiers. +PID namespaces create a mapping from the process IDs inside the namespace to process IDs in the parent namespace. This continues until processes reach the top-level, named init, PID namespace. This isolation behaviour is different to that of the namespaces discussed thus far, as each process within the namespace represents a process in the parent namespace too, albeit with different identifiers. -Although PID namespaces work quite well for creating a Void Process from the perspective of the inside process, some care must be taken in the implementation, as the actions of PID namespaces are highly affected by others. Some examples of this slightly unusual behaviour are shown in Listing \ref{lst:unshare-pid}. +As with network namespaces, PID namespaces have a significant effect on \texttt{/proc}. Further, they cause some unusual behaviour regarding the PID 1 (init) process in the new namespace. These behaviours are shown in Listing \ref{lst:unshare-pid}. The first behaviour shown is that an \texttt{unshare(CLONE\_PID)} call followed immediately by an \texttt{exec} does not create a working shell. The reason for this is that the first process created in the new namespace is given PID 1 and acts as an init process. That is, whichever process the shell spawns first becomes the init process of the namespace, and when that process dies, the namespace can no longer create new processes. This behaviour is avoided by either calling \texttt{unshare(2)} followed by \texttt{fork(2)}, or utilising \texttt{clone(2)} instead, both of which ensure that the correct process is created first in the new namespace. The \texttt{unshare(1)} binary provides a fork flag to solve this, while the implementation of the Void Orchestrator uses \texttt{clone(2)} which has the semantics of combining the two into a single syscall. -The first behaviour shown is that an \texttt{unshare(CLONE\_PID)} call followed immediately by an \texttt{exec} does not have the desired behaviour. The reason for this is that the first process created in the new namespace is given PID 1 and acts as an init process. That is, whichever process the shell spawns first becomes the init process of the namespace, and when that process dies, the namespace can no longer create new processes. This behaviour is avoided by either calling \texttt{unshare(2)} followed by \texttt{fork(2)}, or utilising \texttt{clone(2)} instead. The \texttt{unshare(1)} binary provides a fork flag to solve this, while the implementation of the Void Orchestrator uses \texttt{clone(2)} which has the semantics of combining the two into a single syscall. - -Secondly, we see that even in a shell that appears to be working correctly, processes from outside of the new PID namespace are still visible. This behaviour occurs because the mount of \texttt{/proc} visible to the process in the new PID namespace is the same as the init process. This is solved by remounting \texttt{/proc}, available to \texttt{unshare(3)} with the \texttt{---mount-proc} flag. Care must be taken that this mount is completed in a new mount namespace, or else processes outside of the PID namespace will be affected. The Void Orchestrator again avoids this by voiding the mount namespace entirely, so any access to proc must be either bound to outside the namespace deliberately or freshly mounted. +Secondly, we see that even in a shell that appears to be working correctly, processes from outside of the new PID namespace are still visible. This behaviour occurs because the mount of \texttt{/proc} visible to the process in the new PID namespace is the same as the init process. This is solved by remounting \texttt{/proc}, available to \texttt{unshare(3)} with the \texttt{---mount-proc} flag. Care must be taken that this mount is completed in a new mount namespace, or else processes outside of the PID namespace will be affected. The Void Orchestrator again avoids this by voiding the mount namespace entirely, meaning that any access to \texttt{procfs} must be either freshly mounted or bound to outside the namespace intentionally. \lstset{caption={Unshare behaviour with PID namespaces, with and without forking and remounting proc.}} \begin{lstlisting}[float,label={lst:unshare-pid}] -$ unshare -p +$ unshare --pid -bash: fork: Cannot allocate memory # (new shell in new pid namespace) # ps ax | tail -n 3 -bash: fork: Cannot allocate memory -$ unshare --fork -p +$ unshare --fork --pid # (new shell in new pid namespace) # ps ax | tail -n 3 2645 ? I 0:00 [kworker/...] 2689 pts/1 R+ 0:00 ps ax 2690 pts/1 S+ 0:00 tail -n 2 -$ unshare --fork --mount-proc -p +$ unshare --fork --mount-proc --pid # (new shell in new pid namespace) # ps ax | tail -n 3 1 pts/1 S 0:00 -bash @@ -305,6 +316,8 @@ $ unshare --fork --mount-proc -p \end{lstlisting} +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} + \section{mount namespaces} \label{sec:voiding-mount} @@ -471,6 +484,8 @@ If, instead, one wishes to continue running the existing binary, this is possibl The API is particularly unfriendly to creating a Void Process. The creation of mount namespaces is copy-on-write, and many filesystems are mounted shared. This means that they propagate changes back through namespace boundaries. As the mount namespace does not allow for creating an entirely empty root, extra care must be taken in separating processes. The method taken in this system is mounting a new \texttt{tmpfs} file system in a new namespace, which doesn't propagate to the parent, and using the \texttt{pivot\_root(8)} command to make this the new root. By pivoting to the \texttt{tmpfs}, the old root exists as the only reference in the otherwise empty \texttt{tmpfs}. Finally, after ensuring the old root is set to \texttt{MNT\_PRIVATE} to avoid propagation, the old root can be lazily detached. This allows the binary from the parent namespace, the shim in this case, to continue running correctly. Any new processes only have access to the materials in the empty \texttt{tmpfs}. This new \texttt{tmpfs} never appears in the parent namespace, separating the Void Process effectively from the parent namespace. +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} + \section{user namespaces} \label{sec:voiding-user} @@ -480,6 +495,8 @@ Similarly to many other namespaces, user namespaces suffer from needing to limit To create an effective Void Process content must be written to the files \texttt{/proc/[pid]/uid\_map} and \texttt{/proc/[pid]/gid\_map}. In the case of the shim uid 0 and gid 0 are mapped to the creating user. This is done first such that the remaining stages in creating a Void Process can have root capabilities within the user namespace - this is not possible prior to writing to these files. Otherwise, \texttt{CLONE\_NEWUSER} combines effectively with other namespace flags, ensuring that the user namespace is created first. This enables the other namespaces to be created without additional permissions. +\todo{Discuss how intense the restrictions on who can do what are. Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} + \section{cgroup namespaces} \label{sec:voiding-cgroup} @@ -491,11 +508,20 @@ cgroup namespaces provide limited isolation of the cgroup hierarchy between proc \item Unshare the cgroup namespace. \end{enumerate} -This process excludes the cgroup namespace from the initial \texttt{clone(3)} call, as the cloned process must be moved before creating the new namespace. By following this sequence of calls, the process in the void can only see the leaf which contains itself and nothing else, limiting access to the host system. This is the approach taken in this piece of work. This presents the one point where running the shim with ambient authority rather than high capabilities is potentially limiting. In order to move the process into a leaf the shim must have sufficient authority to modify the cgroup hierarchy. On systemd these processes will be launched underneath a user slice and will have sufficient permissions, but this may vary between systems. This leaves cgroups the most weakly implemented namespace at present. +This process excludes the cgroup namespace from the initial \texttt{clone(3)} call, as the cloned process must be moved before creating the new namespace. By following this sequence of calls, the process in the void can only see the leaf which contains itself and nothing else, limiting access to the host system. This is the approach taken in this piece of work. Running the shim with ambient autrhoirty here presents an issue, as the cgroup hierarchy relies on discretionary access control. In order to move the process into a leaf the shim must have sufficient authority to modify the cgroup hierarchy. On systemd these processes will be launched underneath a user slice and will have sufficient permissions, but this may vary between systems. This leaves cgroups the most weakly implemented namespace at present. -Although good isolation of the host system from the Void Process is provided, the Void Process is in no way hidden from the host. There exists only one cgroups v2 hierarchy on a system (cgroups v1 are ignored for clarity), where resources are delegated through each. This means that all processes contained within the hierarchy must appear in the primary hierarchy, such that the distribution of the single set of system resources can be centrally controlled. This behaviour is similar to the aforementioned pid namespaces, where each process has a distinct PID in each of its parents, but does show up in each. Hiding from the host has little value as a root user there can inspect each namespace manually. +Although good isolation of the host system from the Void Process is provided, the Void Process is in no way hidden from the host. There exists only one cgroups v2 hierarchy on a system (cgroups v1 are ignored for clarity), where resources are delegated through each. This means that all processes contained within the hierarchy must appear in the init hierarchy, such that the distribution of the single set of system resources can be centrally controlled. This behaviour is similar to the aforementioned pid namespaces, where each process has a distinct PID in each of its parents, but does show up in each. + +There are two problems when working with cgroups namespaces in user-space: needing sufficient discretionary access control, and leaving the control of individual application processes in a global namespace. An alternative kernel design would increase the utility by solving both of these problems. A process in a new cgroups namespace could instead create a detached hierarchy with the process as a leaf of the root and full permissions in the user-namespace that created it. The main cgroups hierarchy could then still see a single application to control, while the application itself would have full access over sharing its resources. This presents the ability for mechanisms of managing cgroups to clash between the namespaces, as the outer namespace would now have control over what resources are delegated to the application rather than each process in the application. Such a system would also provide improved behaviour over the current, which requires a delegation flag to be handed to the manager informing it to go no further down the tree. This would be significantly better enforced with namespaces. That is, the main namespace could be handled by \texttt{systemd}, while the \texttt{/docker} namespace could be internally managed by docker. This would allow \texttt{systemd} to move the \texttt{/docker} namespace around as required, with no awareness of the choices made internally. + +\todo{Add vulnerabilities protected from. Discuss lack of vulnerabilities relating to the namespace itself.} + +\section{Summary} + +In this chapter I presented the 8 namespaces available in Linux 5.15. What each namespace protects against, how to completely empty each created namespace, and the constraints in doing so were presented. For cgroup and mount namespaces, alternative designs that increase the usability of the namespaces were discussed. + +Now that the motivation for emptying namespaces has been shown with the avoidance of vulernabilities, facilities to re-expose some of the system must be introduced in order to make useful applications. The methods for reintroducing parts of the system are given in Chapter \ref{chap:filling-the-void}, before demonstrating how to build useful applications in Chapter \ref{chap:building-apps}. -An alternative implementation that would make implementing with the cgroups namespace easier would be one that condenses all of the processes in the sea groups name space into one parent process in the parent main space. This would have the effect of hiding underlying processes from the parent name space, while still allowing control over the sea groups tree as a whole. It would further provide better isolation of the child, as a newly spawned cgroups space would show an empty route that only contains the child process. This would also allow more effective interaction with user namespaces, as the child namespace would only have control over itself, allowing for full control without risking the rest of the tree. This is opposed to the current limited view of the cgroups tree, which appears to have limited usefulness. \chapter{Filling the Void} \label{chap:filling-the-void} @@ -634,11 +660,15 @@ The primary future work to increase the utility of void processes is better perf Dynamic linking works correctly under the shim, however, it currently requires a high level of manual input. Given that the threat model in Section \ref{section:threat-model} specifies trusted binaries, it is feasible to add a pre-spawning phase which appends read-only libraries to the specification for each spawned process automatically before creating appropriate voids. This would allow anything which can link correctly on the host system to link correctly in Void Processes. +\subsection{Dynamic requests} + +In Section \ref{sec:filling-net} a system was presented for dynamically requesting statically specified network sockets. This system of requests back to the shim could be extended to more dynamic behaviour for software that requires it. Some software, particularly that which interfaces with the user, is not able to statically specify their requirements before starting. By specifying instead a range of requests which are legal then making them dynamically, Void Processes would be able to support more software. + \appendix \singlespacing -\bibliographystyle{plainnat} +\bibliographystyle{PhDbiblio-url} \bibliography{references} \end{document} diff --git a/references.bib b/references.bib index 2660507..2b40e68 100644 --- a/references.bib +++ b/references.bib @@ -1,4 +1,65 @@ +@misc{linux_kernel_newbies_editors_linux_2020, + type = {Wiki}, + title = {Linux {Version} 5.6 {Changelog}}, + url = {https://kernelnewbies.org/Linux_5.6}, + urldate = {2022-04-20}, + journal = {Linux Kernel Newbies}, + author = {Linux Kernel Newbies Editors}, + month = mar, + year = {2020}, +} + +@misc{linux_kernel_newbies_editors_linux_2008, + type = {Wiki}, + title = {Linux {Version} 2.6.24 {Changelog}}, + url = {https://kernelnewbies.org/Linux_2_6_24}, + urldate = {2022-04-20}, + journal = {Linux Kernel Newbies}, + author = {Linux Kernel Newbies Editors}, + month = jan, + year = {2008}, +} + +@misc{linux_kernel_newbies_editors_linux_2007, + type = {Wiki}, + title = {Linux {Version} 2.6.23 {Changelog}}, + url = {https://kernelnewbies.org/Linux_2_6_23}, + urldate = {2022-04-20}, + journal = {Linux Kernel Newbies}, + author = {Linux Kernel Newbies Editors}, + month = oct, + year = {2007}, +} + +@misc{linux_kernel_newbies_editors_linux_2006, + type = {Wiki}, + title = {Linux {Version} 2.6.19 {Changelog}}, + url = {https://kernelnewbies.org/Linux_2_6_19}, + urldate = {2022-04-20}, + journal = {Linux Kernel Newbies}, + author = {Linux Kernel Newbies Editors}, + month = nov, + year = {2006}, +} + +@inproceedings{accetta_mach_1986, + title = {Mach: {A} {New} {Kernel} {Foundation} for {UNIX} {Development}}, + booktitle = {Proceedings of the {USENIX} {Summer} {Conference}, {Altanta}, {GA}, {USA}, {June} 1986}, + publisher = {USENIX Association}, + author = {Accetta, Michael J. and Baron, Robert V. and Bolosky, William J. and Golub, David B. and Rashid, Richard F. and Tevanian, Avadis and Young, Michael}, + year = {1986}, + pages = {93--113}, +} + +@article{accetta_mach_nodate, + title = {Mach: {A} {New} {Kernel} {Foundation} {For} {UNIX} {Development}}, + abstract = {Mach is a multiprocessor operating system kernel and environment under development at Carnegie Mellon University. Mach provides a new foundation for UNIX development that spans networks of uniprocessors and multiprocessors. This paper describes Mach and the motivations that led to its design. Also described are some of the details of its implementation and current status.}, + language = {en}, + author = {Accetta, Mike and Baron, Robert and Bolosky, William and Golub, David and Rashid, Richard and Tevanian, Avadis and Young, Michael}, + pages = {16}, +} + @misc{the_openbsd_foundation_openssh_2022, title = {{OpenSSH} 8.9 {Release} {Notes}}, url = {https://www.openssh.com/txt/release-8.9}, @@ -200,46 +261,6 @@ namespaces. This infrastructure is: year = {2006}, } -@misc{noauthor_linux_2020, - type = {Wiki}, - title = {Linux {Version} 5.6 {Changelog}}, - url = {https://kernelnewbies.org/Linux_5.6}, - urldate = {2022-04-20}, - journal = {Linux Kernel Newbies}, - month = mar, - year = {2020}, -} - -@misc{noauthor_linux_2008, - type = {Wiki}, - title = {Linux {Version} 2.6.24 {Changelog}}, - url = {https://kernelnewbies.org/Linux_2_6_24}, - urldate = {2022-04-20}, - journal = {Linux Kernel Newbies}, - month = jan, - year = {2008}, -} - -@misc{noauthor_linux_2007, - type = {Wiki}, - title = {Linux {Version} 2.6.23 {Changelog}}, - url = {https://kernelnewbies.org/Linux_2_6_23}, - urldate = {2022-04-20}, - journal = {Linux Kernel Newbies}, - month = oct, - year = {2007}, -} - -@misc{noauthor_linux_2006, - type = {Wiki}, - title = {Linux {Version} 2.6.19 {Changelog}}, - url = {https://kernelnewbies.org/Linux_2_6_19}, - urldate = {2022-04-20}, - journal = {Linux Kernel Newbies}, - month = nov, - year = {2006}, -} - @misc{torvalds_linux_2002, title = {Linux {Kernel} {Version} 2.5.2 {Changelog}}, url = {https://mirrors.edge.kernel.org/pub/linux/kernel/v2.5/ChangeLog-2.5.2},