Remove Subversion metadata

These files were committed accidentally.
This commit is contained in:
Florian Weimer 2013-04-02 14:25:44 +02:00
parent c2251f3022
commit c861995515
164 changed files with 0 additions and 21568 deletions

View file

@ -1,47 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 74
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema
END
htmltblx.rnc
K 25
svn:wc:ra_dav:version-url
V 87
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/htmltblx.rnc
END
docbook.rnc
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/docbook.rnc
END
dbhierx.rnc
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/dbhierx.rnc
END
dbpoolx.rnc
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/dbpoolx.rnc
END
README
K 25
svn:wc:ra_dav:version-url
V 81
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/README
END
calstblx.rnc
K 25
svn:wc:ra_dav:version-url
V 87
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/calstblx.rnc
END
dbnotnx.rnc
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/291/defensive-coding/trunk/docbook-schema/dbnotnx.rnc
END

View file

@ -1,266 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/docbook-schema
https://svn.devel.redhat.com/repos/product-security
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
htmltblx.rnc
file
2013-01-10T17:17:56.371857Z
21e841469e23f4a9b92aeb9a459d8b53
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
7626
docbook.rnc
file
2013-01-10T17:17:56.371857Z
d61460c3a0f4539fe94f63a6caf75ca0
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
9150
dbhierx.rnc
file
2013-01-10T17:17:56.372857Z
31ef633724ef8f8f23159fefdd55a226
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
43061
dbpoolx.rnc
file
2013-01-10T17:17:56.372857Z
ac617851f210fca2d4e1ef9340383bae
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
167400
README
file
2013-01-10T17:17:56.372857Z
6aa0b595c5fce9b9bb2d35cceac7411f
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
154
calstblx.rnc
file
2013-01-10T17:17:56.372857Z
3477bdcf199a10c15051d27b2b6e2d89
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
6329
dbnotnx.rnc
file
2013-01-10T17:17:56.372857Z
8bf718c756c864dcb6c90fc81f537c50
2012-12-14T10:14:43.664315Z
291
fweimer@REDHAT.COM
2588

View file

@ -1,3 +0,0 @@
This directory contains Relax NG Compact schema files, for use with
Emacs. These files were download from http://www.docbook.org/rng/4.5/
on 2012-07-16.

View file

@ -1,164 +0,0 @@
# ......................................................................
# DocBook CALS Table Model V4.5 ........................................
# File calstblx.mod ....................................................
# Copyright 1992-2002 HaL Computer Systems, Inc.,
# O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software
# Corporation, Norman Walsh, Sun Microsystems, Inc., and the
# Organization for the Advancement of Structured Information
# Standards (OASIS).
#
# This DTD is based on the CALS Table Model
# PUBLIC "-//USA-DOD//DTD Table Model 951010//EN"
#
# $Id: calstblx.dtd 6340 2006-10-03 13:23:24Z nwalsh $
#
# Permission to use, copy, modify and distribute the DocBook DTD
# and its accompanying documentation for any purpose and without fee
# is hereby granted in perpetuity, provided that the above copyright
# notice and this paragraph appear in all copies. The copyright
# holders make no representation about the suitability of the DTD for
# any purpose. It is provided "as is" without expressed or implied
# warranty.
#
# If you modify the DocBook DTD in any way, except for declaring and
# referencing additional sets of general entities and declaring
# additional notations, label your DTD as a variant of DocBook. See
# the maintenance documentation for more information.
#
# Please direct all questions, bug reports, or suggestions for
# changes to the docbook@lists.oasis-open.org mailing list. For more
# information, see http://www.oasis-open.org/docbook/.
# ......................................................................
# This module contains the definitions for the CALS Table Model
# converted to XML. It has been modified slightly for use in the
# combined HTML/CALS models supported by DocBook V4.5.
# These definitions are not directly related to the table model, but are
# used in the default CALS table model and are usually defined elsewhere
# (and prior to the inclusion of this table module) in a CALS DTD.
# no if zero(s),
# yes if any other digits value
yesorno = string
titles = title?
# default for use in entry content
# The parameter entities as defined below provide the CALS table model
# as published (as part of the Example DTD) in MIL-HDBK-28001.
#
# These following declarations provide the CALS-compliant default definitions
# for these entities. However, these entities can and should be redefined
# (by giving the appropriate parameter entity declaration(s) prior to the
# reference to this Table Model declaration set entity) to fit the needs
# of the current application.
tbl.table-titles.mdl = titles
tbl.table-main.mdl = tgroup+ | graphic+
tbl.tgroup.mdl = colspec*, spanspec*, thead?, tfoot?, tbody
tbl.tgroup.att = attribute tgroupstyle { text }?
tbl.row.mdl = (entry | entrytbl)+
tbl.entrytbl.mdl = colspec*, spanspec*, thead?, tbody
# ===== Element and attribute declarations follow. =====
# doc:A formal table in a document.
table = element table { table.attlist, tbl.table.mdl }
table.attlist &=
attribute frame { tbl.frame.attval }?,
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
tbl.table.att,
bodyatt,
secur
# doc:A wrapper for the main content of a table, or part of a table.
tgroup = element tgroup { tgroup.attlist, tbl.tgroup.mdl }
tgroup.attlist &=
attribute cols { text },
tbl.tgroup.att,
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
attribute align { "left" | "right" | "center" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?,
secur
# doc:Specifications for a column in a table.
colspec = element colspec { colspec.attlist, empty }
colspec.attlist &=
attribute colnum { text }?,
attribute colname { text }?,
attribute colwidth { text }?,
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
attribute align { "left" | "right" | "center" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?
# doc:Formatting information for a spanned column in a table.
spanspec = element spanspec { spanspec.attlist, empty }
spanspec.attlist &=
attribute namest { text },
attribute nameend { text },
attribute spanname { text },
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
attribute align { "left" | "right" | "center" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?
# doc:A table header consisting of one or more rows.
thead = element thead { thead.attlist, tbl.hdft.mdl }
thead.attlist &=
attribute valign { "top" | "middle" | "bottom" }?,
secur
# doc:A table footer consisting of one or more rows.
tfoot = element tfoot { tfoot.attlist, tbl.hdft.mdl }
tfoot.attlist &=
attribute valign { "top" | "middle" | "bottom" }?,
secur
# doc:A wrapper for the rows of a table or informal table.
tbody = element tbody { tbody.attlist, tbl.tbody.mdl }
tbody.attlist &=
attribute valign { "top" | "middle" | "bottom" }?,
secur
# doc:A row in a table.
row = element row { row.attlist, tbl.row.mdl }
row.attlist &=
attribute rowsep { yesorno }?,
attribute valign { "top" | "middle" | "bottom" }?,
secur
# doc:A subtable appearing in place of an Entry in a table.
entrytbl = element entrytbl { entrytbl.attlist, tbl.entrytbl.mdl }
entrytbl.attlist &=
attribute cols { text },
tbl.tgroup.att,
attribute colname { text }?,
attribute spanname { text }?,
attribute namest { text }?,
attribute nameend { text }?,
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
attribute align { "left" | "right" | "center" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?,
secur
# doc:A cell in a table.
entry = element entry { entry.attlist, tbl.entry.mdl* }
entry.attlist &=
attribute colname { text }?,
attribute namest { text }?,
attribute nameend { text }?,
attribute spanname { text }?,
attribute morerows { text }?,
attribute colsep { yesorno }?,
attribute rowsep { yesorno }?,
attribute align { "left" | "right" | "center" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?,
attribute rotate { yesorno }?,
attribute valign { "top" | "middle" | "bottom" }?,
secur
# End of DocBook CALS Table Model V4.5 .................................
# ......................................................................

View file

@ -1,85 +0,0 @@
# ......................................................................
# DocBook notations module V4.5 ........................................
# File dbnotnx.mod .....................................................
# Copyright 1992-2004 HaL Computer Systems, Inc.,
# O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software
# Corporation, Norman Walsh, Sun Microsystems, Inc., and the
# Organization for the Advancement of Structured Information
# Standards (OASIS).
#
# $Id: dbnotnx.mod 6340 2006-10-03 13:23:24Z nwalsh $
#
# Permission to use, copy, modify and distribute the DocBook DTD
# and its accompanying documentation for any purpose and without fee
# is hereby granted in perpetuity, provided that the above copyright
# notice and this paragraph appear in all copies. The copyright
# holders make no representation about the suitability of the DTD for
# any purpose. It is provided "as is" without expressed or implied
# warranty.
#
# If you modify the DocBook DTD in any way, except for declaring and
# referencing additional sets of general entities and declaring
# additional notations, label your DTD as a variant of DocBook. See
# the maintenance documentation for more information.
#
# Please direct all questions, bug reports, or suggestions for
# changes to the docbook@lists.oasis-open.org mailing list. For more
# information, see http://www.oasis-open.org/docbook/.
# ......................................................................
# This module contains the notation declarations used by DocBook.
#
# In DTD driver files referring to this module, please use an entity
# declaration that uses the public identifier shown below:
#
# <!ENTITY % dbnotn PUBLIC
# "-//OASIS//ENTITIES DocBook Notations V4.5//EN"
# "dbnotnx.mod">
# %dbnotn;
#
# See the documentation for detailed information on the parameter
# entity and module scheme used in DocBook, customizing DocBook and
# planning for interchange, and changes made since the last release
# of DocBook.
local.notation.class = notAllowed
notation.class =
"BMP"
| "CGM-CHAR"
| "CGM-BINARY"
| "CGM-CLEAR"
| "DITROFF"
| "DVI"
| "EPS"
| "EQN"
| "FAX"
| "GIF"
| "GIF87a"
| "GIF89a"
| "JPG"
| "JPEG"
| "IGES"
| "PCX"
| "PIC"
| "PNG"
| "PS"
| "SGML"
| "TBL"
| "TEX"
| "TIFF"
| "WMF"
| "WPG"
| "SVG"
| "PDF"
| "SWF"
| "linespecific"
| local.notation.class
# WordPerfect Graphic format
# End of DocBook notations module V4.5 .................................
# ......................................................................

View file

@ -1,499 +0,0 @@
# ......................................................................
# DocBook XML DTD V4.5 .................................................
# File docbookx.dtd ....................................................
# Copyright 1992-2006 HaL Computer Systems, Inc.,
# O'Reilly & Associates, Inc., ArborText, Inc., Fujitsu Software
# Corporation, Norman Walsh, Sun Microsystems, Inc., and the
# Organization for the Advancement of Structured Information
# Standards (OASIS).
#
# See also http://docbook.org/specs/
#
# $Id: docbookx.dtd 6340 2006-10-03 13:23:24Z nwalsh $
#
# Permission to use, copy, modify and distribute the DocBook XML DTD
# and its accompanying documentation for any purpose and without fee
# is hereby granted in perpetuity, provided that the above copyright
# notice and this paragraph appear in all copies. The copyright
# holders make no representation about the suitability of the DTD for
# any purpose. It is provided "as is" without expressed or implied
# warranty.
#
# If you modify the DocBook DTD in any way, except for declaring and
# referencing additional sets of general entities and declaring
# additional notations, label your DTD as a variant of DocBook. See
# the maintenance documentation for more information.
#
# Please direct all questions, bug reports, or suggestions for
# changes to the docbook@lists.oasis-open.org mailing list. For more
# information, see http://www.oasis-open.org/docbook/.
# ......................................................................
# This is the driver file for V4.5 of the DocBook DTD.
# Please use the following formal public identifier to identify it:
#
# "-//OASIS//DTD DocBook XML V4.5//EN"
#
# For example, if your document's top-level element is Book, and
# you are using DocBook directly, use the FPI in the DOCTYPE
# declaration:
#
# <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
# "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
# [...]>
#
# Or, if you have a higher-level driver file that customizes DocBook,
# use the FPI in the parameter entity declaration:
#
# <!ENTITY % DocBookDTD PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
# "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
# %DocBookDTD;
#
# See the documentation for detailed information on the parameter
# entity and module scheme used in DocBook, customizing DocBook and
# planning for interchange, and changes made since the last release
# of DocBook.
# ......................................................................
# Enable SGML features .................................................
# ......................................................................
# Notation declarations ................................................
include "dbnotnx.rnc"
# ......................................................................
# ISO character entity sets ............................................
# euro sign, U+20AC NEW
# ......................................................................
# DTD modules ..........................................................
# Information pool ..............
include "dbpoolx.rnc"
# Redeclaration placeholder .....
# Document hierarchy ............
include "dbhierx.rnc"
start =
articleinfo
| honorific
| legalnotice
| procedure
| simplelist
| keycode
| refsynopsisdiv
| article
| phrase
| destructorsynopsis
| itemizedlist
| audioobject
| link
| sect1info
| xref
| glossaryinfo
| varname
| keywordset
| informalequation
| toc
| pagenums
| lot
| shortcut
| glosslist
| option
| bibliosource
| variablelist
| filename
| pob
| colgroup
| foreignphrase
| group
| substeps
| conftitle
| textobject
| menuchoice
| colspec
| contractsponsor
| tocback
| contractnum
| constant
| dedication
| inlineequation
| bibliographyinfo
| country
| glossseealso
| bridgehead
| mousebutton
| surname
| stepalternatives
| tertiaryie
| mediaobject
| msgentry
| fax
| initializer
| table
| task
| setinfo
| videodata
| bibliodiv
| issuenum
| phone
| state
| refsynopsisdivinfo
| member
| glossentry
| term
| msgtext
| tr
| errortype
| confdates
| inlinegraphic
| th
| segmentedlist
| remark
| preface
| structname
| publisher
| td
| oointerface
| refsection
| type
| taskrelated
| msgrel
| artpagenums
| bibliomixed
| revnumber
| firstterm
| seeie
| spanspec
| toclevel5
| trademark
| toclevel4
| toclevel3
| toclevel2
| indexentry
| toclevel1
| colophon
| methodparam
| sidebarinfo
| productnumber
| funcprototype
| inlinemediaobject
| refclass
| lotentry
| paramdef
| classsynopsisinfo
| qandaset
| footnote
| msglevel
| keysym
| citation
| simplemsgentry
| othercredit
| subjectset
| keycap
| orderedlist
| refmiscinfo
| blockinfo
| programlistingco
| abbrev
| sidebar
| informalfigure
| tip
| primaryie
| appendixinfo
| partintro
| glossdiv
| confgroup
| segtitle
| taskprerequisites
| street
| tbody
| caption
| markup
| setindex
| msgsub
| subscript
| orgname
| fieldsynopsis
| refname
| void
| sect5
| sect4
| sect3
| chapter
| sect2
| sect1
| modifier
| col
| orgdiv
| city
| bibliolist
| funcparams
| application
| \token
| imageobject
| literal
| funcsynopsis
| olink
| package
| collab
| seealsoie
| primary
| glossterm
| termdef
| area
| ackno
| function
| collabname
| lineannotation
| guisubmenu
| msgexplan
| errorname
| property
| synopfragmentref
| refentryinfo
| entry
| manvolnum
| synopsis
| emphasis
| appendix
| bookinfo
| contrib
| otheraddr
| copyright
| methodname
| email
| ooclass
| videoobject
| abstract
| firstname
| revremark
| glossdef
| guibutton
| informalexample
| screen
| errorcode
| command
| seriesvolnums
| refpurpose
| parameter
| equation
| tfoot
| code
| jobtitle
| sgmltag
| screenco
| holder
| isbn
| corpcredit
| biblioset
| part
| symbol
| row
| bibliomisc
| imagedata
| secondary
| classname
| callout
| screenshot
| bibliomset
| indexterm
| refsect3
| tocchap
| para
| refsect2
| refsect1
| date
| refdescriptor
| wordasword
| epigraph
| audiodata
| hardware
| confsponsor
| authorgroup
| warning
| authorinitials
| medialabel
| varlistentry
| authorblurb
| itermset
| refsect3info
| informaltable
| guimenuitem
| postcode
| subjectterm
| refnamediv
| note
| figure
| envar
| listitem
| methodsynopsis
| affiliation
| funcsynopsisinfo
| structfield
| blockquote
| keyword
| chapterinfo
| tertiary
| year
| subtitle
| personblurb
| refentry
| citebiblioid
| seglistitem
| bibliography
| msg
| constructorsynopsis
| refsect2info
| volumenum
| database
| funcdef
| uri
| graphicco
| biblioid
| msgmain
| printhistory
| glosssee
| beginpage
| glossary
| set
| highlights
| objectinfo
| tocpart
| guiicon
| revhistory
| seg
| see
| msgorig
| areaspec
| partinfo
| index
| sectioninfo
| refsectioninfo
| optional
| confnum
| replaceable
| refsect1info
| corpauthor
| step
| anchor
| arg
| mathphrase
| setindexinfo
| keycombo
| address
| cmdsynopsis
| computeroutput
| literallayout
| qandaentry
| sect5info
| bibliocoverage
| coref
| editor
| superscript
| personname
| pubsnumber
| graphic
| simplesect
| accel
| secondaryie
| biblioref
| publishername
| bibliorelation
| prefaceinfo
| revision
| screeninfo
| sbr
| example
| citetitle
| issn
| invpartnumber
| indexdiv
| sect4info
| corpname
| lineage
| ooexception
| reference
| revdescription
| title
| edition
| co
| msgaud
| guimenu
| shortaffil
| titleabbrev
| msginfo
| refmeta
| qandadiv
| mediaobjectco
| seealso
| exceptionname
| answer
| programlisting
| tgroup
| refentrytitle
| book
| errortext
| varargs
| sect3info
| citerefentry
| tasksummary
| quote
| othername
| prompt
| entrytbl
| interfacename
| acronym
| modespec
| msgset
| thead
| textdata
| userinput
| attribution
| footnoteref
| action
| tocentry
| tocfront
| author
| imageobjectco
| alt
| question
| ulink
| subject
| pubdate
| returnvalue
| label
| caution
| section
| systemitem
| referenceinfo
| sect2info
| calloutlist
| classsynopsis
| productname
| simpara
| synopfragment
| important
| interface
| releaseinfo
| formalpara
| areaset
| biblioentry
| indexinfo
| guilabel
# ......................................................................
# Other general entities ...............................................
# End of DocBook XML DTD V4.5 ..........................................
# ......................................................................

View file

@ -1,225 +0,0 @@
# ......................................................................
# DocBook XML HTML Table Module V4.5 ...................................
# File htmltblx.mod ....................................................
# Copyright 2003-2006 ArborText, Inc., Norman Walsh, Sun Microsystems,
# Inc., and the Organization for the Advancement of Structured Information
# Standards (OASIS).
#
# $Id: htmltblx.mod 6340 2006-10-03 13:23:24Z nwalsh $
#
# Permission to use, copy, modify and distribute the DocBook XML DTD
# and its accompanying documentation for any purpose and without fee
# is hereby granted in perpetuity, provided that the above copyright
# notice and this paragraph appear in all copies. The copyright
# holders make no representation about the suitability of the DTD for
# any purpose. It is provided "as is" without expressed or implied
# warranty.
#
# If you modify the DocBook XML DTD in any way, except for declaring and
# referencing additional sets of general entities and declaring
# additional notations, label your DTD as a variant of DocBook. See
# the maintenance documentation for more information.
#
# Please direct all questions, bug reports, or suggestions for
# changes to the docbook@lists.oasis-open.org mailing list. For more
# information, see http://www.oasis-open.org/docbook/.
# ......................................................................
# This module contains the definitions for elements that are
# isomorphic to the HTML elements. One could argue we should
# instead have based ourselves on the XHTML Table Module, but the
# HTML one is more like what browsers are likely to accept today
# and users are likely to use.
#
# This module has been developed for use with the DocBook V4.5
# "union table model" in which elements and attlists common to both
# models are defined (as the union) in the CALS table module by
# setting various parameter entities appropriately in this file.
#
# In DTD driver files referring to this module, please use an entity
# declaration that uses the public identifier shown below:
#
# <!ENTITY % htmltbl PUBLIC
# "-//OASIS//ELEMENTS DocBook XML HTML Tables V4.5//EN"
# "htmltblx.mod">
# %htmltbl;
#
# See the documentation for detailed information on the parameter
# entity and module scheme used in DocBook, customizing DocBook and
# planning for interchange, and changes made since the last release
# of DocBook.
# ======================= XHTML Tables =======================================
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
html.coreattrs =
common.attrib,
attribute class { text }?,
attribute style { text }?,
attribute title { text }?
# Does not contain lang or dir because they are in %common.attribs
i18n = attribute xml:lang { xsd:NMTOKEN }?
events =
attribute onclick { text }?,
attribute ondblclick { text }?,
attribute onmousedown { text }?,
attribute onmouseup { text }?,
attribute onmouseover { text }?,
attribute onmousemove { text }?,
attribute onmouseout { text }?,
attribute onkeypress { text }?,
attribute onkeydown { text }?,
attribute onkeyup { text }?
attrs = html.coreattrs, i18n, events
cellhalign =
attribute align { "left" | "center" | "right" | "justify" | "char" }?,
attribute char { text }?,
attribute charoff { text }?
cellvalign =
attribute valign { "top" | "middle" | "bottom" | "baseline" }?
# doc:A group of columns in an HTML table.
colgroup = element colgroup { colgroup.attlist, col* }
# doc:Specifications for a column in an HTML table.
col = element col { col.attlist, empty }
# doc:A row in an HTML table.
tr = element tr { tr.attlist, (th | td)+ }
# doc:A table header entry in an HTML table.
th =
element th {
th.attlist, (para.char.mix | tabentry.mix | table | informaltable)*
}
# doc:A table ntry in an HTML table.
td =
element td {
td.attlist, (para.char.mix | tabentry.mix | table | informaltable)*
}
colgroup.attlist &=
attrs,
[ a:defaultValue = "1" ] attribute span { text }?,
attribute width { text }?,
cellhalign,
cellvalign
col.attlist &=
attrs,
[ a:defaultValue = "1" ] attribute span { text }?,
attribute width { text }?,
cellhalign,
cellvalign
tr.attlist &=
attrs,
cellhalign,
cellvalign,
attribute bgcolor { text }?
th.attlist &=
attrs,
attribute abbr { text }?,
attribute axis { text }?,
attribute headers { xsd:IDREFS }?,
attribute scope { "row" | "col" | "rowgroup" | "colgroup" }?,
[ a:defaultValue = "1" ] attribute rowspan { text }?,
[ a:defaultValue = "1" ] attribute colspan { text }?,
cellhalign,
cellvalign,
attribute nowrap { "nowrap" }?,
attribute bgcolor { text }?,
attribute width { text }?,
attribute height { text }?
td.attlist &=
attrs,
attribute abbr { text }?,
attribute axis { text }?,
attribute headers { xsd:IDREFS }?,
attribute scope { "row" | "col" | "rowgroup" | "colgroup" }?,
[ a:defaultValue = "1" ] attribute rowspan { text }?,
[ a:defaultValue = "1" ] attribute colspan { text }?,
cellhalign,
cellvalign,
attribute nowrap { "nowrap" }?,
attribute bgcolor { text }?,
attribute width { text }?,
attribute height { text }?
# ======================================================
# Set up to read in the CALS model configured to
# merge with the XHTML table model
# ======================================================
tables.role.attrib = role.attrib
# Add label and role attributes to table and informaltable
bodyatt =
attribute floatstyle { text }?,
attribute rowheader { "firstcol" | "norowheader" }?,
label.attrib
# Add common attributes to Table, TGroup, TBody, THead, TFoot, Row,
# EntryTbl, and Entry (and InformalTable element).
secur =
common.attrib,
attribute class { text }?,
attribute style { text }?,
attribute title { text }?,
i18n,
events,
tables.role.attrib
common.table.attribs = bodyatt, secur
# Content model for Table (that also allows HTML tables)
tbl.table.mdl =
(blockinfo?,
formalobject.title.content,
ndxterm.class*,
textobject*,
(graphic+ | mediaobject+ | tgroup+))
| (caption, (col* | colgroup*), thead?, tfoot?, (tbody+ | tr+))
informal.tbl.table.mdl =
(textobject*, (graphic+ | mediaobject+ | tgroup+))
| ((col* | colgroup*), thead?, tfoot?, (tbody+ | tr+))
# Attributes for Table (including HTML ones)
# N.B. rules = (none | groups | rows | cols | all) but it can't be spec'd
# that way because 'all' already occurs in a different enumeration in
# CALS tables (frame).
tbl.table.att =
attribute tabstyle { text }?,
attribute tocentry { yesorno.attvals }?,
attribute shortentry { yesorno.attvals }?,
attribute orient { "port" | "land" }?,
attribute pgwide { yesorno.attvals }?,
attribute summary { text }?,
attribute width { text }?,
attribute border { text }?,
attribute rules { text }?,
attribute cellspacing { text }?,
attribute cellpadding { text }?,
attribute align { "left" | "center" | "right" }?,
attribute bgcolor { text }?
tbl.frame.attval =
"void"
| "above"
| "below"
| "hsides"
| "lhs"
| "rhs"
| "vsides"
| "box"
| "border"
| "top"
| "bottom"
| "topbot"
| "all"
| "sides"
| "none"
# Allow either objects or inlines; beware of REs between elements.
tbl.entry.mdl = para.char.mix | tabentry.mix
# thead, tfoot, and tbody are defined in both table models,
# so we set up parameter entities to define union models for them
tbl.hdft.mdl = tr+ | (colspec*, row+)
tbl.tbody.mdl = tr+ | row+
# End of DocBook XML HTML Table Module V4.5 ............................
# ......................................................................

View file

@ -1,23 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 65
/repos/product-security/!svn/ver/302/defensive-coding/trunk/en-US
END
Defensive_Coding.ent
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/64/defensive-coding/trunk/en-US/Defensive_Coding.ent
END
Book_Info.xml
K 25
svn:wc:ra_dav:version-url
V 79
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Book_Info.xml
END
Defensive_Coding.xml
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Defensive_Coding.xml
END

View file

@ -1,145 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US
https://svn.devel.redhat.com/repos/product-security
2013-01-16T14:32:22.318444Z
302
fweimer@REDHAT.COM
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
Defensive_Coding.ent
file
2013-01-10T17:17:49.038814Z
240837ebc2948c0404c903c2b25ee90a
2012-07-16T12:32:39.042163Z
64
fweimer@REDHAT.COM
54
Python
dir
C
dir
CXX
dir
Book_Info.xml
file
2013-01-10T17:17:49.038814Z
0a9c514a2db8c6783b91a20eea2918c2
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
999
Tasks
dir
Defensive_Coding.xml
file
2013-01-10T17:17:49.038814Z
81327d12a4be4bc9189fbd3eea5c5215
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
1522
Features
dir

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,29 +0,0 @@
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE bookinfo PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<bookinfo id="book-Defensive_Coding">
<title>Defensive Coding</title>
<subtitle>A Guide to Improving Software Security</subtitle>
<edition>1.0</edition>
<issuenum>1.0</issuenum>
<pubsnumber>20</pubsnumber>
<productname>Internal</productname>
<productnumber>6.4</productnumber>
<abstract>
<para>
This document provides guidelines for improving software
security through secure coding. It covers common
programming languages and libraries, and focuses on
concrete recommendations.
</para>
</abstract>
<corpauthor>
<inlinemediaobject>
<imageobject>
<imagedata fileref="Common_Content/images/redhat-logo.svg" format="SVG" />
</imageobject>
</inlinemediaobject>
</corpauthor>
<xi:include href="Common_Content/Legal_Notice.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</bookinfo>

View file

@ -1,2 +0,0 @@
<!ENTITY YEAR "2012">
<!ENTITY HOLDER "Red Hat, Inc">

View file

@ -1,26 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<xi:include href="Book_Info.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<part>
<title>Programming Languages</title>
<xi:include href="C/C.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="CXX/CXX.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Python/Language.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</part>
<part>
<title>Specific Programming Tasks</title>
<xi:include href="Tasks/Library_Design.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/Descriptors.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/File_System.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/Temporary_Files.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/Processes.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/Serialization.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Tasks/Cryptography.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</part>
<part>
<title>Implementing Security Features</title>
<xi:include href="Features/Authentication.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Features/TLS.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</part>
</book>

View file

@ -1,35 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 67
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/C
END
C.xml
K 25
svn:wc:ra_dav:version-url
V 73
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/C/C.xml
END
Allocators.xml
K 25
svn:wc:ra_dav:version-url
V 82
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/C/Allocators.xml
END
Language.xml
K 25
svn:wc:ra_dav:version-url
V 80
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/C/Language.xml
END
schemas.xml
K 25
svn:wc:ra_dav:version-url
V 79
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/C/schemas.xml
END
Libc.xml
K 25
svn:wc:ra_dav:version-url
V 76
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/C/Libc.xml
END

View file

@ -1,6 +0,0 @@
K 10
svn:ignore
V 9
snippets
END

View file

@ -1,198 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US/C
https://svn.devel.redhat.com/repos/product-security
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
C.xml
file
2013-01-10T17:17:40.330763Z
152059b0949055c27918169fb0406ee5
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
503
Allocators.xml
file
2013-01-10T17:17:40.330763Z
483d91643e7a6a8d6545649b2fa0b144
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
8762
Language.xml
file
2013-01-10T17:17:40.330763Z
11fb84b7e9a7c76cfc95dbe918118998
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
6528
schemas.xml
file
2013-01-10T17:17:40.331763Z
769bc2635d36b318161574a1adf2f6e7
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
150
Libc.xml
file
2013-01-10T17:17:40.331763Z
6e4999f743167fd393cbd521fd4d662c
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
8733

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,207 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-C-Allocators">
<title>Memory allocators</title>
<section>
<title><function>malloc</function> and related functions</title>
<para>
The C library interfaces for memory allocation are provided by
<function>malloc</function>, <function>free</function> and
<function>realloc</function>, and the
<function>calloc</function> function. In addition to these
generic functions, there are derived functions such as
<function>strdup</function> which perform allocation using
<function>malloc</function> internally, but do not return
untyped heap memory (which could be used for any object).
</para>
<para>
The C compiler knows about these functions and can use their
expected behavior for optimizations. For instance, the compiler
assumes that an existing pointer (or a pointer derived from an
existing pointer by arithmetic) will not point into the memory
area returned by <function>malloc</function>.
</para>
<para>
If the allocation fails, <function>realloc</function> does not
free the old pointer. Therefore, the idiom <literal>ptr =
realloc(ptr, size);</literal> is wrong because the memory
pointed to by <literal>ptr</literal> leaks in case of an error.
</para>
<section>
<title>Use-after-free errors</title>
<para>
After <function>free</function>, the pointer is invalid.
Further pointer dereferences are not allowed (and are usually
detected by <application>valgrind</application>). Less obvious
is that any <emphasis>use</emphasis> of the old pointer value is
not allowed, either. In particular, comparisons with any other
pointer (or the null pointer) are undefined according to the C
standard.
</para>
<para>
The same rules apply to <function>realloc</function> if the
memory area cannot be enlarged in-place. For instance, the
compiler may assume that a comparison between the old and new
pointer will always return false, so it is impossible to detect
movement this way.
</para>
</section>
<section>
<title>Handling memory allocation errors</title>
<para>
Recovering from out-of-memory errors is often difficult or even
impossible. In these cases, <function>malloc</function> and
other allocation functions return a null pointer. Dereferencing
this pointer lead to a crash. Such dereferences can even be
exploitable for code execution if the dereference is combined
with an array subscript.
</para>
<para>
In general, if you cannot check all allocation calls and
handle failure, you should abort the program on allocation
failure, and not rely on the null pointer dereference to
terminate the process. See
<xref
linkend="sect-Defensive_Coding-Tasks-Serialization-Decoders"/>
for related memory allocation concerns.
</para>
</section>
</section>
<section id="sect-Defensive_Coding-C-Allocators-alloca">
<title><function>alloca</function> and other forms of stack-based
allocation</title>
<para>
Allocation on the stack is risky because stack overflow checking
is implicit. There is a guard page at the end of the memory
area reserved for the stack. If the program attempts to read
from or write to this guard page, a <literal>SIGSEGV</literal>
signal is generated and the program typically terminates.
</para>
<para>
This is sufficient for detecting typical stack overflow
situations such as unbounded recursion, but it fails when the
stack grows in increments larger than the size of the guard
page. In this case, it is possible that the stack pointer ends
up pointing into a memory area which has been allocated for a
different purposes. Such misbehavior can be exploitable.
</para>
<para>
A common source for large stack growth are calls to
<function>alloca</function> and related functions such as
<function>strdupa</function>. These functions should be avoided
because of the lack of error checking. (They can be used safely
if the allocated size is less than the page size (typically,
4096 bytes), but this case is relatively rare.) Additionally,
relying on <function>alloca</function> makes it more difficult
to reorgnize the code because it is not allowed to use the
pointer after the function calling <function>alloca</function>
has returned, even if this function has been inlined into its
caller.
</para>
<para>
Similar concerns apply to <emphasis>variable-length
arrays</emphasis> (VLAs), a feature of the C99 standard which
started as a GNU extension. For large objects exceeding the
page size, there is no error checking, either.
</para>
<para>
In both cases, negative or very large sizes can trigger a
stack-pointer wraparound, and the stack pointer and end up
pointing into caller stack frames, which is fatal and can be
exploitable.
</para>
<para>
If you want to use <function>alloca</function> or VLAs for
performance reasons, consider using a small on-stack array (less
than the page size, large enough to fulfill most requests). If
the requested size is small enough, use the on-stack array.
Otherwise, call <function>malloc</function>. When exiting the
function, check if <function>malloc</function> had been called,
and free the buffer as needed.
</para>
</section>
<section id="sect-Defensive_Coding-C-Allocators-Arrays">
<title>Array allocation</title>
<para>
When allocating arrays, it is important to check for overflows.
The <function>calloc</function> function performs such checks.
</para>
<para>
If <function>malloc</function> or <function>realloc</function>
is used, the size check must be written manually. For instance,
to allocate an array of <literal>n</literal> elements of type
<literal>T</literal>, check that the requested size is not
greater than <literal>n / sizeof(T)</literal>.
</para>
</section>
<section>
<title>Custom memory allocators</title>
<para>
Custom memory allocates come in two forms: replacements for
<function>malloc</function>, and completely different interfaces
for memory management. Both approaches can reduce the
effectiveness of <application>valgrind</application> and similar
tools, and the heap corruption detection provided by GNU libc, so
they should be avoided.
</para>
<para>
Memory allocators are difficult to write and contain many
performance and security pitfalls.
</para>
<itemizedlist>
<listitem>
<para>
When computing array sizes or rounding up allocation
requests (to the next allocation granularity, or for
alignment purposes), checks for arithmetic overflow are
required.
</para>
</listitem>
<listitem>
<para>
Size computations for array allocations need overflow
checking. See <xref
linkend="sect-Defensive_Coding-C-Allocators-Arrays"/>.
</para>
</listitem>
<listitem>
<para>
It can be difficult to beat well-tuned general-purpose
allocators. In micro-benchmarks, pool allocators can show
huge wins, and size-specific pools can reduce internal
fragmentation. But often, utilization of individual pools
is poor, and
</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Conservative garbage collection</title>
<para>
Garbage collection can be an alternative to explicit memory
management using <function>malloc</function> and
<function>free</function>. The Boehm-Dehmers-Weiser allocator
can be used from C programs, with minimal type annotations.
Performance is competitive with <function>malloc</function> on
64-bit architectures, especially for multi-threaded programs.
The stop-the-world pauses may be problematic for some real-time
applications, though.
</para>
<para>
However, using a conservative garbage collector may reduce
opertunities for code reduce because once one library in a
program uses garbage collection, the whole process memory needs
to be subject to it, so that no pointers are missed. The
Boehm-Dehmers-Weiser collector also reserves certain signals for
internal use, so it is not fully transparent to the rest of the
program.
</para>
</section>
</section>

View file

@ -1,11 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-C">
<title>The C Programming Language</title>
<xi:include href="Language.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Libc.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Allocators.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</chapter>

View file

@ -1,150 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-C-Language">
<title>The core language</title>
<para>
C provides no memory safety. Most recommendations in this section
deal with this aspect of the language.
</para>
<section id="sect-Defensive_Coding-C-Undefined">
<title>Undefined behavior</title>
<para>
Some C constructs are defined to be undefined by the C standard.
This does not only mean that the standard does not describe
what happens when the construct is executed. It also allows
optimizing compilers such as GCC to assume that this particular
construct is never reached. In some cases, this has caused
GCC to optimize security checks away. (This is not a flaw in GCC
or the C language. But C certainly has some areas which are more
difficult to use than others.)
</para>
<para>
Common sources of undefined behavior are:
</para>
<itemizedlist>
<listitem><para>out-of-bounds array accesses</para></listitem>
<listitem><para>null pointer dereferences</para></listitem>
<listitem><para>overflow in signed integer arithmetic</para></listitem>
</itemizedlist>
</section>
<section id="sect-Defensive_Coding-C-Pointers">
<title>Recommendations for pointers and array handling</title>
<para>
Always keep track of the size of the array you are working with.
Often, code is more obviously correct when you keep a pointer
past the last element of the array, and calculate the number of
remaining elements by substracting the current position from
that pointer. The alternative, updating a separate variable
every time when the position is advanced, is usually less
obviously correct.
</para>
<para>
<xref linkend="ex-Defensive_Coding-C-Pointers-remaining"/>
shows how to extract Pascal-style strings from a character
buffer. The two pointers kept for length checks are
<varname>inend</varname> and <varname>outend</varname>.
<varname>inp</varname> and <varname>outp</varname> are the
respective positions.
The number of input bytes is checked using the expression
<literal>len > (size_t)(inend - inp)</literal>.
The cast silences a compiler warning;
<varname>inend</varname> is always larger than
<varname>inp</varname>.
</para>
<example id="ex-Defensive_Coding-C-Pointers-remaining">
<title>Array processing in C</title>
<xi:include href="snippets/Pointers-remaining.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
It is important that the length checks always have the form
<literal>len > (size_t)(inend - inp)</literal>, where
<varname>len</varname> is a variable of type
<type>size_t</type> which denotes the <emphasis>total</emphasis>
number of bytes which are about to be read or written next. In
general, it is not safe to fold multiple such checks into one,
as in <literal>len1 + len2 > (size_t)(inend - inp)</literal>,
because the expression on the left can overflow or wrap around
(see <xref linkend="sect-Defensive_Coding-C-Arithmetic"/>), and it
no longer reflects the number of bytes to be processed.
</para>
</section>
<section id="sect-Defensive_Coding-C-Arithmetic">
<title>Recommendations for integer arithmetic</title>
<para>
Overflow in signed integer arithmetic is undefined. This means
that it is not possible to check for overflow after it happened,
see <xref linkend="ex-Defensive_Coding-C-Arithmetic-bad"/>.
</para>
<example id="ex-Defensive_Coding-C-Arithmetic-bad">
<title>Incorrect overflow detection in C</title>
<xi:include href="snippets/Arithmetic-add.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
The following approaches can be used to check for overflow,
without actually causing it.
</para>
<itemizedlist>
<listitem>
<para>
Use a wider type to perform the calculation, check that the
result is within bounds, and convert the result to the
original type. All intermediate results must be checked in
this way.
</para>
</listitem>
<listitem>
<para>
Perform the calculation in the corresponding unsigned type
and use bit fiddling to detect the overflow.
</para>
</listitem>
<listitem>
<para>
Compute bounds for acceptable input values which are known
to avoid overflow, and reject other values. This is the
preferred way for overflow checking on multiplications,
see <xref linkend="ex-Defensive_Coding-C-Arithmetic-mult"/>.
<!-- This approach can result in bogus compiler warnings
with signed types:
http://gcc.gnu.org/bugzilla/post_bug.cgi -->
</para>
</listitem>
</itemizedlist>
<example id="ex-Defensive_Coding-C-Arithmetic-mult">
<title>Overflow checking for unsigned multiplication</title>
<xi:include href="snippets/Arithmetic-mult.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
Basic arithmetic operations a commutative, so for bounds checks,
there are two different but mathematically equivalent
expressions. Sometimes, one of the expressions results in
better code because parts of it can be reduced to a constant.
This applies to overflow checks for multiplication <literal>a *
b</literal> involving a constant <literal>a</literal>, where the
expression is reduced to <literal>b &gt; C</literal> for some
constant <literal>C</literal> determined at compile time. The
other expression, <literal>b &amp;&amp; a > ((unsigned)-1) /
b</literal>, is more difficult to optimize at compile time.
</para>
<para>
When a value is converted to a signed integer, GCC always
chooses the result based on 2's complement arithmetic. This GCC
extension (which is also implemented by other compilers) helps a
lot when implementing overflow checks.
</para>
<para>
Legacy code should be compiled with the <option>-fwrapv</option>
GCC option. As a result, GCC will provide 2's complement
semantics for integer arithmetic, including defined behavior on
integer overflow.
</para>
</section>
</section>

View file

@ -1,227 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-C-Libc">
<title>The C standard library</title>
<para>
Parts of the C standard library (and the UNIX and GNU extensions)
are difficult to use, so you shoud avoid them.
</para>
<para>
Please check the applicable documentation before using the
recommended replacements. Many of these functions allocate
buffers using <function>malloc</function> which your code must
deallocate explicitly using <function>free</function>.
</para>
<section id="sect-Defensive_Coding-C-Absolutely-Banned">
<title>Absolutely banned interfaces</title>
<para>
The functions listed below must not be used because they are
almost always unsafe. Use the indicated replacements instead.
</para>
<itemizedlist>
<listitem><para><function>gets</function>
⟶ <function>fgets</function></para></listitem>
<listitem><para><function>getwd</function>
⟶ <function>getcwd</function>
or <function>get_current_dir_name</function></para></listitem>
<listitem>
<para>
<function>readdir_r</function> ⟶ <function>readdir</function>
<!-- It is quite complicated to allocate a properly-sized
buffer for use with readdir_r, and readdir provides
sufficient thread safety guarantees. -->
<!-- ??? Add File_System cross-reference -->
</para>
</listitem>
<listitem>
<para>
<function>realpath</function> (with a non-NULL second parameter)
⟶ <function>realpath</function> with NULL as the second parameter,
or <function>canonicalize_file_name</function>
<!-- It is complicated to allocate a properly-sized buffer
for use with realpath. -->
<!-- ??? Add File_System cross-reference -->
</para>
</listitem>
</itemizedlist>
<para>
The constants listed below must not be used, either. Instead,
code must allocate memory dynamically and use interfaces with
length checking.
</para>
<itemizedlist>
<listitem>
<para>
<literal>NAME_MAX</literal> (limit not actually enforced by
the kernel)
</para>
</listitem>
<listitem>
<para>
<literal>PATH_MAX</literal> (limit not actually enforced by
the kernel)
</para>
</listitem>
<listitem>
<para>
<literal>_PC_NAME_MAX</literal> (This limit, returned by the
<function>pathconf</function> function, is not enforced by
the kernel.)
</para>
</listitem>
<listitem>
<para>
<literal>_PC_PATH_MAX</literal> (This limit, returned by the
<function>pathconf</function> function, is not enforced by
the kernel.)
</para>
</listitem>
</itemizedlist>
<para>
The following structure members must not be used.
</para>
<itemizedlist>
<listitem>
<para>
<literal>f_namemax</literal> in <literal>struct
statvfs</literal> (limit not actually enforced by the kernel,
see <literal>_PC_NAME_MAX</literal> above)
</para>
</listitem>
</itemizedlist>
</section>
<section id="sect-Defensive_Coding-C-Avoid">
<title>Functions to avoid</title>
<para>
The following string manipulation functions can be used securely
in principle, but their use should be avoided because they are
difficult to use correctly. Calls to these functions can be
replaced with <function>asprintf</function> or
<function>vasprintf</function>. (For non-GNU targets, these
functions are available from Gnulib.) In some cases, the
<function>snprintf</function> function might be a suitable
replacement, see <xref
linkend="sect-Defensive_Coding-C-String-Functions-Length"/>.
</para>
<itemizedlist>
<listitem><para><function>sprintf</function></para></listitem>
<listitem><para><function>strcat</function></para></listitem>
<listitem><para><function>strcpy</function></para></listitem>
<listitem><para><function>vsprintf</function></para></listitem>
</itemizedlist>
<para>
Use the indicated replacements for the functions below.
</para>
<itemizedlist>
<listitem>
<para>
<function>alloca</function> ⟶
<function>malloc</function> and <function>free</function>
(see <xref linkend="sect-Defensive_Coding-C-Allocators-alloca"/>)
</para>
</listitem>
<listitem>
<para>
<function>putenv</function> ⟶
explicit <varname>envp</varname> argument in process creation
(see <xref linkend="sect-Defensive_Coding-Tasks-Processes-environ"/>)
</para>
</listitem>
<listitem>
<para>
<function>setenv</function> ⟶
explicit <varname>envp</varname> argument in process creation
(see <xref linkend="sect-Defensive_Coding-Tasks-Processes-environ"/>)
</para>
</listitem>
<listitem>
<para>
<function>strdupa</function> ⟶
<function>strdup</function> and <function>free</function>
(see <xref linkend="sect-Defensive_Coding-C-Allocators-alloca"/>)
</para>
</listitem>
<listitem>
<para>
<function>strndupa</function> ⟶
<function>strndup</function> and <function>free</function>
(see <xref linkend="sect-Defensive_Coding-C-Allocators-alloca"/>)
</para>
</listitem>
<listitem>
<para>
<function>system</function> ⟶
<function>posix_spawn</function>
or <function>fork</function>/<function>execve</function>/
(see <xref linkend="sect-Defensive_Coding-Tasks-Processes-execve"/>)
</para>
</listitem>
<listitem>
<para>
<function>unsetenv</function> ⟶
explicit <varname>envp</varname> argument in process creation
(see <xref linkend="sect-Defensive_Coding-Tasks-Processes-environ"/>)
</para>
</listitem>
</itemizedlist>
</section>
<section id="sect-Defensive_Coding-C-String-Functions-Length">
<title>String Functions With Explicit Length Arguments</title>
<para>
The <function>snprintf</function> function provides a way to
construct a string in a statically-sized buffer. (If the buffer
size is dynamic, use <function>asprintf</function> instead.)
</para>
<informalexample>
<xi:include href="snippets/String-Functions-snprintf.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
The second argument to the <function>snprintf</function> should
always be the size of the buffer in the first argument (which
should be a character array). Complex pointer and length
arithmetic can introduce errors and nullify the security
benefits of <function>snprintf</function>. If you need to
construct a string iteratively, by repeatedly appending
fragments, consider constructing the string on the heap,
increasing the buffer with <function>realloc</function> as
needed. (<function>snprintf</function> does not support
overlapping the result buffer with argument strings.)
</para>
<para>
If you use <function>vsnprintf</function> (or
<function>snprintf</function>) with a format string which is not
a constant, but a function argument, it is important to annotate
the function with a <literal>format</literal> function
attribute, so that GCC can warn about misuse of your function
(see <xref
linkend="ex-Defensive_Coding-C-String-Functions-format-Attribute"/>).
</para>
<example id="ex-Defensive_Coding-C-String-Functions-format-Attribute">
<title>The <literal>format</literal> function attribute</title>
<xi:include href="snippets/String-Functions-format.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
There are other functions which operator on NUL-terminated
strings and take a length argument which affects the number of
bytes written to the destination: <function>strncpy</function>,
<function>strncat</function>, and <function>stpncpy</function>.
These functions do not ensure that the result string is
NUL-terminated. For <function>strncpy</function>,
NUL termination can be added this way:
</para>
<informalexample>
<xi:include href="snippets/String-Functions-strncpy.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
Some systems support <function>strlcpy</function> and
<function>strlcat</function> functions which behave this way,
but these functions are not part of GNU libc. Using
<function>snprintf</function> with a suitable format string is a
simple (albeit slightly slower) replacement.
</para>
</section>
</section>

View file

@ -1,4 +0,0 @@
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<include rules="../../schemas.xml"/>
</locatingRules>

View file

@ -1,29 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 69
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/CXX
END
CXX.xml
K 25
svn:wc:ra_dav:version-url
V 77
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/CXX/CXX.xml
END
Language.xml
K 25
svn:wc:ra_dav:version-url
V 82
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/CXX/Language.xml
END
schemas.xml
K 25
svn:wc:ra_dav:version-url
V 81
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/CXX/schemas.xml
END
Std.xml
K 25
svn:wc:ra_dav:version-url
V 77
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/CXX/Std.xml
END

View file

@ -1,6 +0,0 @@
K 10
svn:ignore
V 9
snippets
END

View file

@ -1,164 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US/CXX
https://svn.devel.redhat.com/repos/product-security
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
CXX.xml
file
2013-01-10T17:17:40.360763Z
b0f0bf8b20378408157b933ace95025b
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
419
Language.xml
file
2013-01-10T17:17:40.361763Z
0c223f5c8e653b24ad9ee512a9347ff6
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
7232
schemas.xml
file
2013-01-10T17:17:40.361763Z
769bc2635d36b318161574a1adf2f6e7
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
150
Std.xml
file
2013-01-10T17:17:40.362763Z
43d4998b7a340602a1cfb058cac483c9
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
1392

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,10 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-CXX">
<title>The C++ Programming Language</title>
<xi:include href="Language.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Std.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</chapter>

View file

@ -1,186 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-CXX-Language">
<title>The core language</title>
<para>
C++ includes a large subset of the C language. As far as the C
subset is used, the recommendations in <xref
linkend="chap-Defensive_Coding-C"/> apply.
</para>
<section>
<title>Array allocation with <literal>operator new[]</literal></title>
<para>
For very large values of <literal>n</literal>, an expression
like <literal>new T[n]</literal> can return a pointer to a heap
region which is too small. In other words, not all array
elements are actually backed with heap memory reserved to the
array. Current GCC versions generate code that performs a
computation of the form <literal>sizeof(T) * size_t(n) +
cookie_size</literal>, where <literal>cookie_size</literal> is
currently at most 8. This computation can overflow, and
GCC-generated code does not detect this.
</para>
<para>
The <literal>std::vector</literal> template can be used instead
an explicit array allocation. (The GCC implementation detects
overflow internally.)
</para>
<para>
If there is no alternative to <literal>operator new[]</literal>,
code which allocates arrays with a variable length must check
for overflow manually. For the <literal>new T[n]</literal>
example, the size check could be <literal>n || (n > 0 &amp;&amp;
n &gt; (size_t(-1) - 8) / sizeof(T))</literal>. (See <xref
linkend="sect-Defensive_Coding-C-Arithmetic"/>.) If there are
additional dimensions (which must be constants according to the
C++ standard), these should be included as factors in the
divisor.
</para>
<para>
These countermeasures prevent out-of-bounds writes and potential
code execution. Very large memory allocations can still lead to
a denial of service. <xref
linkend="sect-Defensive_Coding-Tasks-Serialization-Decoders"/>
contains suggestions for mitigating this problem when processing
untrusted data.
</para>
<para>
See <xref linkend="sect-Defensive_Coding-C-Allocators-Arrays"/>
for array allocation advice for C-style memory allocation.
</para>
</section>
<section>
<title>Overloading</title>
<para>
Do not overload functions with versions that have different
security characteristics. For instance, do not implement a
function <function>strcat</function> which works on
<type>std::string</type> arguments. Similarly, do not name
methods after such functions.
</para>
</section>
<section>
<title>ABI compatibility and preparing for security updates</title>
<para>
A stable binary interface (ABI) is vastly preferred for security
updates. Without a stable ABI, all reverse dependencies need
recompiling, which can be a lot of work and could even be
impossible in some cases. Ideally, a security update only
updates a single dynamic shared object, and is picked up
automatically after restarting affected processes.
</para>
<para>
Outside of extremely performance-critical code, you should
ensure that a wide range of changes is possible without breaking
ABI. Some very basic guidelines are:
</para>
<itemizedlist>
<listitem>
<para>
Avoid inline functions.
</para>
</listitem>
<listitem>
<para>
Use the pointer-to-implementation idiom.
</para>
</listitem>
<listitem>
<para>
Try to avoid templates. Use them if the increased type
safety provides a benefit to the programmer.
</para>
</listitem>
<listitem>
<para>
Move security-critical code out of templated code, so that
it can be patched in a central place if necessary.
</para>
</listitem>
</itemizedlist>
<para>
The KDE project publishes a document with more extensive
guidelines on ABI-preserving changes to C++ code, <ulink
url="http://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++">Policies/Binary
Compatibility Issues With C++</ulink>
(<emphasis>d-pointer</emphasis> refers to the
pointer-to-implementation idiom).
</para>
</section>
<section id="sect-Defensive_Coding-CXX-Language-CXX11">
<title>C++0X and C++11 support</title>
<para>
GCC offers different language compatibility modes:
</para>
<itemizedlist>
<listitem>
<para>
<option>-std=c++98</option> for the original 1998 C++
standard
</para>
</listitem>
<listitem>
<para>
<option>-std=c++03</option> for the 1998 standard with the
changes from the TR1 technical report
</para>
</listitem>
<listitem>
<para>
<option>-std=c++11</option> for the 2011 C++ standard. This
option should not be used.
</para>
</listitem>
<listitem>
<para>
<option>-std=c++0x</option> for several different versions
of C++11 support in development, depending on the GCC
version. This option should not be used.
<!-- There were two incompatibilies before GCC 4.7.2
(std::list and std::pair), but link C++98 and C++11
code is still unsupported, although it currently has
some chance of working by accident. -->
</para>
</listitem>
</itemizedlist>
<para>
For each of these flags, there are variants which also enable
GNU extensions (mostly language features also found in C99 or
C11): <option>-std=gnu++98</option>,
<option>-std=gnu++03</option>, <option>-std=gnu++11</option>.
Again, <option>-std=gnu++11</option> should not be used.
</para>
<para>
If you enable C++11 support, the ABI of the standard C++ library
<literal>libstdc++</literal> will change in subtle ways.
Currently, no C++ libraries are compiled in C++11 mode, so if
you compile your code in C++11 mode, it will be incompatible
with the rest of the system. Unfortunately, this is also the
case if you do not use any C++11 features. Currently, there is
no safe way to enable C++11 mode (except for freestanding
applications).
</para>
<para>
The meaning of C++0X mode changed from GCC release to GCC
release. Earlier versions were still ABI-compatible with C++98
mode, but in the most recent versions, switching to C++0X mode
activates C++11 support, with its compatibility problems.
</para>
<para>
Some C++11 features (or approximations thereof) are available
with TR1 support, that is, with <option>-std=c++03</option> or
<option>-std=gnu++03</option> and in the
<literal>&lt;tr1/*&gt;</literal> header files. This includes
<literal>std::tr1::shared_ptr</literal> (from
<literal>&lt;tr1/memory&gt;</literal>) and
<literal>std::tr1::function</literal> (from
<literal>&lt;tr1/functional&gt;</literal>). For other C++11
features, the Boost C++ library contains replacements.
</para>
</section>
</section>

View file

@ -1,32 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<section id="sect-Defensive_Coding-CXX-Std">
<title>The C++ standard library</title>
<para>
The C++ standard library includes most of its C counterpart
by reference, see <xref linkend="sect-Defensive_Coding-C-Libc"/>.
</para>
<section>
<title>Containers and <literal>operator[]</literal></title>
<para>
Many containers similar to <literal>std::vector</literal>
provide both <literal>operator[](size_type)</literal> and a
member function <literal>at(size_type)</literal>. This applies
to <literal>std::vector</literal> itself,
<literal>std::array</literal>, <literal>std::string</literal>
and other instances of <literal>std::basic_string</literal>.
</para>
<para>
<literal>operator[](size_type)</literal> is not required by the
standard to perform bounds checking (and the implementation in
GCC does not). In contrast, <literal>at(size_type)</literal>
must perform such a check. Therefore, in code which is not
performance-critical, you should prefer
<literal>at(size_type)</literal> over
<literal>operator[](size_type)</literal>, even though it is
slightly more verbose.
</para>
</section>
</section>

View file

@ -1,4 +0,0 @@
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<include rules="../../schemas.xml"/>
</locatingRules>

View file

@ -1,23 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 74
/repos/product-security/!svn/ver/302/defensive-coding/trunk/en-US/Features
END
TLS.xml
K 25
svn:wc:ra_dav:version-url
V 82
/repos/product-security/!svn/ver/302/defensive-coding/trunk/en-US/Features/TLS.xml
END
schemas.xml
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/Features/schemas.xml
END
Authentication.xml
K 25
svn:wc:ra_dav:version-url
V 93
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Features/Authentication.xml
END

View file

@ -1,6 +0,0 @@
K 10
svn:ignore
V 9
snippets
END

View file

@ -1,130 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US/Features
https://svn.devel.redhat.com/repos/product-security
2013-01-16T14:32:22.318444Z
302
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
TLS.xml
file
2013-01-16T22:05:55.369436Z
d466f82b291f65cf802244af678d52dd
2013-01-16T14:32:22.318444Z
302
fweimer@REDHAT.COM
has-props
41635
schemas.xml
file
2013-01-10T17:17:49.036814Z
769bc2635d36b318161574a1adf2f6e7
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
150
Authentication.xml
file
2013-01-10T17:17:49.036814Z
6430a1389eb187d0fbcc79bea6c1a21e
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
8257

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,189 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Authentication">
<title>Authentication and Authorization</title>
<section id="sect-Defensive_Coding-Authentication-Server">
<title>Authenticating servers</title>
<para>
When connecting to a server, a client has to make sure that it
is actually talking to the server it expects. There are two
different aspects, securing the network path, and making sure
that the expected user runs the process on the target host.
There are several ways to ensure that:
</para>
<itemizedlist>
<listitem>
<para>
The server uses a TLS certificate which is valid according
to the web browser public key infrastructure, and the client
verifies the certificate and the host name.
</para>
</listitem>
<listitem>
<para>
The server uses a TLS certificate which is expectedby the
client (perhaps it is stored in a configuration file read by
the client). In this case, no host name checking is
required.
</para>
</listitem>
<listitem>
<para>
On Linux, UNIX domain sockets (of the
<literal>PF_UNIX</literal> protocol family, sometimes called
<literal>PF_LOCAL</literal>) are restricted by file system
permissions. If the server socket path is not
world-writable, the server identity cannot be spoofed by
local users.
</para>
</listitem>
<listitem>
<para>
Port numbers less than 1024 (<emphasis>trusted
ports</emphasis>) can only be used by
<literal>root</literal>, so if a UDP or TCP server is
running on the local host and it uses a trusted port, its
identity is assured. (Not all operating systems enforce the
trusted ports concept, and the network might not be trusted,
so it is only useful on the local system.)
</para>
</listitem>
</itemizedlist>
<para>
TLS (<xref linkend="chap-Defensive_Coding-TLS"/>) is the
recommended way for securing connections over untrusted
networks.
</para>
<para>
If the server port number is 1024 is higher, a local user can
impersonate the process by binding to this socket, perhaps after
crashing the real server by exploiting a denial-of-service
vulnerability.
</para>
</section>
<section id="sect-Defensive_Coding-Authentication-Host_based">
<title>Host-based authentication</title>
<para>
Host-based authentication uses access control lists (ACLs) to
accept or deny requests from clients. Thsis authentication
method comes in two flavors: IP-based (or, more generally,
address-based) and name-based (with the name coming from DNS or
<filename>/etc/hosts</filename>). IP-based ACLs often use
prefix notation to extend access to entire subnets. Name-based
ACLs sometimes use wildcards for adding groups of hosts (from
entire DNS subtrees). (In the SSH context, host-based
authentication means something completely different and is not
covered in this section.)
</para>
<para>
Host-based authentication trust the network and may not offer
sufficient granularity, so it has to be considered a weak form
of authentication. On the other hand, IP-based authentication
can be made extremely robust and can be applied very early in
input processing, so it offers an opportunity for significantly
reducing the number of potential attackers for many services.
</para>
<para>
The names returned by <function>gethostbyaddr</function> and
<function>getnameinfo</function> functions cannot be trusted.
(DNS PTR records can be set to arbitrary values, not just names
belong to the address owner.) If these names are used for ACL
matching, a forward lookup using
<function>gethostbyaddr</function> or
<function>getaddrinfo</function> has to be performed. The name
is only valid if the original address is found among the results
of the forward lookup (<emphasis>double-reverse
lookup</emphasis>).
</para>
<para>
An empty ACL should deny all access (deny-by-default). If empty
ACLs permits all access, configuring any access list must switch
to deny-by-default for all unconfigured protocols, in both
name-based and address-based variants.
</para>
<para>
Similarly, if an address or name is not matched by the list, it
should be denied. However, many implementations behave
differently, so the actual behavior must be documented properly.
</para>
<para>
IPv6 addresses can embed IPv4 addresses. There is no
universally correct way to deal with this ambiguity. The
behavior of the ACL implementation should be documented.
</para>
</section>
<section id="sect-Defensive_Coding-Authentication-UNIX_Domain">
<title>UNIX domain socket authentication</title>
<para>
UNIX domain sockets (with address family
<literal>AF_UNIX</literal> or <literal>AF_LOCAL</literal>) are
restricted to the local host and offer a special authentication
mechanism: credentials passing.
</para>
<para>
Nowadays, most systems support the
<literal>SO_PEERCRED</literal> (Linux) or
<literal>LOCAL_PEERCRED</literal> (FreeBSD) socket options, or
the <function>getpeereid</function> (other BSDs, MacOS X).
These interfaces provide direct access to the (effective) user
ID on the other end of a domain socket connect, without
cooperation from the other end.
</para>
<para>
Historically, credentials passing was implemented using
ancillary data in the <function>sendmsg</function> and
<function>recvmsg</function> functions. On some systems, only
credentials data that the peer has explicitly sent can be
received, and the kernel checks the data for correctness on the
sending side. This means that both peers need to deal with
ancillary data. Compared to that, the modern interfaces are
easier to use. Both sets of interfaces vary considerably among
UNIX-like systems, unfortunately.
</para>
<para>
If you want to authenticate based on supplementary groups, you
should obtain the user ID using one of these methods, and look
up the list of supplementary groups using
<function>getpwuid</function> (or
<function>getpwuid_r</function>) and
<function>getgrouplist</function>. Using the PID and
information from <filename>/proc/PID/status</filename> is prone
to race conditions and insecure.
</para>
</section>
<section id="sect-Defensive_Coding-Authentication-Netlink">
<title><literal>AF_NETLINK</literal> authentication of origin</title>
<!-- ??? kernel change may make this obsolete:
https://bugzilla.redhat.com/show_bug.cgi?id=851968 -->
<para>
Netlink messages are used as a high-performance data transfer
mechanism between the kernel and the userspace. Traditionally,
they are used to exchange information related to the network
statck, such as routing table entries.
</para>
<para>
When processing Netlink messages from the kernel, it is
important to check that these messages actually originate from
the kernel, by checking that the port ID (or PID) field
<literal>nl_pid</literal> in the <literal>sockaddr_nl</literal>
structure is <literal>0</literal>. (This structure can be
obtained using <function>recvfrom</function> or
<function>recvmsg</function>, it is different from the
<literal>nlmsghdr</literal> structure.) The kernel does not
prevent other processes from sending unicast Netlink messages,
but the <literal>nl_pid</literal> field in the sender's socket
address will be non-zero in such cases.
</para>
<para>
Applications should not use <literal>AF_NETLINK</literal>
sockets as an IPC mechanism among processes, but prefer UNIX
domain sockets for this tasks.
</para>
</section>
</chapter>

View file

@ -1,988 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-TLS">
<title>Transport Layer Security</title>
<para>
Transport Layer Security (TLS, formerly Secure Sockets
Layer/SSL) is the recommended way to to protect integrity and
confidentiality while data is transferred over an untrusted
network connection, and to identify the endpoint.
</para>
<section id="sect-Defensive_Coding-TLS-Pitfalls">
<title>Common Pitfalls</title>
<para>
TLS implementations are difficult to use, and most of them lack
a clean API design. The following sections contain
implementation-specific advice, and some generic pitfalls are
mentioned below.
</para>
<itemizedlist>
<listitem>
<para>
Most TLS implementations have questionable default TLS
cipher suites. Most of them enable anonymous Diffie-Hellman
key exchange (but we generally want servers to authenticate
themselves). Many do not disable ciphers which are subject
to brute-force attacks because of restricted key lengths.
Some even disable all variants of AES in the default
configuration.
</para>
<para>
When overriding the cipher suite defaults, it is recommended
to disable all cipher suites which are not present on a
whitelist, instead of simply enabling a list of cipher
suites. This way, if an algorithm is disabled by default in
the TLS implementation in a future security update, the
application will not re-enable it.
</para>
</listitem>
<listitem>
<para>
The name which is used in certificate validation must match
the name provided by the user or configuration file. No host
name canonicalization or IP address lookup must be performed.
</para>
</listitem>
<listitem>
<para>
The TLS handshake has very poor performance if the TCP Nagle
algorithm is active. You should switch on the
<literal>TCP_NODELAY</literal> socket option (at least for the
duration of the handshake), or use the Linux-specific
<literal>TCP_CORK</literal> option.
</para>
<example id="ex-Defensive_Coding-TLS-Nagle">
<title>Deactivating the TCP Nagle algorithm</title>
<xi:include href="snippets/TLS-Nagle.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</listitem>
<listitem>
<para>
Implementing proper session resumption decreases handshake
overhead considerably. This is important if the upper-layer
protocol uses short-lived connections (like most application
of HTTPS).
</para>
</listitem>
<listitem>
<para>
Both client and server should work towards an orderly
connection shutdown, that is send
<literal>close_notify</literal> alerts and respond to them.
This is especially important if the upper-layer protocol
does not provide means to detect connection truncation (like
some uses of HTTP).
</para>
</listitem>
<listitem>
<para>
When implementing a server using event-driven programming,
it is important to handle the TLS handshake properly because
it includes multiple network round-trips which can block
when an ordinary TCP <function>accept</function> would not.
Otherwise, a client which fails to complete the TLS
handshake for some reason will prevent the server from
handling input from other clients.
</para>
</listitem>
<listitem>
<para>
Unlike regular file descriptors, TLS connections cannot be
passed between processes. Some TLS implementations add
additional restrictions, and TLS connections generally
cannot be used across <function>fork</function> function
calls (see <xref
linkend="sect-Defensive_Coding-Tasks-Processes-Fork-Parallel"/>).
</para>
</listitem>
</itemizedlist>
<section id="sect-Defensive_Coding-TLS-OpenSSL">
<title>OpenSSL Pitfalls</title>
<para>
Some OpenSSL function use <emphasis>tri-state return
values</emphasis>. Correct error checking is extremely
important. Several functions return <literal>int</literal>
values with the following meaning:
</para>
<itemizedlist>
<listitem>
<para>
The value <literal>1</literal> indicates success (for
example, a successful signature verification).
</para>
</listitem>
<listitem>
<para>
The value <literal>0</literal> indicates semantic
failure (for example, a signature verification which was
unsuccessful because the signing certificate was
self-signed).
</para>
</listitem>
<listitem>
<para>
The value <literal>-1</literal> indicates a low-level
error in the system, such as failure to allocate memory
using <function>malloc</function>.
</para>
</listitem>
</itemizedlist>
<para>
Treating such tri-state return values as booleans can lead
to security vulnerabilities. Note that some OpenSSL
functions return boolean results or yet another set of
status indicators. Each function needs to be checked
individually.
</para>
<para>
Recovering precise error information is difficult.
<xref linkend="ex-Defensive_Coding-TLS-OpenSSL-Errors"/>
shows how to obtain a more precise error code after a function
call on an <literal>SSL</literal> object has failed. However,
there are still cases where no detailed error information is
available (e.g., if <function>SSL_shutdown</function> fails
due to a connection teardown by the other end).
</para>
<example id="ex-Defensive_Coding-TLS-OpenSSL-Errors">
<title>Obtaining OpenSSL error codes</title>
<xi:include href="snippets/TLS-OpenSSL-Errors.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
The <function>OPENSSL_config</function> function is
documented to never fail. In reality, it can terminate the
entire process if there is a failure accessing the
configuration file. An error message is written to standard
error, but which might not be visible if the function is
called from a daemon process.
</para>
<para>
OpenSSL contains two separate ASN.1 DER decoders. One set
of decoders operate on BIO handles (the input/output stream
abstraction provided by OpenSSL); their decoder function
names start with <literal>d2i_</literal> and end in
<literal>_fp</literal> or <literal>_bio</literal> (e.g.,
<function>d2i_X509_fp</function> or
<function>d2i_X509_bio</function>). These decoders must not
be used for parsing data from untrusted sources; instead,
the variants without the <literal>_fp</literal> and
<literal>_bio</literal> (e.g.,
<function>d2i_X509</function>) shall be used. The BIO
variants have received considerably less testing and are not
very robust.
</para>
<para>
For the same reason, the OpenSSL command line tools (such as
<command>openssl x509</command>) are generally generally less
robust than the actual library code. They use the BIO
functions internally, and not the more robust variants.
</para>
<para>
The command line tools do not always indicate failure in the
exit status of the <application>openssl</application> process.
For instance, a verification failure in <command>openssl
verify</command> result in an exit status of zero.
</para>
<para>
The OpenSSL server and client applications (<command>openssl
s_client</command> and <command>openssl s_server</command>)
are debugging tools and should <emphasis>never</emphasis> be
used as generic clients. For instance, the
<application>s_client</application> tool reacts in a
surprisign way to lines starting with <literal>R</literal> and
<literal>Q</literal>.
</para>
<para>
OpenSSL allows application code to access private key
material over documented interfaces. This can significantly
increase the part of the code base which has to undergo
security certification.
</para>
</section>
<section id="sect-Defensive_Coding-TLS-Pitfalls-GNUTLS">
<title>GNUTLS Pitfalls</title>
<para>
<filename>libgnutls.so.26</filename> links to
<filename>libpthread.so.0</filename>. Loading the threading
library too late causes problems, so the main program should
be linked with <literal>-lpthread</literal> as well. As a
result, it can be difficult to use GNUTLS in a plugin which is
loaded with the <function>dlopen</function> function. Another
side effect is that applications which merely link against
GNUTLS (even without actually using it) may incur a
substantial overhead because other libraries automatically
switch to thread-safe algorithms.
</para>
<para>
The <function>gnutls_global_init</function> function must be
called before using any functionality provided by the library.
This function is not thread-safe, so external locking is
required, but it is not clear which lock should be used.
Omitting the synchronization does not just lead to a memory
leak, as it is suggested in the GNUTLS documentation, but to
undefined behavior because there is no barrier that would
enforce memory ordering.
</para>
<para>
The <function>gnutls_global_deinit</function> function does
not actually deallocate all resources allocated by
<function>gnutls_global_init</function>. It is currently not
thread-safe. Therefore, it is best to avoid calling it
altogether.
</para>
<para>
The X.509 implementation in GNUTLS is rather lenient. For
example, it is possible to create and process X.509
version&nbsp;1 certificates which carry extensions. These
certificates are (correctly) rejected by other
implementations.
</para>
</section>
<section id="sect-Defensive_Coding-TLS-Pitfalls-OpenJDK">
<title>OpenJDK Pitfalls</title>
<para>
The Java cryptographic framework is highly modular. As a
result, when you request an object implementing some
cryptographic functionality, you cannot be completely sure
that you end up with the well-tested, reviewed implementation
in OpenJDK.
</para>
<para>
OpenJDK (in the source code as published by Oracle) and other
implementations of the Java platform require that the system
administrator has installed so-called <emphasis>unlimited
strength jurisdiction policy files</emphasis>. Without this
step, it is not possible to use the secure algorithms which
offer sufficient cryptographic strength. Most downstream
redistributors of OpenJDK remove this requirement.
</para>
<para>
Some versions of OpenJDK use <filename>/dev/random</filename>
as the randomness source for nonces and other random data
which is needed for TLS operation, but does not actually
require physical randomness. As a result, TLS applications
can block, waiting for more bits to become available in
<filename>/dev/random</filename>.
</para>
</section>
<section id="sect-Defensive_Coding-TLS-Pitfalls-NSS">
<title>NSS Pitfalls</title>
<para>
NSS was not designed to be used by other libraries which can
be linked into applications without modifying them. There is
a lot of global state. There does not seem to be a way to
perform required NSS initialization without race conditions.
</para>
<para>
If the NSPR descriptor is in an unexpected state, the
<function>SSL_ForceHandshake</function> function can succeed,
but no TLS handshake takes place, the peer is not
authenticated, and subsequent data is exchanged in the clear.
</para>
<para>
NSS disables itself if it detects that the process underwent a
<function>fork</function> after the library has been
initialized. This behavior is required by the PKCS#11 API
specification.
</para>
</section>
</section>
<section id="sect-Defensive_Coding-TLS-Client">
<title>TLS Clients</title>
<para>
Secure use of TLS in a client generally involves all of the
following steps. (Individual instructions for specific TLS
implementations follow in the next sections.)
</para>
<itemizedlist>
<listitem>
<para>
The client must configure the TLS library to use a set of
trusted root certificates. These certificates are provided
by the system in <filename
class="directory">/etc/ssl/certs</filename> or files derived
from it.
</para>
</listitem>
<listitem>
<para>
The client selects sufficiently strong cryptographic
primitives and disables insecure ones (such as no-op
encryption). Compression and SSL version 2 support must be
disabled (including the SSLv2-compatible handshake).
</para>
</listitem>
<listitem>
<para>
The client initiates the TLS connection. The Server Name
Indication extension should be used if supported by the
TLS implementation. Before switching to the encrypted
connection state, the contents of all input and output
buffers must be discarded.
</para>
</listitem>
<listitem>
<para>
The client needs to validate the peer certificate provided
by the server, that is, the client must check that there
is a cryptographically protected chain from a trusted root
certificate to the peer certificate. (Depending on the
TLS implementation, a TLS handshake can succeed even if
the certificate cannot be validated.)
</para>
</listitem>
<listitem>
<para>
The client must check that the configured or user-provided
server name matches the peer certificate provided by the
server.
</para>
</listitem>
</itemizedlist>
<para>
It is safe to provide users detailed diagnostics on
certificate validation failures. Other causes of handshake
failures and, generally speaking, any details on other errors
reported by the TLS implementation (particularly exception
tracebacks), must not be divulged in ways that make them
accessible to potential attackers. Otherwise, it is possible
to create decryption oracles.
</para>
<important>
<para>
Depending on the application, revocation checking (against
certificate revocations lists or via OCSP) and session
resumption are important aspects of production-quality
client. These aspects are not yet covered.
</para>
</important>
<section>
<title>Implementation TLS Clients With OpenSSL</title>
<para>
In the following code, the error handling is only exploratory.
Proper error handling is required for production use,
especially in libraries.
<!-- FIXME: Cross-reference event-driven I/O section when it
exists and mention that this is really quite complex to
implement. -->
</para>
<para>
The OpenSSL library needs explicit initialization (see <xref
linkend="ex-Defensive_Coding-TLS-OpenSSL-Init"/>).
</para>
<example id="ex-Defensive_Coding-TLS-OpenSSL-Init">
<title>OpenSSL library initialization</title>
<xi:include href="snippets/TLS-Client-OpenSSL-Init.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
After that, a context object has to be created, which acts as
a factory for connection objects (<xref
linkend="ex-Defensive_Coding-TLS-Client-OpenSSL-CTX"/>). We
use an explicit cipher list so that we do not pick up any
strange ciphers when OpenSSL is upgraded. The actual version
requested in the client hello depends on additional
restrictions in the OpenSSL library. If possible, you should
follow the example code and use the default list of trusted
root certificate authorities provided by the system because
you would have to maintain your own set otherwise, which can
be cumbersome.
</para>
<example id="ex-Defensive_Coding-TLS-Client-OpenSSL-CTX">
<title>OpenSSL client context creation</title>
<xi:include href="snippets/TLS-Client-OpenSSL-CTX.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
A single context object can be used to create multiple
connection objects. It is safe to use the same
<literal>SSL_CTX</literal> object for creating connections
concurrently from multiple threads, provided that the
<literal>SSL_CTX</literal> object is not modified (e.g.,
callbacks must not be changed).
</para>
<para>
After creating the TCP socket and disabling the Nagle
algorithm (per <xref
linkend="ex-Defensive_Coding-TLS-Nagle"/>), the actual
connection object needs to be created, as show in <xref
linkend="ex-Defensive_Coding-TLS-Client-OpenSSL-CTX"/>. If
the handshake started by <function>SSL_connect</function>
fails, the <function>ssl_print_error_and_exit</function>
function from <xref
linkend="ex-Defensive_Coding-TLS-OpenSSL-Errors"/> is called.
</para>
<para>
The <function>certificate_validity_override</function>
function provides an opportunity to override the validity of
the certificate in case the OpenSSL check fails. If such
functionality is not required, the call can be removed,
otherwise, the application developer has to implement it.
</para>
<para>
The host name passed to the functions
<function>SSL_set_tlsext_host_name</function> and
<function>X509_check_host</function> must be the name that was
passed to <function>getaddrinfo</function> or a similar name
resolution function. No host name canonicalization must be
performed. The <function>X509_check_host</function> function
used in the final step for host name matching is currently
only implemented in OpenSSL 1.1, which is not released yet.
In case host name matching fails, the function
<function>certificate_host_name_override</function> is called.
This function should check user-specific certificate store, to
allow a connection even if the host name does not match the
certificate. This function has to be provided by the
application developer. Note that the override must be keyed
by both the certificate <emphasis>and</emphasis> the host
name.
</para>
<example id="ex-Defensive_Coding-TLS-Client-OpenSSL-Connect">
<title>Creating a client connection using OpenSSL</title>
<xi:include href="snippets/TLS-Client-OpenSSL-Connect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
The connection object can be used for sending and receiving
data, as in <xref
linkend="ex-Defensive_Coding-TLS-OpenSSL-Connection-Use"/>.
It is also possible to create a <literal>BIO</literal> object
and use the <literal>SSL</literal> object as the underlying
transport, using <function>BIO_set_ssl</function>.
</para>
<example id="ex-Defensive_Coding-TLS-OpenSSL-Connection-Use">
<title>Using an OpenSSL connection to send and receive data</title>
<xi:include href="snippets/TLS-Client-OpenSSL-Connection-Use.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
When it is time to close the connection, the
<function>SSL_shutdown</function> function needs to be called
twice for an orderly, synchronous connection termination
(<xref
linkend="ex-Defensive_Coding-TLS-OpenSSL-Connection-Close"/>).
This exchanges <literal>close_notify</literal> alerts with the
server. The additional logic is required to deal with an
unexpected <literal>close_notify</literal> from the server.
Note that is necessary to explicitly close the underlying
socket after the connection object has been freed.
</para>
<example id="ex-Defensive_Coding-TLS-OpenSSL-Connection-Close">
<title>Closing an OpenSSL connection in an orderly fashion</title>
<xi:include href="snippets/TLS-OpenSSL-Connection-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
<xref linkend="ex-Defensive_Coding-TLS-OpenSSL-Context-Close"/> shows how
to deallocate the context object when it is no longer needed
because no further TLS connections will be established.
</para>
<example id="ex-Defensive_Coding-TLS-OpenSSL-Context-Close">
<title>Closing an OpenSSL connection in an orderly fashion</title>
<xi:include href="snippets/TLS-OpenSSL-Context-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</section>
<section id="sect-Defensive_Coding-TLS-Client-GNUTLS">
<title>Implementation TLS Clients With GNUTLS</title>
<para>
This section describes how to implement a TLS client with full
certificate validation (but without certificate revocation
checking). Note that the error handling in is only
exploratory and needs to be replaced before production use.
</para>
<para>
The GNUTLS library needs explicit initialization:
</para>
<informalexample id="ex-Defensive_Coding-TLS-GNUTLS-Init">
<xi:include href="snippets/TLS-GNUTLS-Init.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
Failing to do so can result in obscure failures in Base64
decoding. See <xref
linkend="sect-Defensive_Coding-TLS-Pitfalls-GNUTLS"/> for
additional aspects of initialization.
</para>
<para>
Before setting up TLS connections, a credentials objects has
to be allocated and initialized with the set of trusted root
CAs (<xref
linkend="ex-Defensive_Coding-TLS-Client-GNUTLS-Credentials"/>).
</para>
<example id="ex-Defensive_Coding-TLS-Client-GNUTLS-Credentials">
<title>Initializing a GNUTLS credentials structure</title>
<xi:include href="snippets/TLS-Client-GNUTLS-Credentials.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
After the last TLS connection has been closed, this credentials
object should be freed:
</para>
<informalexample>
<xi:include href="snippets/TLS-GNUTLS-Credentials-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
During its lifetime, the credentials object can be used to
initialize TLS session objects from multiple threads, provided
that it is not changed.
</para>
<para>
Once the TCP connection has been established, the Nagle
algorithm should be disabled (see <xref
linkend="ex-Defensive_Coding-TLS-Nagle"/>). After that, the
socket can be associated with a new GNUTLS session object.
The previously allocated credentials object provides the set
of root CAs. The <literal>NORMAL</literal> set of cipher
suites and protocols provides a reasonable default. Then the
TLS handshake must be initiated. This is shown in <xref
linkend="ex-Defensive_Coding-TLS-Client-GNUTLS-Connect"/>.
</para>
<example id="ex-Defensive_Coding-TLS-Client-GNUTLS-Connect">
<title>Establishing a TLS client connection using GNUTLS</title>
<xi:include href="snippets/TLS-Client-GNUTLS-Connect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
After the handshake has been completed, the server certificate
needs to be verified (<xref
linkend="ex-Defensive_Coding-TLS-Client-GNUTLS-Verify"/>). In
the example, the user-defined
<function>certificate_validity_override</function> function is
called if the verification fails, so that a separate,
user-specific trust store can be checked. This function call
can be omitted if the functionality is not needed.
</para>
<example id="ex-Defensive_Coding-TLS-Client-GNUTLS-Verify">
<title>Verifying a server certificate using GNUTLS</title>
<xi:include href="snippets/TLS-Client-GNUTLS-Verify.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
In the next step (<xref
linkend="ex-Defensive_Coding-TLS-Client-GNUTLS-Match"/>, the
certificate must be matched against the host name (note the
unusual return value from
<function>gnutls_x509_crt_check_hostname</function>). Again,
an override function
<function>certificate_host_name_override</function> is called.
Note that the override must be keyed to the certificate
<emphasis>and</emphasis> the host name. The function call can
be omitted if the override is not needed.
</para>
<example id="ex-Defensive_Coding-TLS-Client-GNUTLS-Match">
<title>Matching the server host name and certificate in a
GNUTLS client</title>
<xi:include href="snippets/TLS-Client-GNUTLS-Match.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
In newer GNUTLS versions, certificate checking and host name
validation can be combined using the
<function>gnutls_certificate_verify_peers3</function> function.
</para>
<para>
An established TLS session can be used for sending and
receiving data, as in <xref
linkend="ex-Defensive_Coding-TLS-GNUTLS-Use"/>.
</para>
<example id="ex-Defensive_Coding-TLS-GNUTLS-Use">
<title>Using a GNUTLS session</title>
<xi:include href="snippets/TLS-GNUTLS-Use.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
In order to shut down a connection in an orderly manner, you
should call the <function>gnutls_bye</function> function.
Finally, the session object can be deallocated using
<function>gnutls_deinit</function> (see <xref
linkend="ex-Defensive_Coding-TLS-GNUTLS-Disconnect"/>).
</para>
<example id="ex-Defensive_Coding-TLS-GNUTLS-Disconnect">
<title>Using a GNUTLS session</title>
<xi:include href="snippets/TLS-GNUTLS-Disconnect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</section>
<section id="sect-Defensive_Coding-TLS-Client-OpenJDK">
<title>Implementing TLS Clients With OpenJDK</title>
<para>
The examples below use the following cryptographic-related
classes:
</para>
<informalexample>
<xi:include href="snippets/TLS-Client-OpenJDK-Import.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
If compatibility with OpenJDK 6 is required, it is necessary
to use the internal class
<literal>sun.security.util.HostnameChecker</literal>. (The
public OpenJDK API does not provide any support for dissecting
the subject distinguished name of an X.509 certificate, so a
custom-written DER parser is needed—or we have to use an
internal class, which we do below.) In OpenJDK 7, the
<function>setEndpointIdentificationAlgorithm</function> method
was added to the
<literal>javax.net.ssl.SSLParameters</literal> class,
providing an official way to implement host name checking.
</para>
<para>
TLS connections are established using an
<literal>SSLContext</literal> instance. With a properly
configured OpenJDK installation, the
<literal>SunJSSE</literal> provider uses the system-wide set
of trusted root certificate authorities, so no further
configuration is necessary. For backwards compatibility with
OpenJDK&nbsp;6, the <literal>TLSv1</literal> provider has to
be supported as a fall-back option. This is shown in <xref
linkend="ex-Defensive_Coding-TLS-Client-OpenJDK-Context"/>.
</para>
<example id="ex-Defensive_Coding-TLS-Client-OpenJDK-Context">
<title>Setting up an <literal>SSLContext</literal> for OpenJDK TLS
clients</title>
<xi:include href="snippets/TLS-Client-OpenJDK-Context.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
In addition to the context, a TLS parameter object will be
needed which adjusts the cipher suites and protocols (<xref
linkend="ex-Defensive_Coding-TLS-OpenJDK-Parameters"/>). Like
the context, these parameters can be reused for multiple TLS
connections.
</para>
<example id="ex-Defensive_Coding-TLS-OpenJDK-Parameters">
<title>Setting up <literal>SSLParameters</literal> for TLS use
with OpenJDK</title>
<xi:include href="snippets/TLS-OpenJDK-Parameters.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
As initialized above, the parameter object does not yet
require host name checking. This has to be enabled
separately, and this is only supported by OpenJDK 7 and later:
</para>
<informalexample>
<xi:include href="snippets/TLS-Client-OpenJDK-Hostname.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
All application protocols can use the
<literal>"HTTPS"</literal> algorithm. (The algorithms have
minor differences with regard to wildcard handling, which
should not matter in practice.)
</para>
<para>
<xref linkend="ex-Defensive_Coding-TLS-Client-OpenJDK-Connect"/>
shows how to establish the connection. Before the handshake
is initialized, the protocol and cipher configuration has to
be performed, by applying the parameter object
<literal>params</literal>. (After this point, changes to
<literal>params</literal> will not affect this TLS socket.)
As mentioned initially, host name checking requires using an
internal API on OpenJDK 6.
</para>
<example id="ex-Defensive_Coding-TLS-Client-OpenJDK-Connect">
<title>Establishing a TLS connection with OpenJDK</title>
<xi:include href="snippets/TLS-Client-OpenJDK-Connect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
Starting with OpenJDK 7, the last lines can be omitted,
provided that host name verification has been enabled by
calling the
<function>setEndpointIdentificationAlgorithm</function> method
on the <literal>params</literal> object (before it was applied
to the socket).
</para>
<para>
The TLS socket can be used as a regular socket, as shown in
<xref linkend="ex-Defensive_Coding-TLS-Client-OpenJDK-Use"/>.
</para>
<example id="ex-Defensive_Coding-TLS-Client-OpenJDK-Use">
<title>Using a TLS client socket in OpenJDK</title>
<xi:include href="snippets/TLS-Client-OpenJDK-Use.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<section>
<title>Overriding server certificate validation with OpenJDK 6</title>
<para>
Overriding certificate validation requires a custom trust
manager. With OpenJDK 6, the trust manager lacks
information about the TLS session, and to which server the
connection is made. Certificate overrides have to be tied
to specific servers (host names). Consequently, different
<literal>TrustManager</literal> and
<literal>SSLContext</literal> objects have to be used for
different servers.
</para>
<para>
In the trust manager shown in <xref
linkend="ex-Defensive_Coding-TLS-Client-MyTrustManager"/>,
the server certificate is identified by its SHA-256 hash.
</para>
<example id="ex-Defensive_Coding-TLS-Client-MyTrustManager">
<title>A customer trust manager for OpenJDK TLS clients</title>
<xi:include href="snippets/TLS-Client-OpenJDK-MyTrustManager.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
This trust manager has to be passed to the
<literal>init</literal> method of the
<literal>SSLContext</literal> object, as show in <xref
linkend="ex-Defensive_Coding-TLS-Client-Context_For_Cert"/>.
</para>
<example id="ex-Defensive_Coding-TLS-Client-Context_For_Cert">
<title>Using a custom TLS trust manager with OpenJDK</title>
<xi:include href="snippets/TLS-Client-OpenJDK-Context_For_Cert.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
When certificate overrides are in place, host name
verification should not be performed because there is no
security requirement that the host name in the certificate
matches the host name used to establish the connection (and
it often will not). However, without host name
verification, it is not possible to perform transparent
fallback to certification validation using the system
certificate store.
</para>
<para>
The approach described above works with OpenJDK 6 and later
versions. Starting with OpenJDK 7, it is possible to use a
custom subclass of the
<literal>javax.net.ssl.X509ExtendedTrustManager</literal>
class. The OpenJDK TLS implementation will call the new
methods, passing along TLS session information. This can be
used to implement certificate overrides as a fallback (if
certificate or host name verification fails), and a trust
manager object can be used for multiple servers because the
server address is available to the trust manager.
</para>
</section>
</section>
<section id="sect-Defensive_Coding-TLS-Client-NSS">
<title>Implementing TLS Clients With NSS</title>
<para>
The following code shows how to implement a simple TLS client
using NSS. Note that the error handling needs replacing
before production use.
</para>
<para>
Using NSS needs several header files, as shown in
<xref linkend="ex-Defensive_Coding-TLS-NSS-Includes"/>.
</para>
<example id="ex-Defensive_Coding-TLS-NSS-Includes">
<title>Include files for NSS</title>
<xi:include href="snippets/TLS-NSS-Includes.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
Initializing the NSS library is a complex task (<xref
linkend="ex-Defensive_Coding-TLS-NSS-Init"/>). It is not
thread-safe. By default, the library is in export mode, and
all strong ciphers are disabled. Therefore, after creating
the <literal>NSSInitCContext</literal> object, we probe all
the strong ciphers we want to use, and check if at least one
of them is available. If not, we call
<function>NSS_SetDomesticPolicy</function> to switch to
unrestricted policy mode. This function replaces the existing
global cipher suite policy, that is why we avoid calling it
unless absolutely necessary.
</para>
<para>
The simplest way to configured the trusted root certificates
involves loading the <filename>libnssckbi.so</filename> NSS
module with a call to the
<function>SECMOD_LoadUserModule</function> function. The root
certificates are compiled into this module. (The PEM module
for NSS, <filename>libnsspem.so</filename>, offers a way to
load trusted CA certificates from a file.)
</para>
<example id="ex-Defensive_Coding-TLS-NSS-Init">
<title>Initializing the NSS library</title>
<xi:include href="snippets/TLS-NSS-Init.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
Some of the effects of the initialization can be reverted with
the following function calls:
</para>
<informalexample id="ex-Defensive_Coding-TLS-NSS-Close">
<xi:include href="snippets/TLS-NSS-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
After NSS has been initialized, the TLS connection can be
created (<xref
linkend="ex-Defensive_Coding-TLS-Client-NSS-Connect"/>). The
internal <function>PR_ImportTCPSocket</function> function is
used to turn the POSIX file descriptor
<literal>sockfd</literal> into an NSPR file descriptor. (This
function is de-facto part of the NSS public ABI, so it will
not go away.) Creating the TLS-capable file descriptor
requires a <emphasis>model</emphasis> descriptor, which is
configured with the desired set of protocols and ciphers.
(The <literal>good_ciphers</literal> variable is part of <xref
linkend="ex-Defensive_Coding-TLS-NSS-Init"/>.) We cannot
resort to disabling ciphers not on a whitelist because by
default, the AES cipher suites are disabled. The model
descriptor is not needed anymore after TLS support has been
activated for the existing connection descriptor.
</para>
<para>
The call to <function>SSL_BadCertHook</function> can be
omitted if no mechanism to override certificate verification
is needed. The <literal>bad_certificate</literal> function
must check both the host name specified for the connection and
the certificate before granting the override.
</para>
<para>
Triggering the actual handshake requires three function calls,
<function>SSL_ResetHandshake</function>,
<function>SSL_SetURL</function>, and
<function>SSL_ForceHandshake</function>. (If
<function>SSL_ResetHandshake</function> is omitted,
<function>SSL_ForceHandshake</function> will succeed, but the
data will not be encrypted.) During the handshake, the
certificate is verified and matched against the host name.
</para>
<example id="ex-Defensive_Coding-TLS-Client-NSS-Connect">
<title>Creating a TLS connection with NSS</title>
<xi:include href="snippets/TLS-Client-NSS-Connect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
After the connection has been established, <xref
linkend="ex-Defensive_Coding-TLS-NSS-Use"/> shows how to use
the NSPR descriptor to communicate with the server.
</para>
<example id="ex-Defensive_Coding-TLS-NSS-Use">
<title>Using NSS for sending and receiving data</title>
<xi:include href="snippets/TLS-NSS-Use.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
<xref linkend="ex-Defensive_Coding-TLS-Client-NSS-Close"/>
shows how to close the connection.
</para>
<example id="ex-Defensive_Coding-TLS-Client-NSS-Close">
<title>Closing NSS client connections</title>
<xi:include href="snippets/TLS-Client-NSS-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</section>
<section id="sect-Defensive_Coding-TLS-Client-Python">
<title>Implementing TLS Clients With Python</title>
<para>
The Python distribution provides a TLS implementation in the
<literal>ssl</literal> module (actually a wrapper around
OpenSSL). The exported interface is somewhat restricted, so
that the client code shown below does not fully implement the
recommendations in <xref
linkend="sect-Defensive_Coding-TLS-OpenSSL"/>.
</para>
<important>
<para>
Currently, most Python function which accept
<literal>https://</literal> URLs or otherwise implement
HTTPS support do not perform certificate validation at all.
(For example, this is true for the <literal>httplib</literal>
and <literal>xmlrpclib</literal> modules.) If you use
HTTPS, you should not use the built-in HTTP clients. The
<literal>Curl</literal> class in the <literal>curl</literal>
module, as provided by the <literal>python-pycurl</literal>
package implements proper certificate validation.
</para>
</important>
<para>
The <literal>ssl</literal> module currently does not perform
host name checking on the server certificate. <xref
linkend="ex-Defensive_Coding-TLS-Client-Python-check_host_name"/>
shows how to implement certificate matching, using the parsed
certificate returned by <function>getpeercert</function>.
</para>
<example id="ex-Defensive_Coding-TLS-Client-Python-check_host_name">
<title>Implementing TLS host name checking Python (without
wildcard support)</title>
<xi:include href="snippets/TLS-Client-Python-check_host_name.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
To turn a regular, connected TCP socket into a TLS-enabled
socket, use the <function>ssl.wrap_socket</function> function.
The function call in <xref
linkend="ex-Defensive_Coding-TLS-Client-Python-Connect"/>
provides additional arguments to override questionable
defaults in OpenSSL and in the Python module.
</para>
<itemizedlist>
<listitem>
<para>
<literal>ciphers="HIGH:-aNULL:-eNULL:-PSK:RC4-SHA:RC4-MD5"</literal>
selects relatively strong cipher suites with
certificate-based authentication. (The call to
<function>check_host_name</function> function provides
additional protection against anonymous cipher suites.)
</para>
</listitem>
<listitem>
<para>
<literal>ssl_version=ssl.PROTOCOL_TLSv1</literal> disables
SSL 2.0 support. By default, the <literal>ssl</literal>
module sends an SSL 2.0 client hello, which is rejected by
some servers. Ideally, we would request OpenSSL to
negotiated the most recent TLS version supported by the
server and the client, but the Python module does not
allow this.
</para>
</listitem>
<listitem>
<para>
<literal>cert_reqs=ssl.CERT_REQUIRED</literal> turns on
certificate validation.
</para>
</listitem>
<listitem>
<para>
<literal>ca_certs='/etc/ssl/certs/ca-bundle.crt'</literal>
initializes the certificate store with a set of trusted
root CAs. Unfortunately, it is necessary to hard-code
this path into applications because the default path in
OpenSSL is not available through the Python
<literal>ssl</literal> module.
</para>
</listitem>
</itemizedlist>
<para>
The <literal>ssl</literal> module (and OpenSSL) perform
certificate validation, but the certificate must be compared
manually against the host name, by calling the
<function>check_host_name</function> defined above.
</para>
<example id="ex-Defensive_Coding-TLS-Client-Python-Connect">
<title>Establishing a TLS client connection with Python</title>
<xi:include href="snippets/TLS-Client-Python-Connect.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
After the connection has been established, the TLS socket can
be used like a regular socket:
</para>
<informalexample>
<xi:include href="snippets/TLS-Python-Use.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
<para>
Closing the TLS socket is straightforward as well:
</para>
<informalexample>
<xi:include href="snippets/TLS-Python-Close.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</informalexample>
</section>
</section>
</chapter>

View file

@ -1,4 +0,0 @@
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<include rules="../../schemas.xml"/>
</locatingRules>

View file

@ -1,17 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 72
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/Python
END
Language.xml
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Python/Language.xml
END
schemas.xml
K 25
svn:wc:ra_dav:version-url
V 84
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/Python/schemas.xml
END

View file

@ -1,6 +0,0 @@
K 10
svn:ignore
V 9
snippets
END

View file

@ -1,96 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US/Python
https://svn.devel.redhat.com/repos/product-security
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
Language.xml
file
2013-01-10T17:17:40.317763Z
00327c6f05b6d4d52a043fe8caff08b9
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
2620
schemas.xml
file
2013-01-10T17:17:40.317763Z
769bc2635d36b318161574a1adf2f6e7
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
150

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,74 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Python">
<title>The Python Programming Language</title>
<para>
Python provides memory safety by default, so low-level security
vulnerabilities are rare and typically needs fixing the Python
interpreter or standard library itself.
</para>
<para>
Other sections with Python-specific advice include:
</para>
<itemizedlist>
<listitem>
<para>
<xref linkend="chap-Defensive_Coding-Tasks-Temporary_Files"/>
</para>
</listitem>
<listitem>
<para>
<xref linkend="sect-Defensive_Coding-Tasks-Processes-Creation"/>
</para>
</listitem>
<listitem>
<para>
<xref linkend="chap-Defensive_Coding-Tasks-Serialization"/>, in
particular <xref linkend="sect-Defensive_Coding-Tasks-Serialization-Library"/>
</para>
</listitem>
<listitem>
<para>
<xref linkend="sect-Defensive_Coding-Tasks-Cryptography-Randomness"/>
</para>
</listitem>
</itemizedlist>
<section>
<title>Dangerous standard library features</title>
<para>
Some areas of the standard library, notably the
<literal>ctypes</literal> module, do not provide memory safety
guarantees comparable to the rest of Python. If such
functionality is used, the advice in <xref
linkend="sect-Defensive_Coding-C-Language"/> should be followed.
</para>
</section>
<section>
<title>Run-time compilation and code generation</title>
<para>
The following Python functions and statements related to code
execution should be avoided:
</para>
<itemizedlist>
<listitem><para><function>compile</function></para></listitem>
<listitem><para><function>eval</function></para></listitem>
<listitem><para><literal>exec</literal></para></listitem>
<listitem><para><function>execfile</function></para></listitem>
</itemizedlist>
<para>
If you need to parse integers or floating point values, use the
<function>int</function> and <function>float</function>
functions instead of <function>eval</function>. Sandboxing
untrusted Python code does not work reliably.
</para>
</section>
<section>
<title>Sandboxing</title>
<para>
The <literal>rexec</literal> Python module cannot safely sandbox
untrusted code and should not be used. The standard CPython
implementation is not suitable for sandboxing.
</para>
</section>
</chapter>

View file

@ -1,4 +0,0 @@
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<include rules="../../schemas.xml"/>
</locatingRules>

View file

@ -1,59 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 71
/repos/product-security/!svn/ver/294/defensive-coding/trunk/en-US/Tasks
END
Descriptors.xml
K 25
svn:wc:ra_dav:version-url
V 87
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/Descriptors.xml
END
File_System.xml
K 25
svn:wc:ra_dav:version-url
V 87
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/File_System.xml
END
schemas.xml
K 25
svn:wc:ra_dav:version-url
V 83
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/Tasks/schemas.xml
END
Temporary_Files.xml
K 25
svn:wc:ra_dav:version-url
V 91
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/Temporary_Files.xml
END
Locking.xml
K 25
svn:wc:ra_dav:version-url
V 83
/repos/product-security/!svn/ver/292/defensive-coding/trunk/en-US/Tasks/Locking.xml
END
Processes.xml
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/Processes.xml
END
Cryptography.xml
K 25
svn:wc:ra_dav:version-url
V 88
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/Cryptography.xml
END
Library_Design.xml
K 25
svn:wc:ra_dav:version-url
V 90
/repos/product-security/!svn/ver/281/defensive-coding/trunk/en-US/Tasks/Library_Design.xml
END
Serialization.xml
K 25
svn:wc:ra_dav:version-url
V 89
/repos/product-security/!svn/ver/294/defensive-coding/trunk/en-US/Tasks/Serialization.xml
END

View file

@ -1,6 +0,0 @@
K 10
svn:ignore
V 9
snippets
END

View file

@ -1,334 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/en-US/Tasks
https://svn.devel.redhat.com/repos/product-security
2012-12-19T14:04:47.671665Z
294
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
Descriptors.xml
file
2013-01-10T17:17:40.559764Z
a351aa6cb2ff552031644c821a1562d7
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
10998
File_System.xml
file
2013-01-10T17:17:40.559764Z
bf703da532d93a853979e09b04a2f21f
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
14682
schemas.xml
file
2013-01-10T17:17:40.559764Z
769bc2635d36b318161574a1adf2f6e7
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
150
Temporary_Files.xml
file
2013-01-10T17:17:40.559764Z
c3db39345e4baab59ab738e3912a73ca
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
10131
Locking.xml
file
2013-01-10T17:17:40.560764Z
f44d72a773df72e1e5f5101a3c9a66af
2012-12-14T10:18:44.472257Z
292
fweimer@REDHAT.COM
has-props
226
Processes.xml
file
2013-01-10T17:17:40.560764Z
46f3a354235a27a94fd915ebe73f3db5
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
18957
Cryptography.xml
file
2013-01-10T17:17:40.560764Z
dfd01ca248a464c524b4badbdce2679c
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
4180
Library_Design.xml
file
2013-01-10T17:17:40.560764Z
db4969b9abc8c5d9272ea395488a8896
2012-12-13T13:25:23.103424Z
281
fweimer@REDHAT.COM
has-props
7787
Serialization.xml
file
2013-01-10T17:17:40.560764Z
bc8c4dc03264854d83747d8f2cd1ab6f
2012-12-19T14:04:47.671665Z
294
fweimer@REDHAT.COM
has-props
16361

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,5 +0,0 @@
K 13
svn:mime-type
V 8
text/xml
END

View file

@ -1,111 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Tasks-Cryptography">
<title>Cryptography</title>
<section>
<title>Primitives</title>
<para>
Chosing from the following cryptographic primitives is
recommended:
</para>
<itemizedlist>
<listitem><para>RSA with 2048 bit keys and OAEP</para></listitem>
<listitem><para>AES-128 in CBC mode</para></listitem>
<listitem><para>SHA-256</para></listitem>
<listitem><para>HMAC-SHA-256</para></listitem>
<listitem><para>HMAC-SHA-1</para></listitem>
</itemizedlist>
<para>
Other cryptographic algorithms can be used if they are required
for interoperability with existing software:
</para>
<itemizedlist>
<listitem><para>RSA with key sizes larger than 1024
and legacy padding</para></listitem>
<listitem><para>AES-192</para></listitem>
<listitem><para>AES-256</para></listitem>
<listitem><para>3DES (triple DES, with two or three 56 bit keys)</para></listitem>
<listitem><para>RC4 (but very, very strongly discouraged)</para></listitem>
<listitem><para>SHA-1</para></listitem>
<listitem><para>HMAC-MD5</para></listitem>
</itemizedlist>
<important>
<title>Important</title>
<para>
These primitives are difficult to use in a secure way. Custom
implementation of security protocols should be avoided. For
protecting confidentiality and integrity of network
transmissions, TLS should be used (<xref
linkend="chap-Defensive_Coding-TLS"/>).
</para>
</important>
<!-- TODO: More algorithms are available in the NIST documents
linked from: http://wiki.brq.redhat.com/SecurityTechnologies/FIPS -->
</section>
<section>
<title id="sect-Defensive_Coding-Tasks-Cryptography-Randomness">Randomness</title>
<para>
The following facilities can be used to generate unpredictable
and non-repeating values. When these functions are used without
special safeguards, each individual rnadom value should be at
least 12 bytes long.
</para>
<itemizedlist>
<listitem>
<para><function>PK11_GenerateRandom</function> in the NSS library
(usable for high data rates)</para>
</listitem>
<listitem>
<para><function>RAND_bytes</function> in the OpenSSL library
(usable for high data rates)</para>
</listitem>
<listitem>
<para><function>gnutls_rnd</function> in GNUTLS, with
<literal>GNUTLS_RND_RANDOM</literal> as the first argument
(usable for high data rates)</para>
</listitem>
<listitem>
<para><type>java.security.SecureRandom</type> in Java
(usable for high data rates)</para>
</listitem>
<listitem>
<para><function>os.urandom</function> in Python</para>
</listitem>
<listitem>
<para>Reading from the <filename>/dev/urandom</filename>
character device</para>
</listitem>
</itemizedlist>
<para>
All these functions should be non-blocking, and they should not
wait until physical randomness becomes available. (Some
cryptography providers for Java can cause
<type>java.security.SecureRandom</type> to block, however.)
Those functions which do not obtain all bits directly from
<filename>/dev/urandom</filename> are suitable for high data
rates because they do not deplete the system-wide entropy pool.
</para>
<important>
<title>Difficult to use API</title>
<para>
Both <function>RAND_bytes</function> and
<function>PK11_GenerateRandom</function> have three-state
return values (with conflicting meanings). Careful error
checking is required. Please review the documentation when
using these functions.
</para>
</important>
<para>
Other sources of randomness should be considered predictable.
</para>
<para>
Generating randomness for cryptographic keys in long-term use
may need different steps and is best left to cryptographic
libraries.
</para>
</section>
</chapter>

View file

@ -1,266 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="sect-Defensive_Coding-Tasks-Descriptors">
<title>File Descriptor Management</title>
<para>
File descriptors underlie all input/output mechanisms offered by
the system. They are used to implementation the <literal>FILE
*</literal>-based functions found in
<literal>&lt;stdio.h&gt;</literal>, and all the file and network
communication facilities provided by the Python and Java
environments are eventually implemented in them.
</para>
<para>
File descriptors are small, non-negative integers in userspace,
and are backed on the kernel side with complicated data structures
which can sometimes grow very large.
</para>
<section>
<title>Closing descriptors</title>
<para>
If a descriptor is no longer used by a program and is not closed
explicitly, its number cannot be reused (which is problematic in
itself, see <xref
linkend="sect-Defensive_Coding-Tasks-Descriptors-Limit"/>), and
the kernel resources are not freed. Therefore, it is important
to close all descriptors at the earlierst point in time
possible, but not earlier.
</para>
<section>
<title>Error handling during descriptor close</title>
<para>
The <function>close</function> system call is always
successful in the sense that the passed file descriptor is
never valid after the function has been called. However,
<function>close</function> still can return an error, for
example if there was a file system failure. But this error is
not very useful because the absence of an error does not mean
that all caches have been emptied and previous writes have
been made durable. Programs which need such guarantees must
open files with <literal>O_SYNC</literal> or use
<literal>fsync</literal> or <literal>fdatasync</literal>, and
may also have to <literal>fsync</literal> the directory
containing the file.
</para>
</section>
<section>
<title>Closing descriptors and race conditions</title>
<para>
Unlike process IDs, which are recycle only gradually, the
kernel always allocates the lowest unused file descriptor when
a new descriptor is created. This means that in a
multi-threaded program which constantly opens and closes file
descriptors, descriptors are reused very quickly. Unless
descriptor closing and other operations on the same file
descriptor are synchronized (typically, using a mutex), there
will be race coniditons and I/O operations will be applied to
the wrong file descriptor.
</para>
<para>
Sometimes, it is necessary to close a file descriptor
concurrently, while another thread might be about to use it in
a system call. In order to support this, a program needs to
create a single special file descriptor, one on which all I/O
operations fail. One way to achieve this is to use
<function>socketpair</function>, close one of the descriptors,
and call <literal>shutdown(fd, SHUTRDWR)</literal> on the
other.
</para>
<para>
When a descriptor is closed concurrently, the program does not
call <function>close</function> on the descriptor. Instead it
program uses <function>dup2</function> to replace the
descriptor to be closed with the dummy descriptor created
earlier. This way, the kernel will not reuse the descriptor,
but it will carry out all other steps associated with calling
a descriptor (for instance, if the descriptor refers to a
stream socket, the peer will be notified).
</para>
<para>
This is just a sketch, and many details are missing.
Additional data structures are needed to determine when it is
safe to really close the descriptor, and proper locking is
required for that.
</para>
</section>
<section>
<title>Lingering state after close</title>
<para>
By default, closing a stream socket returns immediately, and
the kernel will try to send the data in the background. This
means that it is impossible to implement accurate accounting
of network-related resource utilization from userspace.
</para>
<para>
The <literal>SO_LINGER</literal> socket option alters the
behavior of <function>close</function>, so that it will return
only after the lingering data has been processed, either by
sending it to the peer successfully, or by discarding it after
the configured timeout. However, there is no interface which
could perform this operation in the background, so a separate
userspace thread is needed for each <function>close</function>
call, causing scalability issues.
</para>
<para>
Currently, there is no application-level countermeasure which
applies universally. Mitigation is possible with
<application>iptables</application> (the
<literal>connlimit</literal> match type in particular) and
specialized filtering devices for denial-of-service network
traffic.
</para>
<para>
These problems are not related to the
<literal>TIME_WAIT</literal> state commonly seen in
<application>netstat</application> output. The kernel
automatically expires such sockets if necessary.
</para>
</section>
</section>
<section id="sect-Defensive_Coding-Tasks-Descriptors-Child_Processes">
<title>Preventing file descriptor leaks to child processes</title>
<para>
Child processes created with <function>fork</function> share
the initial set of file descriptors with their parent
process. By default, file descriptors are also preserved if
a new process image is created with <function>execve</function>
(or any of the other functions such as <function>system</function>
or <function>posix_spawn</function>).
</para>
<para>
Usually, this behavior is not desirable. There are two ways to
turn it off, that is, to prevent new process images from
inheriting the file descriptors in the parent process:
</para>
<itemizedlist>
<listitem>
<para>
Set the close-on-exec flag on all newly created file
descriptors. Traditionally, this flag is controlled by the
<literal>FD_CLOEXEC</literal> flag, using
<literal>F_GETFD</literal> and <literal>F_SETFD</literal>
operations of the <function>fcntl</function> function.
</para>
<para>
However, in a multi-threaded process, there is a race
condition: a subprocess could have been created between the
time the descriptor was created and the
<literal>FD_CLOEXEC</literal> was set. Therefore, many system
calls which create descriptors (such as
<function>open</function> and <function>openat</function>)
now accept the <function>O_CLOEXEC</function> flag
(<function>SOCK_CLOEXEC</function> for
<function>socket</function> and
<function>socketpair</function>), which cause the
<literal>FD_CLOEXEC</literal> flag to be set for the file
descriptor in an atomic fashion. In addition, a few new
systems calls were introduced, such as
<function>pipe2</function> and <function>dup3</function>.
</para>
<para>
The downside of this approach is that every descriptor needs
to receive special treatment at the time of creation,
otherwise it is not completely effective.
</para>
</listitem>
<listitem>
<para>
After calling <function>fork</function>, but before creating
a new process image with <function>execve</function>, all
file descriptors which the child process will not need are
closed.
</para>
<para>
Traditionally, this was implemented as a loop over file
descriptors ranging from <literal>3</literal> to
<literal>255</literal> and later <literal>1023</literal>.
But this is only an approximatio because it is possible to
create file descriptors outside this range easily (see <xref
linkend="sect-Defensive_Coding-Tasks-Descriptors-Limit"/>).
Another approach reads <filename>/proc/self/fd</filename>
and closes the unexpected descriptors listed there, but this
approach is much slower.
</para>
</listitem>
</itemizedlist>
<para>
At present, environments which care about file descriptor
leakage implement the second approach. OpenJDK 6 and 7
are among them.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Descriptors-Limit">
<title>Dealing with the <function>select</function> limit</title>
<para>
By default, a user is allowed to open only 1024 files in a
single process, but the system administrator can easily change
this limit (which is necessary for busy network servers).
However, there is another restriction which is more difficult to
overcome.
</para>
<para>
The <function>select</function> function only supports a
maximum of <literal>FD_SETSIZE</literal> file descriptors
(that is, the maximum permitted value for a file descriptor
is <literal>FD_SETSIZE - 1</literal>, usually 1023.) If a
process opens many files, descriptors may exceed such
limits. It is impossible to query such descriptors using
<function>select</function>.
</para>
<para>
If a library which creates many file descriptors is used in
the same process as a library which uses
<function>select</function>, at least one of them needs to
be changed. <!-- ??? refer to event-driven programming -->
Calls to <function>select</function> can be replaced with
calls to <function>poll</function> or another event handling
mechanism.
</para>
<para>
Alternatively, the library with high descriptor usage can
relocate descriptors above the <literal>FD_SETSIZE</literal>
limit using the following procedure.
</para>
<itemizedlist>
<listitem>
<para>
Create the file descriptor <literal>fd</literal> as
usual, preferably with the <literal>O_CLOEXEC</literal>
flag.
</para>
</listitem>
<listitem>
<para>
Before doing anything else with the descriptor
<literal>fd</literal>, invoke:
</para>
<programlisting language="C">
int newfd = fcntl(fd, F_DUPFD_CLOEXEC, (long)FD_SETSIZE);
</programlisting>
</listitem>
<listitem>
<para>
Check that <literal>newfd</literal> result is
non-negative, otherwise close <literal>fd</literal> and
report an error, and return.
</para>
</listitem>
<listitem>
<para>
Close <literal>fd</literal> and continue to use
<literal>newfd</literal>.
</para>
</listitem>
</itemizedlist>
<para>
The new descriptor has been allocated above the
<literal>FD_SETSIZE</literal>. Even though this algorithm
is racy in the sense that the <literal>FD_SETSIZE</literal>
first descriptors could fill up, a very high degree of
physical parallelism is required before this becomes a problem.
</para>
</section>
</chapter>

View file

@ -1,339 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Tasks-File_System">
<title>File system manipulation</title>
<para>
In this chapter, we discuss general file system manipulation, with
a focus on access files and directories to which an other,
potentially untrusted user has write access.
</para>
<para>
Temporary files are covered in their own chapter, <xref
linkend="chap-Defensive_Coding-Tasks-Temporary_Files"/>.
</para>
<section id="sect-Defensive_Coding-Tasks-File_System-Unowned">
<title>Working with files and directories owned by other users</title>
<para>
Sometimes, it is necessary to operate on files and directories
owned by other (potentially untrusted) users. For example, a
system administrator could remove the home directory of a user,
or a package manager could update a file in a directory which is
owned by an application-specific user. This differs from
accessing the file system as a specific user; see
<xref linkend="sect-Defensive_Coding-Tasks-File_System-Foreign"/>.
</para>
<para>
Accessing files across trust boundaries faces several
challenges, particularly if an entire directory tree is being
traversed:
</para>
<orderedlist>
<listitem>
<para>
Another user might add file names to a writable directory at
any time. This can interfere with file creation and the
order of names returned by <function>readdir</function>.
</para>
</listitem>
<listitem>
<para>
Merely opening and closing a file can have side effects.
For instance, an automounter can be triggered, or a tape
device rewound. Opening a file on a local file system can
block indefinitely, due to mandatory file locking, unless
the <literal>O_NONBLOCK</literal> flag is specified.
</para>
</listitem>
<listitem>
<para>
Hard links and symbolic links can redirect the effect of
file system operations in unexpected ways. The
<literal>O_NOFOLLOW</literal> and
<literal>AT_SYMLINK_NOFOLLOW</literal> variants of system
calls only affected final path name component.
</para>
</listitem>
<listitem>
<para>
The structure of a directory tree can change. For example,
the parent directory of what used to be a subdirectory
within the directory tree being processed could suddenly
point outside that directory tree.
</para>
</listitem>
</orderedlist>
<para>
Files should always be created with the
<literal>O_CREAT</literal> and <literal>O_EXCL</literal> flags,
so that creating the file will fail if it already exists. This
guards against the unexpected appearance of file names, either
due to creation of a new file, or hard-linking of an existing
file. In multi-threaded programs, rather than manipulating the
umask, create the files with mode <literal>000</literal> if
possible, and adjust it afterwards with
<function>fchmod</function>.
</para>
<para>
To avoid issues related to symbolic links and directory tree
restructuring, the “<literal>at</literal>” variants of system
calls have to be used (that is, functions like
<function>openat</function>, <function>fchownat</function>,
<function>fchmodat</function>, and
<function>unlinkat</function>, together with
<literal>O_NOFOLLOW</literal> or
<literal>AT_SYMLINK_NOFOLLOW</literal>). Path names passed to
these functions must have just a single component (that is,
without a slash). When descending, the descriptors of parent
directories must be kept open. The missing
<literal>opendirat</literal> function can be emulated with
<literal>openat</literal> (with an
<literal>O_DIRECTORY</literal> flag, to avoid opening special
files with side effects), followed by
<literal>fdopendir</literal>.
</para>
<para>
If the “<literal>at</literal>” functions are not available, it
is possible to emulate them by changing the current directory.
(Obviously, this only works if the process is not multi-threaded.)
<function>fchdir</function> has to be used to change the current
directory, and the descriptors of the parent directories have to
be kept open, just as with the “<literal>at</literal>”-based
approach. <literal>chdir("...")</literal> is unsafe because it
might ascend outside the intended directory tree.
</para>
<para>
This “<literal>at</literal>” function emulation is currently
required when manipulating extended attributes. In this case,
the <function>lsetxattr</function> function can be used, with a
relative path name consisting of a single component. This also
applies to SELinux contexts and the
<function>lsetfilecon</function> function.
</para>
<para>
Currently, it is not possible to avoid opening special files
<emphasis>and</emphasis> changes to files with hard links if the
directory containing them is owned by an untrusted user.
(Device nodes can be hard-linked, just as regular files.)
<function>fchmodat</function> and <function>fchownat</function>
affect files whose link count is greater than one. But opening
the files, checking that the link count is one with
<function>fstat</function>, and using
<function>fchmod</function> and <function>fchown</function> on
the file descriptor may have unwanted side effects, due to item
2 above. When creating directories, it is therefore important
to change the ownership and permissions only after it has been
fully created. Until that point, file names are stable, and no
files with unexpected hard links can be introduced.
</para>
<para>
Similarly, when just reading a directory owned by an untrusted
user, it is currently impossible to reliably avoid opening
special files.
</para>
<para>
There is no workaround against the instability of the file list
returned by <function>readdir</function>. Concurrent
modification of the directory can result in a list of files
being returned which never actually existed on disk.
</para>
<para>
Hard links and symbolic links can be safely deleted using
<function>unlinkat</function> without further checks because
deletion only affects the name within the directory tree being
processed.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-File_System-Foreign">
<title>Accessing the file system as a different user</title>
<para>
This section deals with access to the file system as a specific
user. This is different from accessing files and directories owned by a
different, potentially untrusted user; see <xref
linkend="sect-Defensive_Coding-Tasks-File_System-Foreign"/>.
</para>
<para>
One approach is to spawn a child process which runs under the
target user and group IDs (both effective and real IDs). Note
that this child process can block indefinitely, even when
processing regular files only. For example, a special FUSE file
system could cause the process to hang in uninterruptible sleep
inside a <function>stat</function> system call.
</para>
<para>
An existing process could change its user and group ID using
<function>setfsuid</function> and <function>setfsgid</function>.
(These functions are preferred over <function>seteuid</function>
and <function>setegid</function> because they do not allow the
impersonated user to send signals to the process.) These
functions are not thread safe. In multi-threaded processes,
these operations need to be performed in a single-threaded child
process. Unexpected blocking may occur as well.
</para>
<para>
It is not recommended to try to reimplement the kernel
permission checks in user space because the required checks are
complex. It is also very difficult to avoid race conditions
during path name resolution.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-File_System-Limits">
<title>File system limits</title>
<para>
For historical reasons, there are preprocessor constants such as
<literal>PATH_MAX</literal>, <literal>NAME_MAX</literal>.
However, on most systems, the length of canonical path names
(absolute path names with all symbolic links resolved, as
returned by <function>realpath</function> or
<function>canonicalize_file_name</function>) can exceed
<literal>PATH_MAX</literal> bytes, and individual file name
components can be longer than <literal>NAME_MAX</literal>. This
is also true of the <literal>_PC_PATH_MAX</literal> and
<literal>_PC_NAME_MAX</literal> values returned by
<function>pathconf</function>, and the
<literal>f_namemax</literal> member of <literal>struct
statvfs</literal>. Therefore, these constants should not be
used. This is also reason why the
<function>readdir_r</function> should never be used (instead,
use <function>readdir</function>).
</para>
<para>
You should not write code in a way that assumes that there is an
upper limit on the number of subdirectories of a directory, the
number of regular files in a directory, or the link count of an
inode.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-File_System-Features">
<title>File system features</title>
<para>
Not all file systems support all features. This makes it very
difficult to write general-purpose tools for copying files. For
example, a copy operation intending to preserve file permissions
will generally fail when copying to a FAT file system.
</para>
<itemizedlist>
<listitem>
<para>
Some file systems are case-insensitive. Most should be
case-preserving, though.
</para>
</listitem>
<listitem>
<para>
Name length limits vary greatly, from eight to thousands of
bytes. Path length limits differ as well. Most systems
impose an upper bound on path names passed to the kernel,
but using relative path names, it is possible to create and
access files whose absolute path name is essentially of
unbounded length.
</para>
</listitem>
<listitem>
<para>
Some file systems do not store names as fairly unrestricted
byte sequences, as it has been traditionally the case on GNU
systems. This means that some byte sequences (outside the
POSIX safe character set) are not valid names. Conversely,
names of existing files may not be representable as byte
sequences, and the files are thus inaccessible on GNU
systems. Some file systems perform Unicode canonicalization
on file names. These file systems preserve case, but
reading the name of a just-created file using
<function>readdir</function> might still result in a
different byte sequence.
</para>
</listitem>
<listitem>
<para>
Permissions and owners are not universally supported (and
SUID/SGID bits may not be available). For example, FAT file
systems assign ownership based on a mount option, and
generally mark all files as executable. Any attempt to
change permissions would result in an error.
</para>
</listitem>
<listitem>
<para>
Non-regular files (device nodes, FIFOs) are not generally
available.
</para>
</listitem>
<listitem>
<para>
Only on some file systems, files can have holes, that is,
not all of their contents is backed by disk storage.
</para>
</listitem>
<listitem>
<para>
<function>ioctl</function> support (even fairly generic
functionality such as <literal>FIEMAP</literal> for
discovering physical file layout and holes) is
file-system-specific.
</para>
</listitem>
<listitem>
<para>
Not all file systems support extended attributes, ACLs and
SELinux metadata. Size and naming restriction on extended
attributes vary.
</para>
</listitem>
<listitem>
<para>
Hard links may not be supported at all (FAT) or only within
the same directory (AFS). Symbolic links may not be
available, either. Reflinks (hard links with copy-on-write
semantics) are still very rare. Recent systems restrict
creation of hard links to users which own the target file or
have read/write access to it, but older systems do not.
</para>
</listitem>
<listitem>
<para>
Renaming (or moving) files using <function>rename</function>
can fail (even when <function>stat</function> indicates that
the source and target directories are located on the same
file system). This system call should work if the old and
new paths are located in the same directory, though.
</para>
</listitem>
<listitem>
<para>
Locking semantics vary among file systems. This affects
advisory and mandatory locks. For example, some network
file systems do not allow deleting files which are opened by
any process.
</para>
</listitem>
<listitem>
<para>
Resolution of time stamps varies from two seconds to
nanoseconds. Not all time stamps are available on all file
systems. File creation time (<emphasis>birth
time</emphasis>) is not exposed over the
<function>stat</function>/<function>fstat</function>
interface, even if stored by the file system.
</para>
</listitem>
</itemizedlist>
</section>
<section id="sect-Defensive_Coding-Tasks-File_System-Free_Space">
<title>Checking free space</title>
<para>
The <function>statvfs</function> and
<function>fstatvfs</function> functions allow programs to
examine the number of available blocks and inodes, through the
members <literal>f_bfree</literal>, <literal>f_bavail</literal>,
<literal>f_ffree</literal>, and <literal>f_favail</literal> of
<literal>struct statvfs</literal>. Some file systems return
fictional values in the <literal>f_ffree</literal> and
<literal>f_favail</literal> fields, so the only reliable way to
discover if the file system still has space for a file is to try
to create it. The <literal>f_bfree</literal> field should be
reasonably accurate, though.
</para>
</section>
</chapter>

View file

@ -1,195 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Tasks-Library_Design">
<title>Library Design</title>
<para>
Throught this section, the term <emphasis>client code</emphasis>
refers to applications and other libraries using the library.
</para>
<section>
<title>State management</title>
<para>
</para>
<section>
<title>Global state</title>
<para>
Global state should be avoided.
</para>
<para>
If this is impossible, the global state must be protected with
a lock. For C/C++, you can use the
<function>pthread_mutex_lock</function>
and <function>pthread_mutex_unlock</function>
functions without linking against <literal>-lpthread</literal>
because the system provides stubs for non-threaded processes.
</para>
<para>
For compatibility with <function>fork</function>, these locks
should be acquired and released in helpers registered with
<function>pthread_atfork</function>. This function is not
available without <literal>-lpthread</literal>, so you need to
use <function>dlsym</function> or a weak symbol to obtain its
address.
</para>
<para>
If you need <function>fork</function> protection for other
reasons, you should store the process ID and compare it to the
value returned by <function>getpid</function> each time you
access the global state. (<function>getpid</function> is not
implemented as a system call and is fast.) If the value
changes, you know that you have to re-create the state object.
(This needs to be combined with locking, of course.)
</para>
</section>
<section>
<title>Handles</title>
<para>
Library state should be kept behind a curtain. Client code
should receive only a handle. In C, the handle can be a
pointer to an incomplete <literal>struct</literal>. In C++,
the handle can be a pointer to an abstract base class, or it
can be hidden using the pointer-to-implementation idiom.
</para>
<para>
The library should provide functions for creating and
destroying handles. (In C++, it is possible to use virtual
destructors for the latter.) Consistency between creation and
destruction of handles is strongly recommended: If the client
code created a handle, it is the responsibility of the client
code to destroy it. (This is not always possible or
convenient, so sometimes, a transfer of ownership has to
happen.)
</para>
<para>
Using handles ensures that it is possible to change the way
the library represents state in a way that is transparent to
client code. This is important to facilitate security updates
and many other code changes.
</para>
<para>
It is not always necessary to protect state behind a handle
with a lock. This depends on the level of thread safety
the library provides.
</para>
</section>
</section>
<section>
<title>Object orientation</title>
<para>
Classes should be either designed as base classes, or it should
be impossible to use them as base classes (like
<literal>final</literal> classes in Java). Classes which are
not designed for inheritance and are used as base classes
nevertheless create potential maintenance hazards because it is
difficult to predict how client code will react when calls to
virtual methods are added, reordered or removed.
</para>
<para>
Virtual member functions can be used as callbacks. See
<xref linkend="sect-Defensive_Coding-Tasks-Library_Design-Callbacks"/>
for some of the challenges involved.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Library_Design-Callbacks">
<title>Callbacks</title>
<para>
Higher-order code is difficult to analyze for humans and
computers alike, so it should be avoided. Often, an
iterator-based interface (a library function which is called
repeatedly by client code and returns a stream of events) leads
to a better design which is easier to document and use.
</para>
<para>
If callbacks are unavoidable, some guidelines for them follow.
</para>
<para>
In modern C++ code, <literal>std::function</literal> objects
should be used for callbacks.
</para>
<para>
In older C++ code and in C code, all callbacks must have an
additional closure parameter of type <literal>void *</literal>,
the value of which can be specified by client code. If
possible, the value of the closure parameter should be provided
by client code at the same time a specific callback is
registered (or specified as a function argument). If a single
closure parameter is shared by multiple callbacks, flexibility
is greatly reduced, and conflicts between different pieces of
client code using the same library object could be unresolvable.
In some cases, it makes sense to provide a de-registration
callback which can be used to destroy the closure parameter when
the callback is no longer used.
</para>
<para>
Callbacks can throw exceptions or call
<function>longjmp</function>. If possible, all library objects
should remain in a valid state. (All further operations on them
can fail, but it should be possible to deallocate them without
causing resource leaks.)
</para>
<para>
The presence of callbacks raises the question if functions
provided by the library are <emphasis>reentrant</emphasis>.
Unless a library was designed for such use, bad things will
happen if a callback function uses functions in the same library
(particularly if they are invoked on the same objects and
manipulate the same state). When the callback is invoked, the
library can be in an inconsistent state. Reentrant functions
are more difficult to write than thread-safe functions (by
definition, simple locking would immediately lead to deadlocks).
It is also difficult to decide what to do when destruction of an
object which is currently processing a callback is requested.
</para>
</section>
<section>
<title>Process attributes</title>
<para>
Several attributes are global and affect all code in the
process, not just the library that manipulates them.
</para>
<itemizedlist>
<listitem><para>
environment variables
(see <xref linkend="sect-Defensive_Coding-Tasks-secure_getenv"/>)
</para></listitem>
<listitem><para>
umask
</para></listitem>
<listitem><para>
user IDs, group IDs and capabilities
</para></listitem>
<listitem><para>
current working directory
</para></listitem>
<listitem><para>
signal handlers, signal masks and signal delivery
</para></listitem>
<listitem><para>
file locks (especially <function>fcntl</function> locks
behave in surprising ways, not just in a multi-threaded
environment)
</para></listitem>
</itemizedlist>
<para>
Library code should avoid manipulating these global process
attributes. It should not rely on environment variables, umask,
the current working directory and signal masks because these
attributes can be inherted from an untrusted source.
</para>
<para>
In addition, there are obvious process-wide aspects such as the
virtual memory layout, the set of open files and dynamic shared
objects, but with the exception of shared objects, these can be
manipulated in a relatively isolated way.
</para>
</section>
</chapter>

View file

@ -1,5 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="sect-Defensive_Coding-Tasks-Locking">
</chapter>

View file

@ -1,483 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="sect-Defensive_Coding-Tasks-Processes">
<title>Processes</title>
<section id="sect-Defensive_Coding-Tasks-Processes-Creation">
<title>Safe process creation</title>
<para>
This section describes how to create new child processes in a
safe manner. In addition to the concerns addressed below, there
is the possibility of file descriptor leaks, see <xref
linkend="sect-Defensive_Coding-Tasks-Descriptors-Child_Processes"/>.
</para>
<section>
<title>Obtaining the program path and the command line
template</title>
<para>
The name and path to the program being invoked should be
hard-coded or controlled by a static configuration file stored
at a fixed location (at an file system absolute path). The
same applies to the template for generating the command line.
</para>
<para>
The configured program name should be an absolute path. If it
is a relative path, the contents of the <envar>PATH</envar>
must be obtained in s secure manner (see <xref
linkend="sect-Defensive_Coding-Tasks-secure_getenv"/>).
If the <envar>PATH</envar> variable is not set or untrusted,
the safe default <literal>/bin:/usr/bin</literal> must be
used.
</para>
<para>
If too much flexibility is provided here, it may allow
invocation of arbitrary programs without proper authorization.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Processes-execve">
<title>Bypassing the shell</title>
<para>
Child processes should be created without involving the system
shell.
</para>
<para>
For C/C++, <function>system</function> should not be used.
The <function>posix_spawn</function> function can be used
instead, or a combination <function>fork</function> and
<function>execve</function>. (In some cases, it may be
preferable to use <function>vfork</function> or the
Linux-specific <function>clone</function> system call instead
of <function>fork</function>.)
</para>
<para>
In Python, the <literal>subprocess</literal> module bypasses
the shell by default (when the <literal>shell</literal>
keyword argument is not set to true).
<function>os.system</function> should not be used.
</para>
<para>
The Java class <type>java.lang.ProcessBuilder</type> can be
used to create subprocesses without interference from the
system shell.
</para>
<important>
<title>Portability notice</title>
<para>
On Windows, there is no argument vector, only a single
argument string. Each application is responsible for parsing
this string into an argument vector. There is considerable
variance among the quoting style recognized by applications.
Some of them expand shell wildcards, others do not. Extensive
application-specific testing is required to make this secure.
</para>
</important>
<para>
Note that some common applications (notably
<application>ssh</application>) unconditionally introduce the
use of a shell, even if invoked directly without a shell. It is
difficult to use these applications in a secure manner. In this
case, untrusted data should be supplied by other means. For
example, standard input could be used, instead of the command
line.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Processes-environ">
<title>Specifying the process environment</title>
<para>
Child processes should be created with a minimal set of
environment variables. This is absolutely essential if there
is a trust transition involved, either when the parent process
was created, or during the creation of the child process.
</para>
<para>
In C/C++, the environment should be constructed as an array of
strings and passed as the <varname>envp</varname> argument to
<function>posix_spawn</function> or <function>execve</function>.
The functions <function>setenv</function>,
<function>unsetenv</function> and <function>putenv</function>
should not be used. They are not thread-safe and suffer from
memory leaks.
</para>
<para>
Python programs need to specify a <literal>dict</literal> for
the the <varname>env</varname> argument of the
<function>subprocess.Popen</function> constructor.
The Java class <literal>java.lang.ProcessBuilder</literal>
provides a <function>environment()</function> method,
which returns a map that can be manipulated.
</para>
<para>
The following list provides guidelines for selecting the set
of environment variables passed to the child process.
</para>
<itemizedlist>
<listitem>
<para>
<envar>PATH</envar> should be initialized to
<literal>/bin:/usr/bin</literal>.
</para>
</listitem>
<listitem>
<para>
<envar>USER</envar> and <envar>HOME</envar> can be inhereted
from the parent process environment, or they can be
initialized from the <literal>pwent</literal> structure
for the user. <!-- ??? refer to dropping privileges -->
</para>
</listitem>
<listitem>
<para>The <envar>DISPLAY</envar> and <envar>XAUTHORITY</envar>
variables should be passed to the subprocess if it is an X
program. Note that this will typically not work across trust
boundaries because <envar>XAUTHORITY</envar> refers to a file
with <literal>0600</literal> permissions.
</para>
</listitem>
<listitem>
<para>
The location-related environment variables
<envar>LANG</envar>, <envar>LANGUAGE</envar>,
<envar>LC_ADDRESS</envar>, <envar>LC_ALL</envar>,
<envar>LC_COLLATE</envar>, <envar>LC_CTYPE</envar>,
<envar>LC_IDENTIFICATION</envar>,
<envar>LC_MEASUREMENT</envar>, <envar>LC_MESSAGES</envar>,
<envar>LC_MONETARY</envar>, <envar>LC_NAME</envar>,
<envar>LC_NUMERIC</envar>, <envar>LC_PAPER</envar>,
<envar>LC_TELEPHONE</envar> and <envar>LC_TIME</envar>
can be passed to the subprocess if present.
</para>
</listitem>
<listitem>
<para>
The called process may need application-specific
environment variables, for example for passing passwords.
(See <xref
linkend="sect-Defensive_Coding-Tasks-Processes-Command_Line_Visibility"/>.)
</para>
</listitem>
<listitem>
<para>
All other environment variables should be dropped. Names
for new environment variables should not be accepted from
untrusted sources.
</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Robust argument list processing</title>
<para>
When invoking a program, it is sometimes necessary to include
data from untrusted sources. Such data should be check
against embedded <literal>NUL</literal> characters because the
system APIs will sliently truncate argument strings at the first
<literal>NUL</literal> character.
</para>
<para>
The following recommendations assume that the program being
invoked uses GNU-style option processing using
<function>getopt_long</function>. This convention is widely
used, but it is just that, and individual programs might
interpret a command line in a different way.
</para>
<para>
If the untrusted data has to go into an option, use the
<literal>--option-name=VALUE</literal> syntax, placing the
option and its value into the same command line argument.
This avoids any potential confusion if the data starts with
<literal>-</literal>.
</para>
<para>
For positional arguments, terminate the option list with a
single <option>--</option> marker after the last option, and
include the data at the right position. The
<option>--</option> marker terminates option processing, and
the data will not be treated as an option even if it starts
with a dash.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Processes-Command_Line_Visibility">
<title>Passing secrets to subprocesses</title>
<para>
The command line (the name of the program and its argument) of
a running process is traditionally available to all local
users. The called program can overwrite this information, but
only after it has run for a bit of time, during which the
information may have been read by other processes. However,
on Linux, the process environment is restricted to the user
who runs the process. Therefore, if you need a convenient way
to pass a password to a child process, use an environment
variable, and not a command line argument. (See <xref
linkend="sect-Defensive_Coding-Tasks-Processes-environ"/>.)
</para>
<important>
<title>Portability notice</title>
<para>
On some UNIX-like systems (notably Solaris), environment
variables can be read by any system user, just like command
lines.
</para>
</important>
<para>
If the environment-based approach cannot be used due to
portability concerns, the data can be passed on standard
input. Some programs (notably <application>gpg</application>)
use special file descriptors whose numbers are specified on
the command line. Temporary files are an option as well, but
they might give digital forensics access to sensitive data
(such as passphrases) because it is difficult to safely delete
them in all cases.
</para>
</section>
</section>
<section>
<title>Handling child process termination</title>
<para>
When child processes terminate, the parent process is signalled.
A stub of the terminated processes (a
<emphasis>zombie</emphasis>, shown as
<literal>&lt;defunct&gt;</literal> by
<application>ps</application>) is kept around until the status
information is collected (<emphasis>reaped</emphasis>) by the
parent process. Over the years, several interfaces for this
have been invented:
</para>
<itemizedlist>
<listitem>
<para>
The parent process calls <function>wait</function>,
<function>waitpid</function>, <function>waitid</function>,
<function>wait3</function> or <function>wait4</function>,
without specifying a process ID. This will deliver any
matching process ID. This approach is typically used from
within event loops.
</para>
</listitem>
<listitem>
<para>
The parent process calls <function>waitpid</function>,
<function>waitid</function>, or <function>wait4</function>,
with a specific process ID. Only data for the specific
process ID is returned. This is typically used in code
which spawns a single subprocess in a synchronous manner.
</para>
</listitem>
<listitem>
<para>
The parent process installs a handler for the
<literal>SIGCHLD</literal> signal, using
<function>sigaction</function>, and specifies to the
<literal>SA_NOCLDWAIT</literal> flag.
This approach could be used by event loops as well.
</para>
</listitem>
</itemizedlist>
<para>
None of these approaches can be used to wait for child process
terminated in a completely thread-safe manner. The parent
process might execute an event loop in another thread, which
could pick up the termination signal. This means that libraries
typically cannot make free use of child processes (for example,
to run problematic code with reduced privileges in a separate
address space).
</para>
<para>
At the moment, the parent process should explicitly wait for
termination of the child process using
<function>waitpid</function> or <function>waitpid</function>,
and hope that the status is not collected by an event loop
first.
</para>
</section>
<section>
<title><literal>SUID</literal>/<literal>SGID</literal>
processes</title>
<!-- ??? need to document real vs effective UID -->
<para>
Programs can be marked in the file system to indicate to the
kernel that a trust transition should happen if the program is
run. The <literal>SUID</literal> file permission bit indicates
that an executable should run with the effective user ID equal
to the owner of the executable file. Similarly, with the
<literal>SGID</literal> bit, the effective group ID is set to
the group of the executable file.
</para>
<para>
Linux supports <emphasis>fscaps</emphasis>, which can grant
additional capabilities to a process in a finer-grained manner.
Additional mechanisms can be provided by loadable security
modules.
</para>
<para>
When such a trust transition has happened, the process runs in a
potentially hostile environment. Additional care is necessary
not to rely on any untrusted information. These concerns also
apply to libraries which can be linked into such processes.
</para>
<section id="sect-Defensive_Coding-Tasks-secure_getenv">
<title>Accessing environment variables</title>
<para>
The following steps are required so that a program does not
accidentally pick up untrusted data from environment
variables.
</para>
<itemizedlist>
<listitem><para>
Compile your C/C++ sources with <literal>-D_GNU_SOURCE</literal>.
The Autoconf macro <literal>AC_GNU_SOURCE</literal> ensures this.
</para></listitem>
<listitem><para>
Check for the presence of the <function>secure_getenv</function>
and <function>__secure_getenv</function> function. The Autoconf
directive <literal>AC_CHECK_FUNCS([__secure_getenv secure_getenv])</literal>
performs these checks.
</para></listitem>
<listitem><para>
Arrange for a proper definition of the
<function>secure_getenv</function> function. See <xref
linkend="ex-Defensive_Coding-Tasks-secure_getenv"/>.
</para></listitem>
<listitem><para>
Use <function>secure_getenv</function> instead of
<function>getenv</function> to obtain the value of critical
environment variables. <function>secure_getenv</function>
will pretend the variable has not bee set if the process
environment is not trusted.
</para></listitem>
</itemizedlist>
<para>
Critical environment variables are debugging flags,
configuration file locations, plug-in and log file locations,
and anything else that might be used to bypass security
restrictions or cause a privileged process to behave in an
unexpected way.
</para>
<para>
Either the <function>secure_getenv</function> function or the
<function>__secure_getenv</function> is available from GNU libc.
</para>
<example id="ex-Defensive_Coding-Tasks-secure_getenv">
<title>Obtaining a definition for <function>secure_getenv</function></title>
<programlisting language="C">
<![CDATA[
#include <stdlib.h>
#ifndef HAVE_SECURE_GETENV
# ifdef HAVE__SECURE_GETENV
# define secure_getenv __secure_getenv
# else
# error neither secure_getenv nor __secure_getenv are available
# endif
#endif
]]>
</programlisting>
</example>
</section>
</section>
<section id="sect-Defensive_Coding-Tasks-Processes-Daemons">
<title>Daemons</title>
<para>
Background processes providing system services
(<emphasis>daemons</emphasis>) need to decouple themselves from
the controlling terminal and the parent process environment:
</para>
<itemizedlist>
<listitem>
<para>Fork.</para>
</listitem>
<listitem>
<para>
In the child process, call <function>setsid</function>. The
parent process can simply exit (using
<function>_exit</function>, to avoid running clean-up
actions twice).
</para>
</listitem>
<listitem>
<para>
In the child process, fork again. Processing continues in
the child process. Again, the parent process should just
exit.
</para>
</listitem>
<listitem>
<para>
Replace the descriptors 0, 1, 2 with a descriptor for
<filename>/dev/null</filename>. Logging should be
redirected to <application>syslog</application>.
</para>
</listitem>
</itemizedlist>
<para>
Older instructions for creating daemon processes recommended a
call to <literal>umask(0)</literal>. This is risky because it
often leads to world-writable files and directories, resulting
in security vulnerabilities such as arbitrary process
termination by untrusted local users, or log file truncation.
If the <emphasis>umask</emphasis> needs setting, a restrictive
value such as <literal>027</literal> or <literal>077</literal>
is recommended.
</para>
<para>
Other aspects of the process environment may have to changed as
well (environment variables, signal handler disposition).
</para>
<para>
It is increasingly common that server processes do not run as
background processes, but as regular foreground process under a
supervising master process (such as
<application>systemd</application>). Server processes should
offer a command line option which disables forking and
replacement of the standard output and standard error streams.
Such an option is also useful for debugging.
</para>
</section>
<section>
<title>Semantics of command line arguments</title>
<!-- ??? This applies in two ways, safely calling an other process
and support for being called safely. Also need to address
untrusted current directory on USB sticks. -->
<para>
After process creation and option processing, it is up to the
child process to interpret the arguments. Arguments can be
file names, host names, or URLs, and many other things. URLs
can refer to the local network, some server on the Internet,
or to the local file system. Some applications even accept
arbitrary code in arguments (for example,
<application>python</application> with the
<option>-c</option> option).
</para>
<para>
Similar concerns apply to environment variables, the contents
of the current directory and its subdirectories.
<!-- ??? refer to section on temporary directories -->
</para>
<para>
Consequently, careful analysis is required if it is safe to
pass untrusted data to another program.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Processes-Fork-Parallel">
<title><function>fork</function> as a primitive for parallelism</title>
<para>
A call to <function>fork</function> which is not immediately
followed by a call to <function>execve</function> (perhaps after
rearranging and closing file descriptors) is typically unsafe,
especially from a library which does not control the state of
the entire process. Such use of <function>fork</function>
should be replaced with proper child processes or threads.
</para>
</section>
</chapter>

View file

@ -1,397 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Tasks-Serialization">
<title>Serialization and Deserialization</title>
<para>
Protocol decoders and file format parsers are often the
most-exposed part of an application because they are exposed with
little or no user interaction and before any authentication and
security checks are made. They are also difficult to write
robustly in languages which are not memory-safe.
</para>
<section id="sect-Defensive_Coding-Tasks-Serialization-Decoders">
<title>Recommendations for manually written decoders</title>
<para>
For C and C++, the advice in <xref
linkend="sect-Defensive_Coding-C-Pointers"/> applies. In
addition, avoid non-character pointers directly into input
buffers. Pointer misalignment causes crashes on some
architectures.
</para>
<para>
When reading variable-sized objects, do not allocate large
amounts of data solely based on the value of a size field. If
possible, grow the data structure as more data is read from the
source, and stop when no data is available. This helps to avoid
denial-of-service attacks where little amounts of input data
results in enormous memory allocations during decoding.
Alternatively, you can impose reasonable bounds on memory
allocations, but some protocols do not permit this.
</para>
</section>
<section>
<title>Protocol design</title>
<para>
Binary formats with explicit length fields are more difficult to
parse robustly than those where the length of dynamically-sized
elements is derived from sentinel values. A protocol which does
not use length fields and can be written in printable ASCII
characters simplifies testing and debugging. However, binary
protocols with length fields may be more efficient to parse.
</para>
</section>
<section>
<title id="sect-Defensive_Coding-Tasks-Serialization-Library">Library
support for deserialization</title>
<para>
For some languages, generic libraries are available which allow
to serialize and deserialize user-defined objects. The
deserialization part comes in one of two flavors, depending on
the library. The first kind uses type information in the data
stream to control which objects are instantiated. The second
kind uses type definitions supplied by the programmer. The
first one allows arbitrary object instantiation, the second one
generally does not.
</para>
<para>
The following serialization frameworks are in the first category,
are known to be unsafe, and must not be used for untrusted data:
</para>
<itemizedlist>
<listitem><para>
Python's <package>pickle</package> and <package>cPickle</package>
modules
</para></listitem>
<listitem><para>
Perl's <package>Storable</package> package
</para></listitem>
<listitem><para>
Java serialization (<type>java.io.ObjectInputStream</type>)
</para></listitem>
<listitem><para>
PHP serialization (<function>unserialize</function>)
</para></listitem>
<listitem><para>
Most implementations of YAML
</para></listitem>
</itemizedlist>
<para>
When using a type-directed deserialization format where the
types of the deserialized objects are specified by the
programmer, make sure that the objects which can be instantiated
cannot perform any destructive actions in their destructors,
even when the data members have been manipulated.
</para>
<para>
JSON decoders do not suffer from this problem. But you must not
use the <function>eval</function> function to parse JSON objects
in Javascript; even with the regular expression filter from RFC
4627, there are still information leaks remaining.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML">
<title>XML serialization</title>
<para>
</para>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-External">
<title>External references</title>
<para>
XML documents can contain external references. They can occur
in various places.
</para>
<itemizedlist>
<listitem>
<para>
In the DTD declaration in the header of an XML document:
</para>
<informalexample>
<programlisting language="XML">
<![CDATA[<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">]]>
</programlisting>
</informalexample>
</listitem>
<listitem>
<para>
In a namespace declaration:
</para>
<informalexample>
<programlisting language="XML">
<![CDATA[<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">]]>
</programlisting>
</informalexample>
</listitem>
<listitem>
<para>
In an entity defintion:
</para>
<informalexample>
<programlisting language="XML">
<![CDATA[<!ENTITY sys SYSTEM "http://www.example.com/ent.xml">
<!ENTITY pub PUBLIC "-//Example//Public Entity//EN"
"http://www.example.com/pub-ent.xml">]]>
</programlisting>
</informalexample>
</listitem>
<listitem>
<para>
In a notation:
</para>
<informalexample>
<programlisting language="XML">
<![CDATA[<!NOTATION not SYSTEM "../not.xml">]]>
</programlisting>
</informalexample>
</listitem>
</itemizedlist>
<para>
Originally, these external references were intended as unique
identifiers, but by many XML implementations, they are used
for locating the data for the referenced element. This causes
unwanted network traffic, and may disclose file system
contents or otherwise unreachable network resources, so this
functionality should be disabled.
</para>
<para>
Depending on the XML library, external referenced might be
processed not just when parsing XML, but also when generating
it.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-Entities">
<title>Entity expansion</title>
<para>
When external DTD processing is disabled, an internal DTD
subset can still contain entity definitions. Entity
declarations can reference other entities. Some XML libraries
expand entities automatically, and this processing cannot be
switched off in some places (such as attribute values or
content models). Without limits on the entity nesting level,
this expansion results in data which can grow exponentially in
length with size of the input. (If there is a limit on the
nesting level, the growth is still polynomial, unless further
limits are imposed.)
</para>
<para>
Consequently, the processing internal DTD subsets should be
disabled if possible, and only trusted DTDs should be
processed. If a particular XML application does not permit
such restrictions, then application-specific limits are called
for.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-XInclude">
<title>XInclude processing</title>
<para>
XInclude processing can reference file and network resources
and include them into the document, much like external entity
references. When parsing untrusted XML documents, XInclude
processing should be truned off.
</para>
<para>
XInclude processing is also fairly complex and may pull in
support for the XPointer and XPath specifications,
considerably increasing the amount of code required for XML
processing.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-Validation">
<title>Algorithmic complexity of XML validation</title>
<para>
DTD-based XML validation uses regular expressions for content
models. The XML specification requires that content models
are deterministic, which means that efficient validation is
possible. However, some implementations do not enforce
determinism, and require exponential (or just polynomial)
amount of space or time for validating some DTD/document
combinations.
</para>
<para>
XML schemas and RELAX NG (via the <literal>xsd:</literal>
prefix) directly support textual regular expressions which are
not required to be deterministic.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-Expat">
<title>Using Expat for XML parsing</title>
<para>
By default, Expat does not try to resolve external IDs, so no
steps are required to block them. However, internal entity
declarations are processed. Installing a callback which stops
parsing as soon as such entities are encountered disables
them, see <xref
linkend="ex-Defensive_Coding-Tasks-Serialization-XML-Expat-EntityDeclHandler"/>.
Expat does not perform any validation, so there are no
problems related to that.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-Expat-EntityDeclHandler">
<title>Disabling XML entity processing with Expat</title>
<xi:include href="snippets/Serialization-XML-Expat-EntityDeclHandler.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
This handler must be installed when the
<literal>XML_Parser</literal> object is created (<xref
linkend="ex-Defensive_Coding-Tasks-Serialization-XML-Expat-Create"/>).
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-Expat-Create">
<title>Creating an Expat XML parser</title>
<xi:include href="snippets/Serialization-XML-Expat-Create.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
It is also possible to reject internal DTD subsets altogeher,
using a suitable
<literal>XML_StartDoctypeDeclHandler</literal> handler
installed with <function>XML_SetDoctypeDeclHandler</function>.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse">
<title>Using OpenJDK for XML parsing and validation</title>
<para>
OpenJDK contains facilities for DOM-based, SAX-based, and
StAX-based document parsing. Documents can be validated
against DTDs or XML schemas.
</para>
<para>
The approach taken to deal with entity expansion differs from
the general recommendation in <xref
linkend="sect-Defensive_Coding-Tasks-Serialization-XML-Entities"/>.
We enable the the feature flag
<literal>javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING</literal>,
which enforces heuristic restrictions on the number of entity
expansions. Note that this flag alone does not prevent
resolution of external references (system IDs or public IDs),
so it is slightly misnamed.
</para>
<para>
In the following sections, we use helper classes to prevent
external ID resolution.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-NoEntityResolver">
<title>Helper class to prevent DTD external entity resolution in OpenJDK</title>
<xi:include href="snippets/Serialization-XML-OpenJDK-NoEntityResolver.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-NoResourceResolver">
<title>Helper class to prevent schema resolution in
OpenJDK</title>
<xi:include href="snippets/Serialization-XML-OpenJDK-NoResourceResolver.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
<xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-Imports"/>
shows the imports used by the examples.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-Imports">
<title>Java imports for OpenJDK XML parsing</title>
<xi:include href="snippets/Serialization-XML-OpenJDK-Imports.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-DOM">
<title>DOM-based XML parsing and DTD validation in OpenJDK</title>
<para>
This approach produces a
<literal>org.w3c.dom.Document</literal> object from an input
stream. <xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-DOM"/>
use the data from the <literal>java.io.InputStream</literal>
instance in the <literal>inputStream</literal> variable.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-DOM">
<title>DOM-based XML parsing in OpenJDK</title>
<xi:include href="snippets/Serialization-XML-OpenJDK_Parse-DOM.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
External entity references are prohibited using the
<literal>NoEntityResolver</literal> class in
<xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-NoEntityResolver"/>.
Because external DTD references are prohibited, DTD validation
(if enabled) will only happen against the internal DTD subset
embedded in the XML document.
</para>
<para>
To validate the document against an external DTD, use a
<literal>javax.xml.transform.Transformer</literal> class to
add the DTD reference to the document, and an entity
resolver which whitelists this external reference.
</para>
</section>
<section id="sect-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-SAX">
<title>XML Schema validation in OpenJDK</title>
<para>
<xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-XMLSchema_SAX"/>
shows how to validate a document against an XML Schema,
using a SAX-based approach. The XML data is read from an
<literal>java.io.InputStream</literal> in the
<literal>inputStream</literal> variable.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-XMLSchema_SAX">
<title>SAX-based validation against an XML schema in
OpenJDK</title>
<xi:include href="snippets/Serialization-XML-OpenJDK_Parse-XMLSchema_SAX.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
<para>
The <literal>NoResourceResolver</literal> class is defined
in <xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK-NoResourceResolver"/>.
</para>
<para>
If you need to validate a document against an XML schema,
use the code in <xref
linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-DOM"/>
to create the document, but do not enable validation at this
point. Then use
<xref linkend="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-XMLSchema_DOM"/>
to perform the schema-based validation on the
<literal>org.w3c.dom.Document</literal> instance
<literal>document</literal>.
</para>
<example id="ex-Defensive_Coding-Tasks-Serialization-XML-OpenJDK_Parse-XMLSchema_DOM">
<title>Validation of a DOM document against an XML schema in
OpenJDK</title>
<xi:include href="snippets/Serialization-XML-OpenJDK_Parse-XMLSchema_DOM.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</example>
</section>
</section>
</section>
<section>
<title>Protocol Encoders</title>
<para>
For protocol encoders, you should write bytes to a buffer which
grows as needed, using an exponential sizing policy. Explicit
lengths can be patched in later, once they are known.
Allocating the required number of bytes upfront typically
requires separate code to compute the final size, which must be
kept in sync with the actual encoding step, or vulnerabilities
may result. In multi-threaded code, parts of the object being
deserialized might change, so that the computed size is out of
date.
</para>
<para>
You should avoid copying data directly from a received packet
during encoding, disregarding the format. Propagating malformed
data could enable attacks on other recipients of that data.
</para>
<para>
When using C or C++ and copying whole data structures directly
into the output, make sure that you do not leak information in
padding bytes between fields or at the end of the
<literal>struct</literal>.
</para>
</section>
</chapter>

View file

@ -1,257 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<chapter id="chap-Defensive_Coding-Tasks-Temporary_Files">
<title>Temporary files</title>
<para>
In this chapter, we describe how to create temporary files and
directories, how to remove them, and how to work with programs
which do not create files in ways that a safe with a shared
directory for temporary files. General file system manipulation
is treated in a separate chapter, <xref
linkend="chap-Defensive_Coding-Tasks-File_System"/>.
</para>
<para>
Secure creation of temporary files has four different aspects.
</para>
<itemizedlist>
<listitem>
<para>
The location of the directory for temporary files must be
obtained in a secure manner (that is, untrusted environment
variables must be ignored, see <xref
linkend="sect-Defensive_Coding-Tasks-secure_getenv"/>).
</para>
</listitem>
<listitem>
<para>
A new file must be created. Reusing an existing file must be
avoided (the <filename class="directory">/tmp</filename> race
condition). This is tricky because traditionally, system-wide
temporary directories shared by all users are used.
</para>
</listitem>
<listitem>
<para>
The file must be created in a way that makes it impossible for
other users to open it.
</para>
</listitem>
<listitem>
<para>
The descriptor for the temporary file should not leak to
subprocesses.
</para>
</listitem>
</itemizedlist>
<para>
All functions mentioned below will take care of these aspects.
</para>
<para>
Traditionally, temporary files are often used to reduce memory
usage of programs. More and more systems use RAM-based file
systems such as <literal>tmpfs</literal> for storing temporary
files, to increase performance and decrease wear on Flash storage.
As a result, spooling data to temporary files does not result in
any memory savings, and the related complexity can be avoided if
the data is kept in process memory.
</para>
<section id="chap-Defensive_Coding-Tasks-Temporary_Files-Location">
<title>Obtaining the location of temporary directory</title>
<para>
Some functions below need the location of a directory which
stores temporary files. For C/C++ programs, use the following
steps to obtain that directory:
</para>
<itemizedlist>
<listitem>
<para>
Use <function>secure_getenv</function> to obtain the value
of the <literal>TMPDIR</literal> environment variable. If
it is set, convert the path to a fully-resolved absolute
path, using <literal>realpath(path, NULL)</literal>. Check
if the new path refers to a directory and is writeable. In
this case, use it as the temporary directory.
</para>
</listitem>
<listitem>
<para>
Fall back to <filename class="directory">/tmp</filename>.
</para>
</listitem>
</itemizedlist>
<para>
In Python, you can use the <varname>tempfile.tempdir</varname>
variable.
</para>
<para>
Java does not support SUID/SGID programs, so you can use the
<function>java.lang.System.getenv(String)</function> method to
obtain the value of the <literal>TMPDIR</literal> environment
variable, and follow the two steps described above. (Java's
default directory selection does not honor
<literal>TMPDIR</literal>.)
</para>
</section>
<section>
<title>Named temporary files</title>
<para>
The <function>mkostemp</function> function creates a named
temporary file. You should specify the
<literal>O_CLOEXEC</literal> flag to avoid file descriptor leaks
to subprocesses. (Applications which do not use multiple threads
can also use <function>mkstemp</function>, but libraries should
use <function>mkostemp</function>.) For determining the
directory part of the file name pattern, see <xref
linkend="chap-Defensive_Coding-Tasks-Temporary_Files-Location"/>.
</para>
<para>
The file is not removed automatically. It is not safe to rename
or delete the file before processing, or transform the name in
any way (for example, by adding a file extension). If you need
multiple temporary files, call <function>mkostemp</function>
multiple times. Do not create additional file names derived
from the name provided by a previous
<function>mkostemp</function> call. However, it is safe to close
the descriptor returned by <function>mkostemp</function> and
reopen the file using the generated name.
</para>
<para>
The Python class <literal>tempfile.NamedTemporaryFile</literal>
provides similar functionality, except that the file is deleted
automatically by default. Note that you may have to use the
<literal>file</literal> attribute to obtain the actual file
object because some programming interfaces cannot deal with
file-like objects. The C function <function>mkostemp</function>
is also available as <function>tempfile.mkstemp</function>.
</para>
<para>
In Java, you can use the
<function>java.io.File.createTempFile(String, String,
File)</function> function, using the temporary file location
determined according to <xref
linkend="chap-Defensive_Coding-Tasks-Temporary_Files-Location"/>.
Do not use <function>java.io.File.deleteOnExit()</function> to
delete temporary files, and do not register a shutdown hook for
each temporary file you create. In both cases, the deletion
hint cannot be removed from the system if you delete the
temporary file prior to termination of the VM, causing a memory
leak.
</para>
</section>
<section>
<title>Temporary files without names</title>
<para>
The <function>tmpfile</function> function creates a temporary
file and immediately deletes it, while keeping the file open.
As a result, the file lacks a name and its space is deallocated
as soon as the file descriptor is closed (including the implicit
close when the process terminates). This avoids cluttering the
temporary directory with orphaned files.
</para>
<para>
Alternatively, if the maximum size of the temporary file is
known beforehand, the <function>fmemopen</function> function can
be used to create a <literal>FILE *</literal> object which is
backed by memory.
</para>
<para>
In Python, unnamed temporary files are provided by the
<literal>tempfile.TemporaryFile</literal> class, and the
<literal>tempfile.SpooledTemporaryFile</literal> class provides
a way to avoid creation of small temporary files.
</para>
<para>
Java does not support unnamed temporary files.
</para>
</section>
<section id="chap-Defensive_Coding-Tasks-Temporary_Directory">
<title>Temporary directories</title>
<para>
The <function>mkdtemp</function> function can be used to create
a temporary directory. (For determining the directory part of
the file name pattern, see <xref
linkend="chap-Defensive_Coding-Tasks-Temporary_Files-Location"/>.)
The directory is not automatically removed. In Python, this
function is available as <function>tempfile.mkdtemp</function>.
In Java 7, temporary directories can be created using the
<function>java.nio.file.Files.createTempDirectory(Path, String,
FileAttribute...)</function> function.
</para>
<para>
When creating files in the temporary directory, use
automatically generated names, e.g., derived from a sequential
counter. Files with externally provided names could be picked
up in unexpected contexts, and crafted names could actually
point outside of the tempoary directory (due to
<emphasis>directory traversal</emphasis>).
</para>
<para>
Removing a directory tree in a completely safe manner is
complicated. Unless there are overriding performance concerns,
the <application>rm</application> program should be used, with
the <option>-rf</option> and <option>--</option> options.
</para>
</section>
<section>
<title>Compensating for unsafe file creation</title>
<para>
There are two ways to make a function or program which excepts a
file name safe for use with temporary files. See
<xref linkend="sect-Defensive_Coding-Tasks-Processes-Creation"/>,
for details on subprocess creation.
</para>
<itemizedlist>
<listitem>
<para>
Create a temporary directory and place the file there. If
possible, run the program in a subprocess which uses the
temporary directory as its current directory, with a
restricted environment.
Use generated names for all files in that temporary
directory. (See <xref
linkend="chap-Defensive_Coding-Tasks-Temporary_Directory"/>.)
</para>
</listitem>
<listitem>
<para>
Create the temporary file and pass the generated file name
to the function or program. This only works if the function
or program can cope with a zero-length existing file. It is
safe only under additional assumptions:
</para>
<itemizedlist>
<listitem>
<para>
The function or program must not create additional files
whose name is derived from the specified file name or
are otherwise predictable.
</para>
</listitem>
<listitem>
<para>
The function or program must not delete the file before
processing it.
</para>
</listitem>
<listitem>
<para>
It must not access any existing files in the same
directory.
</para>
</listitem>
</itemizedlist>
<para>
It is often difficult to check whether these additional
assumptions are matched, therefore this approach is not
recommended.
</para>
</listitem>
</itemizedlist>
</section>
</chapter>

View file

@ -1,4 +0,0 @@
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<include rules="../../schemas.xml"/>
</locatingRules>

View file

@ -1,11 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 67
/repos/product-security/!svn/ver/288/defensive-coding/trunk/scripts
END
split-snippets.py
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/288/defensive-coding/trunk/scripts/split-snippets.py
END

View file

@ -1,62 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/scripts
https://svn.devel.redhat.com/repos/product-security
2012-12-14T09:21:42.199416Z
288
fweimer@REDHAT.COM
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
split-snippets.py
file
2013-01-10T17:17:49.053814Z
a3a947246ad0000e1e2f3836674da5ce
2012-12-14T09:21:42.199416Z
288
fweimer@REDHAT.COM
3637

View file

@ -1,106 +0,0 @@
#!/usr/bin/python
# Split source code files into XML snippets for inclusion in the
# documentation.
#
# Usage: python split-snippets.py TARGET-ROOT INPUT-FILE...
#
# Directives in the input files have the form:
#
# //+ Directory File-Base-Name
# lines to be included in
# the file
# //-
#
# In this example, the lines are written to the file
# en-US/Directory/snippets/File-Base-Name.xml under the TARGET-ROOT
# directory. Whitespace shared with the starting line is stripped.
# Instead of "//", it is possible to use "#".
import re
import sys
target_root = sys.argv[1]
def output_file_name(dirname, basename):
return "{0}/en-US/{1}/snippets/{2}.xml".format(
target_root, dirname, basename)
re_open_file = re.compile(
r'^(\s*)(?://|#)\+\s+([a-zA-Z0-9_-]+)\s+([a-zA-Z0-9_-]+)\s*\n?$')
re_close_file = re.compile(r'^\s*(?://|#)\-\s*\n?$')
def extension_to_language(path, map={
'c' : 'C',
'py' : 'Python',
'java' : 'Java',
}):
return map.get(path.split('.')[-1], 'C')
def write_single_file(path, contents, language):
assert not [ch for ch in language if ch in "<>&\""]
with file(path, "w") as out:
out.write('''<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE programlisting PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
]>
<!-- Automatically generated file. Do not edit. -->
<programlisting language="''' + language + '''">
''')
for line in contents:
for ch in line:
if ch in "<>&":
out.write("&#{0};".format(ord(ch)))
else:
out.write(ch)
out.write("</programlisting>\n")
def write_output(output):
for (outpath, (origpath, contents)) in output.items():
write_single_file(outpath, contents,
extension_to_language(origpath))
def process_file(path):
output = {}
with file(path) as f:
current_file = None
current_contents = None
indent = None
for line in f.readlines():
match = re_open_file.match(line)
if match is not None:
if current_file is None:
current_file = output_file_name(
match.group(2), match.group(3))
if current_file in output:
raise IOError("{0} written by {1} and {2}",
current_file, output[current_file][0],
path)
indent = match.group(1)
current_contents = []
output[current_file] = (path, current_contents)
else:
raise IOError("{0}: unterminated export to {1}".format(
path, current_file))
continue
match = re_close_file.match(line)
if match is not None:
if current_file is None:
raise IOError("{0}: closing file which is not open")
else:
current_file = None
current_contents = None
indent = None
continue
if current_file is not None:
if line.startswith(indent):
line = line[len(indent):]
current_contents.append(line)
if current_file is not None:
raise IOError("{0}: unterminated export to {1}".format(
path, current_file))
write_output(output)
for path in sys.argv[2:]:
process_file(path)

View file

@ -1,107 +0,0 @@
K 25
svn:wc:ra_dav:version-url
V 63
/repos/product-security/!svn/ver/302/defensive-coding/trunk/src
END
tcp_connect.c
K 25
svn:wc:ra_dav:version-url
V 77
/repos/product-security/!svn/ver/230/defensive-coding/trunk/src/tcp_connect.c
END
TLS-Client-OpenSSL.c
K 25
svn:wc:ra_dav:version-url
V 84
/repos/product-security/!svn/ver/276/defensive-coding/trunk/src/TLS-Client-OpenSSL.c
END
C-Arithmetic-add.c
K 25
svn:wc:ra_dav:version-url
V 82
/repos/product-security/!svn/ver/203/defensive-coding/trunk/src/C-Arithmetic-add.c
END
x509_check_host.c
K 25
svn:wc:ra_dav:version-url
V 81
/repos/product-security/!svn/ver/235/defensive-coding/trunk/src/x509_check_host.c
END
TLS-Client-Python.py
K 25
svn:wc:ra_dav:version-url
V 84
/repos/product-security/!svn/ver/269/defensive-coding/trunk/src/TLS-Client-Python.py
END
tcp_connect.h
K 25
svn:wc:ra_dav:version-url
V 77
/repos/product-security/!svn/ver/230/defensive-coding/trunk/src/tcp_connect.h
END
check-function.py
K 25
svn:wc:ra_dav:version-url
V 81
/repos/product-security/!svn/ver/235/defensive-coding/trunk/src/check-function.py
END
XML-Parser-Expat.c
K 25
svn:wc:ra_dav:version-url
V 82
/repos/product-security/!svn/ver/277/defensive-coding/trunk/src/XML-Parser-Expat.c
END
TLSClientOpenJDK.java
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/302/defensive-coding/trunk/src/TLSClientOpenJDK.java
END
src.mk
K 25
svn:wc:ra_dav:version-url
V 70
/repos/product-security/!svn/ver/277/defensive-coding/trunk/src/src.mk
END
TLS-Client-NSS.c
K 25
svn:wc:ra_dav:version-url
V 80
/repos/product-security/!svn/ver/259/defensive-coding/trunk/src/TLS-Client-NSS.c
END
DERParser.java
K 25
svn:wc:ra_dav:version-url
V 78
/repos/product-security/!svn/ver/298/defensive-coding/trunk/src/DERParser.java
END
XMLParserOpenJDK.java
K 25
svn:wc:ra_dav:version-url
V 85
/repos/product-security/!svn/ver/287/defensive-coding/trunk/src/XMLParserOpenJDK.java
END
TLS-Client-GNUTLS.c
K 25
svn:wc:ra_dav:version-url
V 83
/repos/product-security/!svn/ver/264/defensive-coding/trunk/src/TLS-Client-GNUTLS.c
END
C-Arithmetic-mult.c
K 25
svn:wc:ra_dav:version-url
V 83
/repos/product-security/!svn/ver/203/defensive-coding/trunk/src/C-Arithmetic-mult.c
END
C-Pointers-remaining.c
K 25
svn:wc:ra_dav:version-url
V 86
/repos/product-security/!svn/ver/199/defensive-coding/trunk/src/C-Pointers-remaining.c
END
C-String-Functions.c
K 25
svn:wc:ra_dav:version-url
V 84
/repos/product-security/!svn/ver/266/defensive-coding/trunk/src/C-String-Functions.c
END

View file

@ -1,11 +0,0 @@
K 10
svn:ignore
V 96
TLS-Client-GNUTLS
TLS-Client-NSS
TLS-Client-OpenSSL
*.class
XML-Parser-Expat
C-String-Functions
END

View file

@ -1,609 +0,0 @@
10
dir
305
https://svn.devel.redhat.com/repos/product-security/defensive-coding/trunk/src
https://svn.devel.redhat.com/repos/product-security
2013-01-16T14:32:22.318444Z
302
fweimer@REDHAT.COM
has-props
9bd5cf0f-f2b3-0410-b1a9-d5c590f50bf1
x509_check_host.c
file
2013-01-10T17:17:51.274827Z
cf59145b923348078c5b629bbc36a612
2012-11-15T13:30:09.956181Z
235
fweimer@REDHAT.COM
10523
C-Arithmetic-add.c
file
2013-01-10T17:17:51.273827Z
ba6ae49b4b718e5adfa2a8d1f9cf3447
2012-11-08T17:22:27.237361Z
203
fweimer@REDHAT.COM
273
TLS-Client-OpenSSL.c
file
2013-01-10T17:17:51.273827Z
8225e11e3ec21df2a67e39b36565a1c9
2012-12-11T10:28:32.959649Z
276
fweimer@REDHAT.COM
9290
tcp_connect.c
file
2013-01-10T17:17:51.273827Z
b2d13024e62b0e1ba54bbd7daf9ae36d
2012-11-14T11:42:15.218138Z
230
fweimer@REDHAT.COM
1260
TLS-Client-Python.py
file
2013-01-10T17:17:51.274827Z
20864f5c6dd1badf9eeb2f5942296628
2012-12-06T16:39:47.679456Z
269
fweimer@REDHAT.COM
1785
tcp_connect.h
file
2013-01-10T17:17:51.274827Z
12a090a3cf7b1a3bc3328907aac1c9a1
2012-11-14T11:42:15.218138Z
230
fweimer@REDHAT.COM
212
check-function.py
file
2013-01-10T17:17:51.274827Z
c2c9d4e9c7d1c9f6e47546cf282447ba
2012-11-15T13:30:09.956181Z
235
fweimer@REDHAT.COM
502
XML-Parser-Expat.c
file
2013-01-10T17:17:51.274827Z
49a0ea5daaf9083655ad0aff29b6b75c
2012-12-11T13:38:24.215899Z
277
fweimer@REDHAT.COM
3193
TLSClientOpenJDK.java
file
2013-01-16T22:05:55.401436Z
4abb6802c85413ee92c77e48cbb9b286
2013-01-16T14:32:22.318444Z
302
fweimer@REDHAT.COM
9265
src.mk
file
2013-01-10T17:17:51.274827Z
4906704e783cba97d0a39690cf24d67a
2012-12-11T13:38:24.215899Z
277
fweimer@REDHAT.COM
1783
TLS-Client-NSS.c
file
2013-01-10T17:17:51.274827Z
61e2a46efaa13617ee79c1959caf9e57
2012-11-22T09:09:14.707205Z
259
fweimer@REDHAT.COM
8741
DERParser.java
file
2013-01-10T17:17:51.275827Z
31258b320a9b296256618b29be2770e4
2012-12-20T14:35:12.581576Z
298
fweimer@REDHAT.COM
8895
XMLParserOpenJDK.java
file
2013-01-10T17:17:51.275827Z
bdaea7ab5c87509aa6fe9ecd7e9b8ee9
2012-12-14T09:12:55.055726Z
287
fweimer@REDHAT.COM
11200
TLS-Client-GNUTLS.c
file
2013-01-10T17:17:51.275827Z
e6414ded83341422b06e84ac93cef929
2012-11-27T10:01:21.025921Z
264
fweimer@REDHAT.COM
8435
data
dir
C-Arithmetic-mult.c
file
2013-01-10T17:17:51.276827Z
f38568ac032b21c11f048af0b7ddbad5
2012-11-08T17:22:27.237361Z
203
fweimer@REDHAT.COM
176
C-Pointers-remaining.c
file
2013-01-10T17:17:51.276827Z
ccd36adf2f3ecdeec33c83fa3a232156
2012-11-08T16:59:17.247803Z
199
fweimer@REDHAT.COM
896
C-String-Functions.c
file
2013-01-10T17:17:51.276827Z
1b5f94f6d6c566d3d9ff3a4c76680636
2012-11-27T11:25:03.476147Z
266
fweimer@REDHAT.COM
961

View file

@ -1,17 +0,0 @@
//+ C Arithmetic-add
void report_overflow(void);
int
add(int a, int b)
{
int result = a + b;
if (a < 0 || b < 0) {
return -1;
}
// The compiler can optimize away the following if statement.
if (result < 0) {
report_overflow();
}
return result;
}
//-

View file

@ -1,12 +0,0 @@
void report_overflow(void);
//+ C Arithmetic-mult
unsigned
mul(unsigned a, unsigned b)
{
if (b && a > ((unsigned)-1) / b) {
report_overflow();
}
return a * b;
}
//-

View file

@ -1,51 +0,0 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
//+ C Pointers-remaining
ssize_t
extract_strings(const char *in, size_t inlen, char **out, size_t outlen)
{
const char *inp = in;
const char *inend = in + inlen;
char **outp = out;
char **outend = out + outlen;
while (inp != inend) {
size_t len;
char *s;
if (outp == outend) {
errno = ENOSPC;
goto err;
}
len = (unsigned char)*inp;
++inp;
if (len > (size_t)(inend - inp)) {
errno = EINVAL;
goto err;
}
s = malloc(len + 1);
if (s == NULL) {
goto err;
}
memcpy(s, inp, len);
inp += len;
s[len] = '\0';
*outp = s;
++outp;
}
return outp - out;
err:
{
int errno_old = errno;
while (out != outp) {
free(*out);
++out;
}
errno = errno_old;
}
return -1;
}
//-

View file

@ -1,49 +0,0 @@
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
static void
log_string(const char *s)
{
puts(s);
}
//+ C String-Functions-format
void log_format(const char *format, ...) __attribute__((format(printf, 1, 2)));
void
log_format(const char *format, ...)
{
char buf[1000];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
log_string(buf);
}
//-
int
main(void)
{
{
int numerator = 3, denominator = 4;
//+ C String-Functions-snprintf
char fraction[30];
snprintf(fraction, sizeof(fraction), "%d/%d", numerator, denominator);
//-
puts(fraction);
}
log_format("%s %x", "foo", 0xba4);
{
const char *const data = "this message is quite long";
//+ C String-Functions-strncpy
char buf[10];
strncpy(buf, data, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
//-
assert(strlen(buf) == 9);
assert(strncmp(buf, data, 9) == 0);
}
}

View file

@ -1,274 +0,0 @@
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
/**
* Minimalistic DER parser suitable for extracting the commonName attribute from
* a subject distinguished name of an X.509 certificate.
*
* <p>
* All elements in the DER structure can be parsed using:
*
* <pre>
* while (parser.isRemaining()) {
* if (!parser.next()) {
* handleError();
* break;
* }
* // Examine parser.getTagClass() etc. here.
* }
* </pre>
* <p>
* Note that this parser only handles structures of up to 16 MB in size.
*
* @author Florian Weimer <fweimer@redhat.com>
*
*/
public final class DERParser {
private final byte[] data;
private final int end;
private int offset;
private int tag = -1;
private int contentLength = -1;
// Content starts at offset - contentLength.
/**
* Creates a new parser for the specified array.
*
* @param data
* the data to parse (not copied)
* @throws NullPointerException
* the argument is null
*/
public DERParser(byte[] data) {
this(data, 0, data.length);
}
/**
* Creates an new parser for the slice [offset, offset + length) of the byte
* array.
*
* @param data
* the array to parse from (not copied)
* @param offset
* the offset at which to start parsing
* @param length
* the number of bytes to parse
* @throws NullPointerException
* the array argument is null
* @throws ArrayIndexOutOfBoundsException
* offset or length are negative or extend past the end of the
* array
*/
public DERParser(byte[] data, int offset, int length) {
this.data = data;
this.offset = offset;
end = offset + length;
if (offset < 0 || length < 0 || offset > data.length || end < 0
|| end > data.length)
throw new ArrayIndexOutOfBoundsException();
}
/**
* Returns true if more data can be extracted from the input.
*/
public boolean isRemaining() {
return offset < end;
}
/**
* Decodes the next tag/length/value element in the input data. After that,
* the parsed data can be examined using
* {@link #getTag()}, {@link #getLength()}, {@link #getString()}, and
* {@link #open()}.
* @return true if the TLV could be parsed successfully, false otherwise
*/
public boolean next() {
if (offset >= end)
throw new IllegalStateException("input exhausted");
int identifier = data[offset];
tag = identifier & ~0x20; // mask out P/C bit
if ((tag & 0x1f) == 31)
return false; // long form of type not supported
++offset;
if (offset >= end)
return false;
contentLength = data[offset];
if (contentLength < 0) {
int subLength = contentLength & 0x7f;
contentLength = 0;
switch (subLength) {
case 3:
++offset;
if (offset >= end)
return false;
contentLength = (data[offset] & 0xFF) << 16;
//$FALL-THROUGH$
case 2:
++offset;
if (offset >= end)
return false;
contentLength = contentLength | ((data[offset] & 0xFF) << 8);
//$FALL-THROUGH$
case 1:
++offset;
if (offset >= end)
return false;
contentLength = contentLength | (data[offset] & 0xFF);
break;
case 0:
default:
// We only need to support DER values up to 16 MB.
return false;
}
}
++offset;
if (offset + contentLength < 0 || offset + contentLength > end)
return false;
offset += contentLength;
return true;
}
public static final int TAG_OBJECT_IDENTIFIER = 6;
public static final int TAG_UTF8_STRING = 12;
public static final int TAG_SEQUENCE = 16;
public static final int TAG_SET = 17;
public static final int TAG_PRINTABLE_STRING = 19;
public static final int TAG_TELETEX_STRING = 20;
public static final int TAG_IA5_STRING = 22;
public static final int TAG_UNIVERSAL_STRING = 28;
public static final int TAG_BMP_STRING = 30;
/**
* Returns the tag value encountered by the most recent call to
* {@link #next()}.
* @return if the class is universal, an integer between 0 and 31,
* otherwise a positive integer less than 255 (which includes
* the class bits as well)
*/
public int getTag() {
return tag;
}
/**
* Returns the length (in bytes) of the content encountered by the most
* recent call to {@link #next()}.
*
* @return a non-negative integer
*/
public int getLength() {
return contentLength;
}
/**
* Returns true if the current content bytes are equal to the specified
* bytes.
*
* @param reference
* the byte array to compare the current content to
* @return true if length the byte content match
*/
public boolean isContent(byte[] reference) {
if (reference.length != contentLength)
return false;
int off = offset - contentLength;
for (int i = 0; i < reference.length; ++i) {
if (data[off + i] != reference[i])
return false;
}
return true;
}
/**
* Returns the current object as a string.
*
* @return a new string which contains the current content in decoded form
* @throws IllegalStateException
* the parser is not positioned at a string type
*/
public String getString() {
String charset;
switch (tag) {
case TAG_UTF8_STRING:
charset = "UTF-8";
break;
case TAG_PRINTABLE_STRING:
case TAG_TELETEX_STRING: // ASCII super-set not supported by Java
case TAG_IA5_STRING:
charset = "ASCII";
break;
case TAG_UNIVERSAL_STRING:
charset = "UTF-32BE";
break;
case TAG_BMP_STRING:
charset = "UTF-16BE";
break;
default:
throw new IllegalStateException(
"string requested for non-string type " + tag);
}
return new String(data, offset - contentLength, contentLength,
Charset.forName(charset));
}
/**
* Returns a DER parser for the current substructure
*
* @return a new DER parser object which shares the underlying byte array
* with this one
*/
public DERParser open() {
return new DERParser(data, offset - contentLength, contentLength);
}
// Code below only included for exploratory purposes.
private static final byte[] OID_COMMON_NAME = { 2 * 40 + 5, 4, 3 };
public static String getHostname(X509Certificate peer) {
DERParser outer = new DERParser(peer.getSubjectX500Principal()
.getEncoded());
if (!outer.next() || outer.getTag() != DERParser.TAG_SEQUENCE)
return null;
outer = outer.open();
String mostSpecificCN = null;
while (outer.isRemaining()) {
if (!outer.next() || outer.getTag() != DERParser.TAG_SET)
return null;
DERParser inner = outer.open();
if (!inner.next() || inner.getTag() != DERParser.TAG_SEQUENCE)
continue;
inner = inner.open();
if (inner.next() && inner.getTag() == TAG_OBJECT_IDENTIFIER
&& inner.isContent(OID_COMMON_NAME)) {
inner.next(); // read value
try {
mostSpecificCN = inner.getString();
} catch (IllegalArgumentException e) {
// Ignore unsupported string types.
}
}
}
return mostSpecificCN;
}
public static void main(String[] args) throws Exception {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
for (String arg : args) {
InputStream in = new BufferedInputStream(
new FileInputStream(arg));
try {
X509Certificate cert =
(X509Certificate) factory.generateCertificate(in);
System.out.format("%s: %s%n", arg, getHostname(cert));
} finally {
in.close();
}
}
}
}

View file

@ -1,279 +0,0 @@
#include <assert.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/x509.h>
#include "tcp_connect.h"
static void __attribute__((noreturn))
usage(const char *progname)
{
fprintf(stderr, "usage: %s HOST PORT\n", progname);
exit(2);
}
static void
info_certificate_override(const char *reason,
const gnutls_datum_t cert, const char *host)
{
#ifdef HAVE_GNUTLS_HASH_FAST
unsigned char digest[20];
assert(gnutls_hash_get_len(GNUTLS_DIG_SHA1) == sizeof(digest));
int ret = gnutls_hash_fast(GNUTLS_DIG_SHA1,
cert.data, cert.size, digest);
if (ret < 0) {
fprintf(stderr, "error: SHA1 digest failed: %s\n", gnutls_strerror(ret));
exit(1);
}
fprintf(stderr, "info: %s override for "
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%s%s%s\n",
reason,
digest[0], digest[1], digest[2], digest[3], digest[4],
digest[5], digest[6], digest[7], digest[8], digest[9],
digest[10], digest[11], digest[12], digest[13], digest[14],
digest[15], digest[16], digest[17], digest[18], digest[19],
host ? " (host name \"" : "", host ? host : "", host ? "\")" : "");
#endif
}
/* If certificate host name checking fails, this function is called to
implement an alternative matching, based on user overrides. */
static int
certificate_host_name_override(const gnutls_datum_t cert, const char *host)
{
// Just a dummy implementation. User overrides must be keyed both
// by certificate (or its hash) and host name.
if (getenv("CERT_OVERRIDE") != NULL) {
info_certificate_override("host name", cert, host);
return 1;
}
return 0;
}
/* If certificate validity checking fails, this function provides a
second chance to accept the peer certificate. If no user overrides
are needed, this function can be removed. */
static int
certificate_validity_override(const gnutls_datum_t cert)
{
// Just a dummy implementation for testing. This should check a
// user-maintained certificate store containing explicitly accepted
// certificates.
if (getenv("CERT_OVERRIDE") != NULL) {
info_certificate_override("certificate validity", cert, NULL);
return 1;
}
return 0;
}
int
main(int argc, char **argv)
{
if (argc != 3) {
usage(argv[0]);
}
//+ Features TLS-GNUTLS-Init
gnutls_global_init();
//-
//+ Features TLS-Client-GNUTLS-Credentials
// Load the trusted CA certificates.
gnutls_certificate_credentials_t cred = NULL;
int ret = gnutls_certificate_allocate_credentials (&cred);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_certificate_allocate_credentials: %s\n",
gnutls_strerror(ret));
exit(1);
}
// gnutls_certificate_set_x509_system_trust needs GNUTLS version 3.0
// or newer, so we hard-code the path to the certificate store
// instead.
static const char ca_bundle[] = "/etc/ssl/certs/ca-bundle.crt";
ret = gnutls_certificate_set_x509_trust_file
(cred, ca_bundle, GNUTLS_X509_FMT_PEM);
if (ret == 0) {
fprintf(stderr, "error: no certificates found in: %s\n", ca_bundle);
exit(1);
}
if (ret < 0) {
fprintf(stderr, "error: gnutls_certificate_set_x509_trust_files(%s): %s\n",
ca_bundle, gnutls_strerror(ret));
exit(1);
}
//-
const char *host = argv[1];
const char *service = argv[2];
// Perform name lookup, create the TCP client socket, and connect to
// the server.
int sockfd = tcp_connect(host, service);
if (sockfd < 0) {
perror("connect");
exit(1);
}
// Deactivate the Nagle algorithm.
{
const int val = 1;
int ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0) {
perror("setsockopt(TCP_NODELAY)");
exit(1);
}
}
//+ Features TLS-Client-GNUTLS-Connect
// Create the session object.
gnutls_session_t session;
ret = gnutls_init(&session, GNUTLS_CLIENT);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_init: %s\n",
gnutls_strerror(ret));
exit(1);
}
// Configure the cipher preferences.
const char *errptr = NULL;
ret = gnutls_priority_set_direct(session, "NORMAL", &errptr);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_priority_set_direct: %s\n"
"error: at: \"%s\"\n", gnutls_strerror(ret), errptr);
exit(1);
}
// Install the trusted certificates.
ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_credentials_set: %s\n",
gnutls_strerror(ret));
exit(1);
}
// Associate the socket with the session object and set the server
// name.
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(uintptr_t)sockfd);
ret = gnutls_server_name_set(session, GNUTLS_NAME_DNS,
host, strlen(host));
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_server_name_set: %s\n",
gnutls_strerror(ret));
exit(1);
}
// Establish the session.
ret = gnutls_handshake(session);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_handshake: %s\n",
gnutls_strerror(ret));
exit(1);
}
//-
//+ Features TLS-Client-GNUTLS-Verify
// Obtain the server certificate chain. The server certificate
// itself is stored in the first element of the array.
unsigned certslen = 0;
const gnutls_datum_t *const certs =
gnutls_certificate_get_peers(session, &certslen);
if (certs == NULL || certslen == 0) {
fprintf(stderr, "error: could not obtain peer certificate\n");
exit(1);
}
// Validate the certificate chain.
unsigned status = (unsigned)-1;
ret = gnutls_certificate_verify_peers2(session, &status);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_certificate_verify_peers2: %s\n",
gnutls_strerror(ret));
exit(1);
}
if (status != 0 && !certificate_validity_override(certs[0])) {
gnutls_datum_t msg;
#if GNUTLS_VERSION_AT_LEAST_3_1_4
int type = gnutls_certificate_type_get (session);
ret = gnutls_certificate_verification_status_print(status, type, &out, 0);
#else
ret = -1;
#endif
if (ret == 0) {
fprintf(stderr, "error: %s\n", msg.data);
gnutls_free(msg.data);
exit(1);
} else {
fprintf(stderr, "error: certificate validation failed with code 0x%x\n",
status);
exit(1);
}
}
//-
//+ Features TLS-Client-GNUTLS-Match
// Match the peer certificate against the host name.
// We can only obtain a set of DER-encoded certificates from the
// session object, so we have to re-parse the peer certificate into
// a certificate object.
gnutls_x509_crt_t cert;
ret = gnutls_x509_crt_init(&cert);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_x509_crt_init: %s\n",
gnutls_strerror(ret));
exit(1);
}
// The peer certificate is the first certificate in the list.
ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "error: gnutls_x509_crt_import: %s\n",
gnutls_strerror(ret));
exit(1);
}
ret = gnutls_x509_crt_check_hostname(cert, host);
if (ret == 0 && !certificate_host_name_override(certs[0], host)) {
fprintf(stderr, "error: host name does not match certificate\n");
exit(1);
}
gnutls_x509_crt_deinit(cert);
//-
//+ Features TLS-GNUTLS-Use
char buf[4096];
snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host);
ret = gnutls_record_send(session, buf, strlen(buf));
if (ret < 0) {
fprintf(stderr, "error: gnutls_record_send: %s\n", gnutls_strerror(ret));
exit(1);
}
ret = gnutls_record_recv(session, buf, sizeof(buf));
if (ret < 0) {
fprintf(stderr, "error: gnutls_record_recv: %s\n", gnutls_strerror(ret));
exit(1);
}
//-
write(STDOUT_FILENO, buf, ret);
//+ Features TLS-GNUTLS-Disconnect
// Initiate an orderly connection shutdown.
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
if (ret < 0) {
fprintf(stderr, "error: gnutls_bye: %s\n", gnutls_strerror(ret));
exit(1);
}
// Free the session object.
gnutls_deinit(session);
//-
//+ Features TLS-GNUTLS-Credentials-Close
gnutls_certificate_free_credentials(cred);
//-
}

View file

@ -1,297 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "tcp_connect.h"
//+ Features TLS-NSS-Includes
// NSPR include files
#include <prerror.h>
#include <prinit.h>
// NSS include files
#include <nss.h>
#include <pk11pub.h>
#include <secmod.h>
#include <ssl.h>
#include <sslproto.h>
// Private API, no other way to turn a POSIX file descriptor into an
// NSPR handle.
NSPR_API(PRFileDesc*) PR_ImportTCPSocket(int);
//-
static void __attribute__((noreturn))
usage(const char *progname)
{
fprintf(stderr, "usage: %s HOST PORT\n", progname);
exit(2);
}
SECStatus
bad_certificate(void *arg, PRFileDesc *fd)
{
const char *host = arg;
CERTCertificate *cert = SSL_PeerCertificate(fd);
if (cert == NULL) {
return SECFailure;
}
// Just a dummy implementation. User overrides must be keyed both
// by certificate (or its hash) and host name.
if (getenv("CERT_OVERRIDE") != NULL) {
unsigned char sha1[20];
if (PK11_HashBuf(SEC_OID_SHA1, sha1,
cert->derCert.data, cert->derCert.len) != SECSuccess) {
fprintf(stderr, "error: could not hash certificate\n");
return SECFailure;
}
SECItem si = {.data = sha1, .len = sizeof(sha1)};
char *hex = CERT_Hexify(&si, 1);
if (hex == NULL) {
fprintf(stderr, "error: could not hash certificate\n");
return SECFailure;
}
fprintf(stderr, "info: certificate override for %s (host name %s)\n",
hex, host);
PORT_Free(hex);
CERT_DestroyCertificate(cert);
return SECSuccess;
}
CERT_DestroyCertificate(cert);
return SECFailure;
}
int
main(int argc, char **argv)
{
if (argc != 3) {
usage(argv[0]);
}
const char *host = argv[1];
const char *service = argv[2];
// Perform name lookup, create the TCP client socket, and connect to
// the server.
int sockfd = tcp_connect(host, service);
if (sockfd < 0) {
perror("connect");
exit(1);
}
// Deactivate the Nagle algorithm.
{
const int val = 1;
int ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0) {
perror("setsockopt(TCP_NODELAY)");
exit(1);
}
}
//+ Features TLS-NSS-Init
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
NSSInitContext *const ctx =
NSS_InitContext("sql:/etc/pki/nssdb", "", "", "", NULL,
NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
if (ctx == NULL) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: NSPR error code %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
// Ciphers to enable.
static const PRUint16 good_ciphers[] = {
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
SSL_RSA_WITH_3DES_EDE_CBC_SHA,
SSL_NULL_WITH_NULL_NULL // sentinel
};
// Check if the current policy allows any strong ciphers. If it
// doesn't, switch to the "domestic" (unrestricted) policy. This is
// not thread-safe and has global impact. Consequently, we only do
// it if absolutely necessary.
int found_good_cipher = 0;
for (const PRUint16 *p = good_ciphers; *p != SSL_NULL_WITH_NULL_NULL;
++p) {
PRInt32 policy;
if (SSL_CipherPolicyGet(*p, &policy) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: policy for cipher %u: error %d: %s\n",
(unsigned)*p, err, PR_ErrorToName(err));
exit(1);
}
if (policy == SSL_ALLOWED) {
fprintf(stderr, "info: found cipher %x\n", (unsigned)*p);
found_good_cipher = 1;
break;
}
}
if (!found_good_cipher) {
if (NSS_SetDomesticPolicy() != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: NSS_SetDomesticPolicy: error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
}
// Initialize the trusted certificate store.
char module_name[] = "library=libnssckbi.so name=\"Root Certs\"";
SECMODModule *module = SECMOD_LoadUserModule(module_name, NULL, PR_FALSE);
if (module == NULL || !module->loaded) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: NSPR error code %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
//-
//+ Features TLS-Client-NSS-Connect
// Wrap the POSIX file descriptor. This is an internal NSPR
// function, but it is very unlikely to change.
PRFileDesc* nspr = PR_ImportTCPSocket(sockfd);
sockfd = -1; // Has been taken over by NSPR.
// Add the SSL layer.
{
PRFileDesc *model = PR_NewTCPSocket();
PRFileDesc *newfd = SSL_ImportFD(NULL, model);
if (newfd == NULL) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: NSPR error code %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
model = newfd;
newfd = NULL;
if (SSL_OptionSet(model, SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: set SSL_ENABLE_SSL2 error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
if (SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, PR_FALSE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: set SSL_V2_COMPATIBLE_HELLO error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
if (SSL_OptionSet(model, SSL_ENABLE_DEFLATE, PR_FALSE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: set SSL_ENABLE_DEFLATE error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
// Disable all ciphers (except RC4-based ciphers, for backwards
// compatibility).
const PRUint16 *const ciphers = SSL_GetImplementedCiphers();
for (unsigned i = 0; i < SSL_GetNumImplementedCiphers(); i++) {
if (ciphers[i] != SSL_RSA_WITH_RC4_128_SHA
&& ciphers[i] != SSL_RSA_WITH_RC4_128_MD5) {
if (SSL_CipherPrefSet(model, ciphers[i], PR_FALSE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: disable cipher %u: error %d: %s\n",
(unsigned)ciphers[i], err, PR_ErrorToName(err));
exit(1);
}
}
}
// Enable the strong ciphers.
for (const PRUint16 *p = good_ciphers; *p != SSL_NULL_WITH_NULL_NULL;
++p) {
if (SSL_CipherPrefSet(model, *p, PR_TRUE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: enable cipher %u: error %d: %s\n",
(unsigned)*p, err, PR_ErrorToName(err));
exit(1);
}
}
// Allow overriding invalid certificate.
if (SSL_BadCertHook(model, bad_certificate, (char *)host) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: SSL_BadCertHook error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
newfd = SSL_ImportFD(model, nspr);
if (newfd == NULL) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: SSL_ImportFD error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
nspr = newfd;
PR_Close(model);
}
// Perform the handshake.
if (SSL_ResetHandshake(nspr, PR_FALSE) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: SSL_ResetHandshake error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
if (SSL_SetURL(nspr, host) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: SSL_SetURL error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
if (SSL_ForceHandshake(nspr) != SECSuccess) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: SSL_ForceHandshake error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
//-
//+ Features TLS-NSS-Use
char buf[4096];
snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\n\r\n", host);
PRInt32 ret = PR_Write(nspr, buf, strlen(buf));
if (ret < 0) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: PR_Write error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
ret = PR_Read(nspr, buf, sizeof(buf));
if (ret < 0) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: PR_Read error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
//-
write(STDOUT_FILENO, buf, ret);
//+ Features TLS-Client-NSS-Close
// Send close_notify alert.
if (PR_Shutdown(nspr, PR_SHUTDOWN_BOTH) != PR_SUCCESS) {
const PRErrorCode err = PR_GetError();
fprintf(stderr, "error: PR_Read error %d: %s\n",
err, PR_ErrorToName(err));
exit(1);
}
// Closes the underlying POSIX file descriptor, too.
PR_Close(nspr);
//-
//+ Features TLS-NSS-Close
SECMOD_DestroyModule(module);
NSS_ShutdownContext(ctx);
//-
return 0;
}

View file

@ -1,329 +0,0 @@
#include <errno.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "tcp_connect.h"
int X509_check_host(X509 *, const unsigned char *chk, size_t chklen,
unsigned int flags);
static void __attribute__((noreturn))
usage(const char *progname)
{
fprintf(stderr, "usage: %s HOST PORT\n", progname);
exit(2);
}
static void
info_certificate_override(const char *reason, X509 *crt, const char *host)
{
int derlen = i2d_X509(crt, NULL);
if (derlen < 0) {
fprintf(stderr, "error: could not DER-encode certificate\n");
exit(1);
}
unsigned char *der = malloc(derlen);
if (der == NULL) {
perror("malloc");
exit(1);
}
{
unsigned char *p = der;
if (i2d_X509(crt, &p) < 0) {
fprintf(stderr, "error: could not DER-encode certificate\n");
exit(1);
}
}
unsigned char digest[20];
SHA1(der, derlen, digest);
fprintf(stderr, "info: %s override for "
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%s%s%s\n",
reason,
digest[0], digest[1], digest[2], digest[3], digest[4],
digest[5], digest[6], digest[7], digest[8], digest[9],
digest[10], digest[11], digest[12], digest[13], digest[14],
digest[15], digest[16], digest[17], digest[18], digest[19],
host ? " (host name \"" : "", host ? host : "", host ? "\")" : "");
free(der);
}
/* If certificate host name checking fails, this function is called to
implement an alternative matching, based on user overrides. */
static int
certificate_host_name_override(X509 *crt, const char *host)
{
// Just a dummy implementation. User overrides must be keyed both
// by certificate (or its hash) and host name.
if (getenv("CERT_OVERRIDE") != NULL) {
info_certificate_override("host name", crt, host);
return 1;
}
return 0;
}
/* If certificate validity checking fails, this function provides a
second chance to accept the peer certificate. If no user overrides
are needed, this function can be removed. */
static int
certificate_validity_override(X509 *crt)
{
// Just a dummy implementation for testing. This should check a
// user-maintained certificate store containing explicitly accepted
// certificates.
if (getenv("CERT_OVERRIDE") != NULL) {
info_certificate_override("certificate validity", crt, NULL);
return 1;
}
return 0;
}
static void __attribute__((noreturn))
failure(const char *msg)
{
fprintf(stderr, "error: %s: %s\n", msg, strerror(errno));
exit(2);
}
//+ Features TLS-OpenSSL-Errors
static void __attribute__((noreturn))
ssl_print_error_and_exit(SSL *ssl, const char *op, int ret)
{
int subcode = SSL_get_error(ssl, ret);
switch (subcode) {
case SSL_ERROR_NONE:
fprintf(stderr, "error: %s: no error to report\n", op);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_X509_LOOKUP:
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
fprintf(stderr, "error: %s: invalid blocking state %d\n", op, subcode);
break;
case SSL_ERROR_SSL:
fprintf(stderr, "error: %s: TLS layer problem\n", op);
case SSL_ERROR_SYSCALL:
fprintf(stderr, "error: %s: system call failed: %s\n", op, strerror(errno));
break;
case SSL_ERROR_ZERO_RETURN:
fprintf(stderr, "error: %s: zero return\n", op);
}
exit(1);
}
//-
int
main(int argc, char **argv)
{
if (argc != 3) {
usage(argv[0]);
}
BIO *bio_err=BIO_new_fp(stderr, BIO_NOCLOSE);
if (bio_err == NULL) {
perror("BIO_ne_fp(stderr)");
exit(1);
}
//+ Features TLS-Client-OpenSSL-Init
// The following call prints an error message and calls exit() if
// the OpenSSL configuration file is unreadable.
OPENSSL_config(NULL);
// Provide human-readable error messages.
SSL_load_error_strings();
// Register ciphers.
SSL_library_init();
//-
//+ Features TLS-Client-OpenSSL-CTX
// Configure a client connection context. Send a hendshake for the
// highest supported TLS version, and disable compression.
const SSL_METHOD *const req_method = SSLv23_client_method();
SSL_CTX *const ctx = SSL_CTX_new(req_method);
if (ctx == NULL) {
ERR_print_errors(bio_err);
exit(1);
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION);
// Adjust the ciphers list based on a whitelist. First enable all
// ciphers of at least medium strength, to get the list which is
// compiled into OpenSSL.
if (SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM") != 1) {
ERR_print_errors(bio_err);
exit(1);
}
{
// Create a dummy SSL session to obtain the cipher list.
SSL *ssl = SSL_new(ctx);
if (ssl == NULL) {
ERR_print_errors(bio_err);
exit(1);
}
STACK_OF(SSL_CIPHER) *active_ciphers = SSL_get_ciphers(ssl);
if (active_ciphers == NULL) {
ERR_print_errors(bio_err);
exit(1);
}
// Whitelist of candidate ciphers.
static const char *const candidates[] = {
"AES128-GCM-SHA256", "AES128-SHA256", "AES256-SHA256", // strong ciphers
"AES128-SHA", "AES256-SHA", // strong ciphers, also in older versions
"RC4-SHA", "RC4-MD5", // backwards compatibility, supposed to be weak
"DES-CBC3-SHA", "DES-CBC3-MD5", // more backwards compatibility
NULL
};
// Actually selected ciphers.
char ciphers[300];
ciphers[0] = '\0';
for (const char *const *c = candidates; *c; ++c) {
for (int i = 0; i < sk_SSL_CIPHER_num(active_ciphers); ++i) {
if (strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(active_ciphers, i)),
*c) == 0) {
if (*ciphers) {
strcat(ciphers, ":");
}
strcat(ciphers, *c);
break;
}
}
}
SSL_free(ssl);
// Apply final cipher list.
if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) {
ERR_print_errors(bio_err);
exit(1);
}
}
// Load the set of trusted root certificates.
if (!SSL_CTX_set_default_verify_paths(ctx)) {
ERR_print_errors(bio_err);
exit(1);
}
//-
const char *host = argv[1];
const char *service = argv[2];
// Perform name lookup, create the TCP client socket, and connect to
// the server.
int sockfd = tcp_connect(host, service);
if (sockfd < 0) {
perror("connect");
exit(1);
}
// Deactivate the Nagle algorithm.
//+ Features TLS-Nagle
const int val = 1;
int ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0) {
perror("setsockopt(TCP_NODELAY)");
exit(1);
}
//-
//+ Features TLS-Client-OpenSSL-Connect
// Create the connection object.
SSL *ssl = SSL_new(ctx);
if (ssl == NULL) {
ERR_print_errors(bio_err);
exit(1);
}
SSL_set_fd(ssl, sockfd);
// Enable the ServerNameIndication extension
if (!SSL_set_tlsext_host_name(ssl, host)) {
ERR_print_errors(bio_err);
exit(1);
}
// Perform the TLS handshake with the server.
ret = SSL_connect(ssl);
if (ret != 1) {
// Error status can be 0 or negative.
ssl_print_error_and_exit(ssl, "SSL_connect", ret);
}
// Obtain the server certificate.
X509 *peercert = SSL_get_peer_certificate(ssl);
if (peercert == NULL) {
fprintf(stderr, "peer certificate missing");
exit(1);
}
// Check the certificate verification result. Allow an explicit
// certificate validation override in case verification fails.
int verifystatus = SSL_get_verify_result(ssl);
if (verifystatus != X509_V_OK && !certificate_validity_override(peercert)) {
fprintf(stderr, "SSL_connect: verify result: %s\n",
X509_verify_cert_error_string(verifystatus));
exit(1);
}
// Check if the server certificate matches the host name used to
// establish the connection.
// FIXME: Currently needs OpenSSL 1.1.
if (X509_check_host(peercert, (const unsigned char *)host, strlen(host),
0) != 1
&& !certificate_host_name_override(peercert, host)) {
fprintf(stderr, "SSL certificate does not match host name\n");
exit(1);
}
X509_free(peercert);
//-
//+ Features TLS-Client-OpenSSL-Connection-Use
const char *const req = "GET / HTTP/1.0\r\n\r\n";
if (SSL_write(ssl, req, strlen(req)) < 0) {
ssl_print_error_and_exit(ssl, "SSL_write", ret);
}
char buf[4096];
ret = SSL_read(ssl, buf, sizeof(buf));
if (ret < 0) {
ssl_print_error_and_exit(ssl, "SSL_read", ret);
}
//-
write(STDOUT_FILENO, buf, ret);
//+ Features TLS-OpenSSL-Connection-Close
// Send the close_notify alert.
ret = SSL_shutdown(ssl);
switch (ret) {
case 1:
// A close_notify alert has already been received.
break;
case 0:
// Wait for the close_notify alert from the peer.
ret = SSL_shutdown(ssl);
switch (ret) {
case 0:
fprintf(stderr, "info: second SSL_shutdown returned zero\n");
break;
case 1:
break;
default:
ssl_print_error_and_exit(ssl, "SSL_shutdown 2", ret);
}
break;
default:
ssl_print_error_and_exit(ssl, "SSL_shutdown 1", ret);
}
SSL_free(ssl);
close(sockfd);
//-
//+ Features TLS-OpenSSL-Context-Close
SSL_CTX_free(ctx);
//-
BIO_free(bio_err);
return 0;
}

View file

@ -1,56 +0,0 @@
#!/usr/bin/python
# WARNING: See the guidelines for problems with this code!
import socket
import ssl
import sys
_, host, port = sys.argv
#+ Features TLS-Client-Python-check_host_name
def check_host_name(peercert, name):
"""Simple certificate/host name checker. Returns True if the
certificate matches, False otherwise. Does not support
wildcards."""
# Check that the peer has supplied a certificate.
# None/{} is not acceptable.
if not peercert:
return False
if peercert.has_key("subjectAltName"):
for typ, val in peercert["subjectAltName"]:
if typ == "DNS" and val == name:
return True
else:
# Only check the subject DN if there is no subject alternative
# name.
cn = None
for attr, val in peercert["subject"]:
# Use most-specific (last) commonName attribute.
if attr == "commonName":
cn = val
if cn is not None:
return cn == name
return False
#-
# WARNING: See the guidelines for problems with this code!
sock = socket.create_connection((host, port))
#+ Features TLS-Client-Python-Connect
sock = ssl.wrap_socket(sock,
ciphers="HIGH:-aNULL:-eNULL:-PSK:RC4-SHA:RC4-MD5",
ssl_version=ssl.PROTOCOL_TLSv1,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs='/etc/ssl/certs/ca-bundle.crt')
# getpeercert() triggers the handshake as a side effect.
if not check_host_name(sock.getpeercert(), host):
raise IOError("peer certificate does not match host name")
#-
#+ Features TLS-Python-Use
sock.write("GET / HTTP/1.1\r\nHost: " + host + "\r\n\r\n")
print sock.read()
#-
#+ Features TLS-Python-Close
sock.close()
#-

View file

@ -1,262 +0,0 @@
//+ Features TLS-Client-OpenJDK-Import
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import sun.security.util.HostnameChecker;
//-
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.ArrayList;
import java.nio.charset.Charset;
public class TLSClientOpenJDK {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
usage();
}
int index = 0;
byte[] certHash = null;
if (args[index].equals("--accept")) {
++index;
if (args.length != 4) {
usage();
}
certHash = decodeHex(args[index++]);
} else if (args.length != 2) {
usage();
}
String host = args[index++];
int port;
try {
port = Integer.parseInt(args[index]);
} catch (NumberFormatException e) {
port = 0;
}
if (port <= 0 || port > 65535) {
usage();
}
SSLContext ctx;
if (certHash == null) {
ctx = createContext();
} else {
ctx = createContextForCertificate(certHash);
}
SSLParameters params = createParameters(ctx);
if (certHash == null) {
params.setEndpointIdentificationAlgorithm(null);
}
runDemo(ctx, params, host, port);
}
private static SSLContext createContext() throws Exception {
//+ Features TLS-Client-OpenJDK-Context
// Create the context. Specify the SunJSSE provider to avoid
// picking up third-party providers. Try the TLS 1.2 provider
// first, then fall back to TLS 1.0.
SSLContext ctx;
try {
ctx = SSLContext.getInstance("TLSv1.2", "SunJSSE");
} catch (NoSuchAlgorithmException e) {
try {
ctx = SSLContext.getInstance("TLSv1", "SunJSSE");
} catch (NoSuchAlgorithmException e1) {
// The TLS 1.0 provider should always be available.
throw new AssertionError(e1);
} catch (NoSuchProviderException e1) {
throw new AssertionError(e1);
}
} catch (NoSuchProviderException e) {
// The SunJSSE provider should always be available.
throw new AssertionError(e);
}
ctx.init(null, null, null);
//-
return ctx;
}
static
//+ Features TLS-Client-OpenJDK-MyTrustManager
public class MyTrustManager implements X509TrustManager {
private final byte[] certHash;
public MyTrustManager(byte[] certHash) throws Exception {
this.certHash = certHash;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
byte[] digest = getCertificateDigest(chain[0]);
String digestHex = formatHex(digest);
if (Arrays.equals(digest, certHash)) {
System.err.println("info: accepting certificate: " + digestHex);
} else {
throw new CertificateException("certificate rejected: " +
digestHex);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
//-
private static SSLContext createContextForCertificate(byte[] certHash)
throws Exception {
//+ Features TLS-Client-OpenJDK-Context_For_Cert
SSLContext ctx;
try {
ctx = SSLContext.getInstance("TLSv1.2", "SunJSSE");
} catch (NoSuchAlgorithmException e) {
try {
ctx = SSLContext.getInstance("TLSv1", "SunJSSE");
} catch (NoSuchAlgorithmException e1) {
throw new AssertionError(e1);
} catch (NoSuchProviderException e1) {
throw new AssertionError(e1);
}
} catch (NoSuchProviderException e) {
throw new AssertionError(e);
}
MyTrustManager tm = new MyTrustManager(certHash);
ctx.init(null, new TrustManager[] {tm}, null);
//-
return ctx;
}
private static SSLParameters createParameters(SSLContext ctx)
throws Exception {
//+ Features TLS-OpenJDK-Parameters
// Prepare TLS parameters. These have to applied to every TLS
// socket before the handshake is triggered.
SSLParameters params = ctx.getDefaultSSLParameters();
// Do not send an SSL-2.0-compatible Client Hello.
ArrayList<String> protocols = new ArrayList<String>(
Arrays.asList(params.getProtocols()));
protocols.remove("SSLv2Hello");
params.setProtocols(protocols.toArray(new String[protocols.size()]));
// Adjust the supported ciphers.
ArrayList<String> ciphers = new ArrayList<String>(
Arrays.asList(params.getCipherSuites()));
ciphers.retainAll(Arrays.asList(
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_RC4_128_SHA1",
"SSL_RSA_WITH_RC4_128_MD5",
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV"));
params.setCipherSuites(ciphers.toArray(new String[ciphers.size()]));
//-
// Activate host name verification. Requires OpenJDK 7.
//+ Features TLS-Client-OpenJDK-Hostname
params.setEndpointIdentificationAlgorithm("HTTPS");
//-
return params;
}
private static void runDemo(SSLContext ctx, SSLParameters params,
String host, int port) throws Exception {
// Note: The code below misses the close() call, to avoid
// messing up the indentation in the generated documentation.
//+ Features TLS-Client-OpenJDK-Connect
// Create the socket and connect it at the TCP layer.
SSLSocket socket = (SSLSocket) ctx.getSocketFactory()
.createSocket(host, port);
// Disable the Nagle algorithm.
socket.setTcpNoDelay(true);
// Adjust ciphers and protocols.
socket.setSSLParameters(params);
// Perform the handshake.
socket.startHandshake();
// Validate the host name. The match() method throws
// CertificateException on failure.
X509Certificate peer = (X509Certificate)
socket.getSession().getPeerCertificates()[0];
// This is the only way to perform host name checking on OpenJDK 6.
HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match(
host, peer);
//-
//+ Features TLS-Client-OpenJDK-Use
socket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n"
.getBytes(Charset.forName("UTF-8")));
byte[] buffer = new byte[4096];
int count = socket.getInputStream().read(buffer);
System.out.write(buffer, 0, count);
//-
}
private static byte[] decodeHex(String s) {
byte[] result = new byte[32];
if (s.length() != result.length * 2) {
throw new IllegalArgumentException(s);
}
for (int i = 0; i < result.length; ++i) {
int a = Character.digit(s.charAt(2 * i), 16);
int b = Character.digit(s.charAt(2 * i + 1), 16);
if (a < 0 || b < 0) {
throw new IllegalArgumentException(s);
}
result[i] = (byte) ((a << 4) | b);
}
return result;
}
private static String formatHex(byte[] digest) {
String digestHex;
{
StringBuilder sb = new StringBuilder(digest.length * 2);
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xFF));
}
digestHex = sb.toString();
}
return digestHex;
}
private static byte[] getCertificateDigest(X509Certificate chain)
throws AssertionError, CertificateEncodingException {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e1) {
throw new AssertionError(e1);
}
byte[] digest = md.digest(chain.getEncoded());
return digest;
}
private static void usage() {
System.err.format("usage: %s [--accept CERT-HASH] HOST PORT%n",
TLSClientOpenJDK.class.getName());
System.exit(1);
}
}

View file

@ -1,135 +0,0 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <expat.h>
static void
print_escaped(const char *p, size_t len)
{
const char *end = p + len;
while (p < end) {
unsigned char ch = *p;
// Technically, we should also match on certain UTF-8 sequences,
// but this is not implemented here.
if ((0x01 <= ch && ch <= 0x08)
|| ch == 0x0B || ch == 0x0C
|| (0x0E <= ch && ch <= 0x1F)
|| ch == '"' || ch == '\'' || ch == '<' || ch == '>' || ch == '"'
|| ch == 0x7F) {
printf("&#%d;", (int)ch);
} else {
putc(ch, stdout);
}
++p;
}
}
static void
StartElementHandler(void *userData,
const XML_Char *name, const XML_Char **attrs)
{
printf("<%s", name);
while (*attrs) {
printf(" %s=\"", *attrs);
++attrs;
print_escaped(*attrs, strlen(*attrs));
++attrs;
putc('"', stdout);
}
putc('>', stdout);
}
static void
EndElementHandler(void *userData, const XML_Char *name)
{
printf("</%s>", name);
}
static void
CharacterDataHandler(void *userData, const XML_Char *s, int len)
{
print_escaped(s, len);
}
static void
CommentHandler(void *userData, const XML_Char *s)
{
printf("<!-- %s -->", s);
}
//+ Tasks Serialization-XML-Expat-EntityDeclHandler
// Stop the parser when an entity declaration is encountered.
static void
EntityDeclHandler(void *userData,
const XML_Char *entityName, int is_parameter_entity,
const XML_Char *value, int value_length,
const XML_Char *base, const XML_Char *systemId,
const XML_Char *publicId, const XML_Char *notationName)
{
XML_StopParser((XML_Parser)userData, XML_FALSE);
}
//-
int
main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "usage: %s XML-FILE\n", argv[0]);
return 2;
}
const char *file = argv[1];
int fd = open(file, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
perror("open");
return 1;
}
//+ Tasks Serialization-XML-Expat-Create
XML_Parser parser = XML_ParserCreate("UTF-8");
if (parser == NULL) {
fprintf(stderr, "XML_ParserCreate failed\n");
close(fd);
exit(1);
}
// EntityDeclHandler needs a reference to the parser to stop
// parsing.
XML_SetUserData(parser, parser);
// Disable entity processing, to inhibit entity expansion.
XML_SetEntityDeclHandler(parser, EntityDeclHandler);
//-
// Handlers for demonstration purposes.
XML_SetElementHandler(parser, StartElementHandler, EndElementHandler);
XML_SetCharacterDataHandler(parser, CharacterDataHandler);
XML_SetCommentHandler(parser, CommentHandler);
char buffer[8192];
ssize_t ret;
do {
ret = read(fd, buffer, sizeof(buffer));
if (ret < 0) {
perror("read");
XML_ParserFree(parser);
close(fd);
return 1;
}
enum XML_Status status = XML_Parse(parser, buffer, ret, ret == 0);
if (status != XML_STATUS_OK) {
fprintf(stderr, "%s:%zu:%zu: error: %s\n",
file, XML_GetCurrentLineNumber(parser),
XML_GetCurrentColumnNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser)));
XML_ParserFree(parser);
close(fd);
return 1;
}
} while (ret != 0);
XML_ParserFree(parser);
close(fd);
return 0;
}

View file

@ -1,286 +0,0 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
//+ Tasks Serialization-XML-OpenJDK-Imports
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
//-
public final class XMLParserOpenJDK {
public static void main(String[] args) throws Exception {
String validationType = args[0];
File schema = new File(args[1]);
String file = args[2];
if (validationType.equals("OpenJDK-XSD-SAX")) {
validateXSDSAX(schema, file);
} else if (validationType.equals("OpenJDK-RNG-SAX")) {
validateSAX(XMLConstants.RELAXNG_NS_URI, schema, file);
} else if (validationType.equals("OpenJDK-DTD-SAX")) {
validateSAX(XMLConstants.XML_DTD_NS_URI, schema, file);
} else if (validationType.equals("OpenJDK-XSD-DOM")) {
validateXSDDOM(schema, file);
} else if (validationType.equals("OpenJDK-RNG-DOM")) {
validateDOM(XMLConstants.W3C_XML_SCHEMA_NS_URI,
schema, file, false);
} else if (validationType.equals("OpenJDK-DTD-DOM")) {
validateDOM(XMLConstants.XML_DTD_NS_URI, schema, file, false);
} else if (validationType.equals("OpenJDK-XSD-DOM-Validate")) {
validateDOM(XMLConstants.W3C_XML_SCHEMA_NS_URI,
schema, file, true);
} else if (validationType.equals("OpenJDK-RNG-DOM-Validate")) {
validateDOM(XMLConstants.W3C_XML_SCHEMA_NS_URI,
schema, file, true);
} else if (validationType.equals("OpenJDK-DTD-DOM-Validate")) {
validateDOM(XMLConstants.XML_DTD_NS_URI, schema, file, true);
} else if (validationType.equals("OpenJDK-SAX")) {
parseSAX(file, false);
} else if (validationType.equals("OpenJDK-DOM")) {
parseDOM(file, false);
} else if (validationType.equals("OpenJDK-SAX-Validate")) {
parseSAX(file, true);
} else if (validationType.equals("OpenJDK-DOM-Validate")) {
parseDOM(file, true);
} else {
throw new Exception("invalid validator: " + validationType);
}
}
static
//+ Tasks Serialization-XML-OpenJDK-NoResourceResolver
class NoResourceResolver implements LSResourceResolver {
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
// Throwing an exception stops validation.
throw new RuntimeException(String.format(
"resolution attempt: type=%s namespace=%s " +
"publicId=%s systemId=%s baseURI=%s",
type, namespaceURI, publicId, systemId, baseURI));
}
}
//-
private static void validateXSDSAX( File schemaFile, String file)
throws Exception {
FileInputStream inputStream = new FileInputStream(file);
try {
//+ Tasks Serialization-XML-OpenJDK_Parse-XMLSchema_SAX
SchemaFactory factory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
// This enables restrictions on the schema and document
// complexity.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// This prevents resource resolution by the schema itself.
// If the schema is trusted and references additional files,
// this line must be omitted, otherwise loading these files
// will fail.
factory.setResourceResolver(new NoResourceResolver());
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
// This prevents external resource resolution.
validator.setResourceResolver(new NoResourceResolver());
validator.validate(new SAXSource(new InputSource(inputStream)));
//-
} finally {
inputStream.close();
}
}
/**
* Same as {@link #validateXSDSAX(File, String)}, but the schema type URI
* is not hard-coded.
*/
private static void validateSAX(String uri, File schemaFile, String file)
throws Exception {
FileInputStream inputStream = new FileInputStream(file);
try {
SchemaFactory factory = SchemaFactory.newInstance(uri);
// This enables restrictions on the schema and document
// complexity.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// This prevents resource resolution by the schema itself.
// If the schema is trusted and references additional files,
// this line must be omitted, otherwise loading these files
// will fail.
factory.setResourceResolver(new NoResourceResolver());
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
// This prevents external resource resolution.
validator.setResourceResolver(new NoResourceResolver());
validator.validate(new SAXSource(new InputSource(inputStream)));
} finally {
inputStream.close();
}
}
private static void validateXSDDOM(File schemaFile, String file) throws Exception {
FileInputStream inputStream = new FileInputStream(file);
try {
Document document = parseDOM(file, false);
//+ Tasks Serialization-XML-OpenJDK_Parse-XMLSchema_DOM
SchemaFactory factory = SchemaFactory.newInstance(
XMLConstants.W3C_XML_SCHEMA_NS_URI);
// This enables restrictions on schema complexity.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// The following line prevents resource resolution
// by the schema itself.
factory.setResourceResolver(new NoResourceResolver());
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
// This prevents external resource resolution.
validator.setResourceResolver(new NoResourceResolver());
validator.validate(new DOMSource(document));
//-
} finally {
inputStream.close();
}
}
/**
* Same as {@link #validateXSDDOM(File, String)}, but does not hard-code
* the schema type URI.
*/
private static void validateDOM(String uri, File schemaFile, String file,
boolean validate) throws Exception {
FileInputStream inputStream = new FileInputStream(file);
try {
Document document = parseDOM(file, validate);
SchemaFactory factory = SchemaFactory.newInstance(uri);
// This enables restrictions on schema complexity.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// The following line prevents resource resolution
// by the schema itself.
factory.setResourceResolver(new NoResourceResolver());
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
// This prevents external resource resolution.
validator.setResourceResolver(new NoResourceResolver());
validator.validate(new DOMSource(document));
} finally {
inputStream.close();
}
}
static
//+ Tasks Serialization-XML-OpenJDK-Errors
class Errors implements ErrorHandler {
@Override
public void warning(SAXParseException exception) {
exception.printStackTrace();
}
@Override
public void fatalError(SAXParseException exception) {
exception.printStackTrace();
}
@Override
public void error(SAXParseException exception) {
exception.printStackTrace();
}
}
//-
static
//+ Tasks Serialization-XML-OpenJDK-NoEntityResolver
class NoEntityResolver implements EntityResolver {
@Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
// Throwing an exception stops validation.
throw new IOException(String.format(
"attempt to resolve \"%s\" \"%s\"", publicId, systemId));
}
}
//-
private static void parseSAX(String file, boolean validate)
throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
if (validate) {
factory.setValidating(true);
}
SAXParser parser = factory.newSAXParser();
FileInputStream in = new FileInputStream(file);
try {
XMLReader reader = parser.getXMLReader();
reader.setEntityResolver(new NoEntityResolver());
reader.setErrorHandler(new Errors());
reader.parse(new InputSource(in));
} finally {
in.close();
}
}
private static Document parseDOM(String file, boolean validate)
throws Exception {
FileInputStream inputStream = new FileInputStream(file);
try {
return parseDOMInternal(inputStream);
} finally {
inputStream.close();
}
}
private static Document parseDOMInternal(FileInputStream inputStream)
throws ParserConfigurationException, SAXException, IOException {
//+ Tasks Serialization-XML-OpenJDK_Parse-DOM
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Impose restrictions on the complexity of the DTD.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// Turn on validation.
// This step can be omitted if validation is not desired.
factory.setValidating(true);
// Parse the document.
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(new NoEntityResolver());
builder.setErrorHandler(new Errors());
Document document = builder.parse(inputStream);
//-
return document;
}
}

View file

@ -1,18 +0,0 @@
#!/usr/bin/python
# Usage: python check-function.py DSO/FUNCTION-NAME/OUTPUT
#
# Prints OUTPUT if libDSO.so can be loaded and defines FUNCTION-NAME
# as a function, or nothing otherwise.
import ctypes
import sys
for (dsoname, funcname, output) in [arg.split("/", 3)
for arg in sys.argv[1:]]:
try:
dso = ctypes.CDLL("lib{0}.so".format(dsoname))
except OSError:
continue
if getattr(dso, funcname, None) is not None:
print output

View file

@ -1,53 +0,0 @@
.PHONY: build-sources
CC = gcc
CWARNFLAGS = -Wall -W -Wno-unused-parameter -Werror=implicit-function-declaration
CFLAGS = -std=gnu99 -O2 $(CWARNFLAGS) -g
# List files which should only be compiled for syntax checking.
compile_only += C-Pointers-remaining
compile_only += C-Arithmetic-add
compile_only += C-Arithmetic-mult
# List Java files which sould be compiled
compile_java += TLSClientOpenJDK
# List fiels which will be compiled and linked, together with
# additional dependencies.
compile_and_link += C-String-Functions
compile_and_link += TLS-Client-OpenSSL
LIBS_TLS-Client-OpenSSL = -lssl -lcrypto
compile_and_link += TLS-Client-GNUTLS
LIBS_TLS-Client-GNUTLS = -lgnutls
compile_and_link += TLS-Client-NSS
CFLAGS_TLS-Client-NSS = -I/usr/include/nspr4 -I/usr/include/nss3
LIBS_TLS-Client-NSS = -lnss3 -lnspr4 -lssl3
compile_and_link += XML-Parser-Expat
LIBS_XML-Parser-Expat = -lexpat
# Define preprocessor symbols if certain functions exist.
CHECK_FUNCTION = crypto/X509_check_host/-DHAVE_X509_CHECK_HOST \
gnutls/gnutls_hash_fast/-DHAVE_GNUTLS_HASH_FAST
DEFINES := $(shell python src/check-function.py $(CHECK_FUNCTION))
CLASS_compile_java := $(patsubst %,src/%.class,$(compile_java))
BIN_compile_and_link := $(patsubst %,src/%,$(compile_and_link))
build-src: $(patsubst %,src/%.o,$(compile_only)) $(CLASS_compile_java) \
$(BIN_compile_and_link)
clean-src:
-rm src/*.o src/*.class $(BIN_compile_and_link)
src/%.o: src/%.c
$(CC) $(CFLAGS) $(DEFINES) $(CFLAGS_$(basename $(notdir $@))) -c $< -o $@
src/%.class: src/%.java
javac -source 1.6 -target 1.6 -Xlint:all $^
src/%: src/%.o
$(CC) $^ -o $@ $(LIBS_$(notdir $@))
src/TLS-Client-GNUTLS: src/tcp_connect.o
src/TLS-Client-OpenSSL: src/tcp_connect.o src/x509_check_host.o
src/TLS-Client-NSS: src/tcp_connect.o

View file

@ -1,52 +0,0 @@
#include "tcp_connect.h"
#include <errno.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int
tcp_connect(const char *host, const char *service)
{
// A real-world implementation should connect to one IPv4 and one
// IPv address in parallel, until a responsive server is found.
const struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *result;
int ret = getaddrinfo(host, service, &hints, &result);
if (ret != 0) {
fprintf(stderr, "error: name lookup failure for %s/%s: %s\n",
host, service, gai_strerror(ret));
exit(1);
}
if (result == NULL) {
fprintf(stderr, "error: no addresses found for %s/%s\n", host, service);
freeaddrinfo(result);
return -1;
}
for (const struct addrinfo *ai = result; ai; ai = ai->ai_next) {
ret = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (ret < 0) {
continue;
}
if (connect(ret, ai->ai_addr, ai->ai_addrlen) == 0) {
break;
}
int save = errno;
close(ret);
errno = save;
ret = -1;
}
if (ret < 0) {
return -1;
}
freeaddrinfo(result);
return ret;
}

View file

@ -1,6 +0,0 @@
#pragma once
/* Establishes a TCP connect to SERVICE at HOST and returns the socket
descriptor. Calls exit on error (and prints an error message). */
int tcp_connect(const char *host, const char *service);

View file

@ -1,355 +0,0 @@
// This file is based on a (currently patched) file from OpenSSL,
// namely crypto/x509v3/v3_utl.c. It is included here for testing
// purposes only.
/* v3_utl.c */
/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project.
*/
/* ====================================================================
* Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* licensing@OpenSSL.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
#ifndef HAVE_X509_CHECK_HOST
#include <string.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
/* Always check subject name for host match even if subject alt names present */
#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1
/* Disable wild-card matching for dnsName fields and common name. */
#define X509_CHECK_FLAG_NO_WILDCARDS 0x2
typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len,
const unsigned char *subject, size_t subject_len);
/* Compare while ASCII ignoring case. */
static int equal_nocase(const unsigned char *pattern, size_t pattern_len,
const unsigned char *subject, size_t subject_len)
{
if (pattern_len != subject_len)
return 0;
while (pattern_len)
{
unsigned char l = *pattern;
unsigned char r = *subject;
/* The pattern must not contain NUL characters. */
if (l == 0)
return 0;
if (l != r)
{
if ('A' <= l && l <= 'Z')
l = (l - 'A') + 'a';
if ('A' <= r && r <= 'Z')
r = (r - 'A') + 'a';
if (l != r)
return 0;
}
++pattern;
++subject;
--pattern_len;
}
return 1;
}
/* Compare using memcmp. */
static int equal_case(const unsigned char *pattern, size_t pattern_len,
const unsigned char *subject, size_t subject_len)
{
/* The pattern must not contain NUL characters. */
if (memchr(pattern, '\0', pattern_len) != NULL)
return 0;
if (pattern_len != subject_len)
return 0;
return !memcmp(pattern, subject, pattern_len);
}
/* RFC 5280, section 7.5, requires that only the domain is compared in
a case-insensitive manner. */
static int equal_email(const unsigned char *a, size_t a_len,
const unsigned char *b, size_t b_len)
{
if (a_len != b_len)
return 0;
size_t i = a_len;
/* We search backwards for the '@' character, so that we do
not have to deal with quoted local-parts. The domain part
is compared in a case-insensitive manner. */
while (i > 0)
{
--i;
if (a[i] == '@' || b[i] == '@')
{
if (!equal_nocase(a + i, a_len - i,
b + i, a_len - i))
return 0;
break;
}
}
if (i == 0)
i = a_len;
return equal_case(a, i, b, i);
}
/* Compare the prefix and suffix with the subject, and check that the
characters in-between are valid. */
static int wildcard_match(const unsigned char *prefix, size_t prefix_len,
const unsigned char *suffix, size_t suffix_len,
const unsigned char *subject, size_t subject_len)
{
const unsigned char *wildcard_start;
const unsigned char *wildcard_end;
const unsigned char *p;
if (subject_len < prefix_len + suffix_len)
return 0;
if (!equal_nocase(prefix, prefix_len, subject, prefix_len))
return 0;
wildcard_start = subject + prefix_len;
wildcard_end = subject + (subject_len - suffix_len);
if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len))
return 0;
/* The wildcard must match at least one character. */
if (wildcard_start == wildcard_end)
return 0;
/* Check that the part matched by the wildcard contains only
permitted characters and only matches a single label. */
for (p = wildcard_start; p != wildcard_end; ++p)
if (!(('0' <= *p && *p <= '9') ||
('A' <= *p && *p <= 'Z') ||
('a' <= *p && *p <= 'z') ||
*p == '-'))
return 0;
return 1;
}
/* Checks if the memory region consistens of [0-9A-Za-z.-]. */
static int valid_domain_characters(const unsigned char *p, size_t len)
{
while (len)
{
if (!(('0' <= *p && *p <= '9') ||
('A' <= *p && *p <= 'Z') ||
('a' <= *p && *p <= 'z') ||
*p == '-' || *p == '.'))
return 0;
++p;
--len;
}
return 1;
}
/* Find the '*' in a wildcard pattern. If no such character is found
or the pattern is otherwise invalid, returns NULL. */
static const unsigned char *wildcard_find_star(const unsigned char *pattern,
size_t pattern_len)
{
const unsigned char *star = memchr(pattern, '*', pattern_len);
size_t dot_count = 0;
const unsigned char *suffix_start;
size_t suffix_length;
if (star == NULL)
return NULL;
suffix_start = star + 1;
suffix_length = (pattern + pattern_len) - (star + 1);
if (!(valid_domain_characters(pattern, star - pattern) &&
valid_domain_characters(suffix_start, suffix_length)))
return NULL;
/* Check that the suffix matches at least two labels. */
while (suffix_length)
{
if (*suffix_start == '.')
++dot_count;
++suffix_start;
--suffix_length;
}
if (dot_count < 2)
return NULL;
return star;
}
/* Compare using wildcards. */
static int equal_wildcard(const unsigned char *pattern, size_t pattern_len,
const unsigned char *subject, size_t subject_len)
{
const unsigned char *star;
/* Do not match IDNA names. */
if (subject_len >=4 && memcmp(subject, "xn--", 4) == 0)
star = NULL;
else
star = wildcard_find_star(pattern, pattern_len);
if (star == NULL)
return equal_nocase(pattern, pattern_len,
subject, subject_len);
return wildcard_match(pattern, star - pattern,
star + 1, (pattern + pattern_len) - star - 1,
subject, subject_len);
}
/* Compare an ASN1_STRING to a supplied string. If they match
* return 1. If cmp_type > 0 only compare if string matches the
* type, otherwise convert it to UTF8.
*/
static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
const unsigned char *b, size_t blen)
{
if (!a->data || !a->length)
return 0;
if (cmp_type > 0)
{
if (cmp_type != a->type)
return 0;
if (cmp_type == V_ASN1_IA5STRING)
return equal(a->data, a->length, b, blen);
if (a->length == (int)blen && !memcmp(a->data, b, blen))
return 1;
else
return 0;
}
else
{
int astrlen, rv;
unsigned char *astr;
astrlen = ASN1_STRING_to_UTF8(&astr, a);
if (astrlen < 0)
return 0;
rv = equal(astr, astrlen, b, blen);
OPENSSL_free(astr);
return rv;
}
}
static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags, int check_type)
{
GENERAL_NAMES *gens = NULL;
X509_NAME *name = NULL;
int i;
int cnid;
int alt_type;
equal_fn equal;
if (check_type == GEN_EMAIL)
{
cnid = NID_pkcs9_emailAddress;
alt_type = V_ASN1_IA5STRING;
equal = equal_email;
}
else if (check_type == GEN_DNS)
{
cnid = NID_commonName;
alt_type = V_ASN1_IA5STRING;
if (flags & X509_CHECK_FLAG_NO_WILDCARDS)
equal = equal_nocase;
else
equal = equal_wildcard;
}
else
{
cnid = 0;
alt_type = V_ASN1_OCTET_STRING;
equal = equal_case;
}
if (chklen == 0)
chklen = strlen((const char *)chk);
gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
if (gens)
{
int rv = 0;
for (i = 0; i < sk_GENERAL_NAME_num(gens); i++)
{
GENERAL_NAME *gen;
ASN1_STRING *cstr;
gen = sk_GENERAL_NAME_value(gens, i);
if(gen->type != check_type)
continue;
if (check_type == GEN_EMAIL)
cstr = gen->d.rfc822Name;
else if (check_type == GEN_DNS)
cstr = gen->d.dNSName;
else
cstr = gen->d.iPAddress;
if (do_check_string(cstr, alt_type, equal, chk, chklen))
{
rv = 1;
break;
}
}
GENERAL_NAMES_free(gens);
if (rv)
return 1;
if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid)
return 0;
}
i = -1;
name = X509_get_subject_name(x);
while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0)
{
X509_NAME_ENTRY *ne;
ASN1_STRING *str;
ne = X509_NAME_get_entry(name, i);
str = X509_NAME_ENTRY_get_data(ne);
if (do_check_string(str, -1, equal, chk, chklen))
return 1;
}
return 0;
}
int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen,
unsigned int flags)
{
return do_x509_check(x, chk, chklen, flags, GEN_DNS);
}
#endif

Some files were not shown because too many files have changed in this diff Show more