From: Tony Mack
+
+
+ Verification examines a chain of certificates to ensure that each parent
+ signs the child, and that some certificate in the chain is signed by a
+ trusted certificate. Verification is a basic recursion:
+
+ Geniwrapper uses a PLCAPI connection to perform operations on the registry,
+ such as creating and deleting slices. This connection requires an account on
+ the PLC server with full administrator access. The Url parameter controls
+ whether the connection uses PLCAPI directly (i.e. Geniwrapper is located on
+ the same machine as PLC), or uses a XMLRPC connection to the PLC machine. If
+ you wish to use the API directly, then remove the Url field from the
+ dictionary.
+ Geniwrapper uses two crypto libraries: pyOpenSSL and M2Crypto to implement
+the necessary crypto functionality. Ideally just one of these libraries
+would be used, but unfortunately each of these libraries is independently
+lacking. The pyOpenSSL library is missing many necessary functions, and
+the M2Crypto library has crashed inside of some of the functions. The
+design decision is to use pyOpenSSL whenever possible as it seems more
+stable, and only use M2Crypto for those functions that are not possible
+in pyOpenSSL.
+
+This module exports two classes: Keypair and Certificate. The certificate class implements a general purpose X509 certificate, making
+use of the appropriate pyOpenSSL or M2Crypto abstractions. For more information about this class, see The Certificate Class. Public-private key pairs are implemented by the Keypair class. For more information about this class, see The Keypair Class. The certificate class implements a general purpose X509 certificate, making
+use of the appropriate pyOpenSSL or M2Crypto abstractions. It also adds
+several addition features, such as the ability to maintain a chain of
+parent certificates, and storage of application-specific data.
+
+Certificates include the ability to maintain a chain of parents. Each
+certificate includes a pointer to it's parent certificate. When loaded
+from a file or a string, the parent chain will be automatically loaded.
+When saving a certificate to a file or a string, the caller can choose
+whether to save the parent certificates as well. Create a certificate object. Add an X509 extension to the certificate. Add_extension can only be called
+once for a particular extension name, due to limitations in the underlying
+library. Create a blank X509 certificate and store it in this object. Return the data string that was previously set with set_data Get an X509 extension from the certificate Get the issuer name Return the certificate object of the parent of this certificate. Get the public key of the certificate.
+It is returned in the form of a Keypair object. Get the subject name of the certificate Return True if pkey is identical to the public key that is contained in the certificate. Given a certificate cert, verify that this certificate was signed by the
+public key contained in cert. Throw an exception otherwise. Load the certificate from a file Given a pyOpenSSL X509 object, store that object inside of this
+certificate object. Load the certificate from a string Save the certificate to a file. Save the certificate to a string. Set_data is a wrapper around add_extension. It stores the parameter str in
+the X509 subject_alt_name extension. Set_data can only be called once, due
+to limitations in the underlying library. Sets the issuer private key and name Set the parent certficiate. Get the public key of the certificate. Set the subject name of the certificate Sign the certificate using the issuer private key and issuer subject previous set with set_issuer(). Verify the authenticity of a certificate. Verification examines a chain of certificates to ensure that each parent
+signs the child, and that some certificate in the chain is signed by a
+trusted certificate.
+
+Verification is a basic recursion:
+
+
+ 1.6 Extending Geniwrapper to other environments
+
+ The PLC Wrapper and Component Wrapper are implemented to work on top of the
+ Planetlab environment, but Geniwrapper was designed specifically to be easy to
+ port to other platforms or environments. The utility classes are generally
+ planetlab-independent, and the planetlab specific code is located in the
+ directories that house the PLC and Component wrappers. The utility classes
+ implement most of the complexity of the Geni wrapper, allowing the PLC and
+ Component wrappers to be relatively lightweight.
+
+ For example, looking at the code for the stop_slice function implemented in
+ the component wrapper:
+
+ def stop_slice(self, cred_str):
+
+ self.decode_authentication(cred_str, "stopslice")
+ slicename =
+ hrn_to_pl_slicename(self.object_gid.get_hrn())
+ print "stopslice:", slicename
+ accounts.get(slicename).stop()
+
+ The hrn_to_pl_slicename() function and accounts() class are planetlab-specific
+ and implement the actual stopping of the slice. The remaining code that
+ implements the Geni protocol, decodes credentials and authentication, etc., is
+ planetlab-independent, and could be easily used in a non-planetlab
+ environment.
+
+
+ issuerKey = Keypair(create=True)
+ issuerSubject = "testissuer"
+
+ cert.set_issuer(issuerKey, issuerSubject)
+
+ cert.sign()
+ if this_certificate was signed by trusted_certs:
+
return
else
return verify_chain(parent, trusted_certs)
+ 2.2 GIDS (gid.py)
+
+
+ 2.4 Rights (rights.py)
+
+ Rights are implemented by two classes:
+
+ Right - represents a single right
+ RightList - represents a list of rights A right may allow several different
+ operations.
+
+ For example, the "info" right allows "listslices", "listcomponentresources",
+ etc.
+
+ 2.5 Records (record.py)
+
+ (gidCaller, gidObject, attributes, rspec, delegate)
+
+ gidCaller = GID of the caller performing the operation
+ gidObject = GID of the slice
+ attributes = slice attributes (keys, vref, instantiation,
+ etc)
+ rspec = resources
+
+
+ 2.7 Hierarchy of Authorities (hierarchy.py)
+
+ This module implements a hierarchy of authorities and performs a similar
+ function as the "tree" module of the original geniwrapper prototype. An HRN
+ is assumed to be a string of authorities separated by dots. For example,
+ "planetlab.us.arizona.bakers". Each component of the HRN is a different
+ authority, with the last component being a leaf in the tree. Each authority
+ is stored in a subdirectory on the registry.
+
+ Inside this subdirectory are several files:
+ *.GID - GID file
+ *.PKEY - private key file
+ *.DBINFO - database info
+
+ The hierarchy class can be used to create GIDs, Credentials, and Tickets for
+ a given authority.
+
+ 2.8 Configuration Information (config.py)
+
+ This module holds configuration parameters for geniwrapper. There are two main
+ pieces of information that are used: the database connection and the PLCAPI
+ connection.
+
+ Geniwrapper uses a MYSQL database to store records. This database may be
+ co-located with the PLC database, or it may be a separate database. The
+ following parameters define the connection to the database. Note that
+ Geniwrapper does not access any of the PLC databases directly via a mysql
+ connection; All PLC databases are accessed via PLCAPI.
+
+ 2.8.1 Database Configuration
+
+ dbinfo={}
+ dbinfo['dbname'] = 'planetlab4'
+ dbinfo['address'] = 'localhost'
+ dbinfo['port'] = 5432
+ dbinfo['user'] = 'pgsqluser'
+ dbinfo['password'] = '4c77b272-c892-4bdf-a833-dddeeee1a2ed'
+
+ pl_auth = {'Username':
+ 'root@198.0.0.132',
+ 'AuthMethod': 'password',
+ 'AuthString': 'root',
+ "Url":
+ "https://localhost:443/PLCAPI/"
+ }
+
+ The Geni Component Wrapper implements the Geni Slice and Component Management
+ interfaces. It includes functions for redeeming tickets,
+ starting/stopping/resetting/deleting slices, and management such as rebooting
+ the component.
+
+
+ InstantiateSlice is not implemented, as that operation is a combination of
+ GetTicket/RedeemTicket and would therefore span the PLC and Component
+ wrappers.
+
+ In a production environment, all of these steps would be integrated into the
+ DVD boot image for the planetlab node.
+
+ privs: refresh,resolve,info
+ gidCaller:
+ hrn:
+ planetlab.us.pl.account_test
+ uuid:
+ 276262316202422735940395896620385479122
+ gidObject:
+ hrn:
+ planetlab.us.pl.account_test
+ uuid:
+ 276262316202422735940395896620385479122
+ delegate: False
+
+ hrn: planetlab.us.pl.account_test
+ type: user
+ gid:
+ hrn:
+ planetlab.us.pl.account_test
+ uuid:
+ 276262316202422735940395896620385479122
+ pointer: 6
+ geni_info:
+ email :
+ test@test.com
+ pl_info:
+ bio : None
+ first_name : test
+ last_name : account
+ last_updated : 1222497672
+ uuid : None
+ roles : ['user']
+ title : None
+ url : None
+ key_ids : [1]
+ enabled : True
+ slice_ids : [24]
+ phone : None
+ peer_person_id : None
+ role_ids : [30]
+ person_id : 6
+ date_created : 1219083140
+ site_ids : [1]
+ peer_id : None
+ email :
+ test@test.com
+
+ hrn: planetlab.us.pl
+ type: sa
+ gid:
+ hrn: planetlab.us.pl
+ uuid:
+ 294786197975089072547582920862317666209
+ pointer: 1
+ geni_info:
+ pi :
+ ['planetlab.us.pl.Administrator_Default']
+ pl_info:
+ last_updated : 1224136003
+ node_ids : [1]
+ site_id : 1
+ pcu_ids : []
+ max_slices : 100
+ ext_consortium_id : None
+ peer_site_id : None
+ abbreviated_name : plctest
+ uuid :
+ 230749975723590978208303655640765327534
+ person_ids : [2, 4, 6]
+ slice_ids : [24, 1, 2]
+ latitude : None
+ peer_id : None
+ max_slivers : 1000
+ is_public : False
+ address_ids : []
+ name : plctest Central
+ url :
+ http://198.0.0.132/
+ enabled : True
+ longitude : None
+ login_base : pl
+ date_created : 1209428329
+ RESULT:
+ RECORD planetlab.us.pl
+ hrn: planetlab.us.pl
+ type: ma
+ gid:
+ hrn: planetlab.us.pl
+ uuid:
+ 294786197975089072547582920862317666209
+ pointer: 1
+ geni_info:
+ operator : []
+ owner :
+ ['planetlab.us.pl.Administrator_Default']
+ pl_info:
+ last_updated : 1224136003
+ node_ids : [1]
+ site_id : 1
+ pcu_ids : []
+ max_slices : 100
+ ext_consortium_id : None
+ peer_site_id : None
+ abbreviated_name : plctest
+ uuid :
+ 230749975723590978208303655640765327534
+ person_ids : [2, 4, 6]
+ slice_ids : [24, 1, 2]
+ latitude : None
+ peer_id : None
+ max_slivers : 1000
+ is_public : False
+ address_ids : []
+ name : plctest Central
+ url :
+ http://198.0.0.132/
+ enabled : True
+ longitude : None
+ login_base : pl
+ date_created : 1209428329
+
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..463dbaf6
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,11 @@
+### this seems like totally out-of-date
+doc:
+ pythondoc.py ../sfa/util/certificate.py ../sfa/util/credential.py ../sfa/util/gid.py \
+ ../sfa/util/rights.py ../sfa/util/config.py ../sfa/trust/hierarchy.py \
+ ../sfa/util/record.py ../sfa/util/client.py \
+ ../sfa/util/server.py
+
+ pythondoc.py ../sfa/registry/registry.py ../sfa/registry/import.py \
+ ../sfa/registry/nuke.py
+
+ pythondoc.py ../component/component.py
diff --git a/docs/README b/docs/README
new file mode 100644
index 00000000..3dad1aaf
--- /dev/null
+++ b/docs/README
@@ -0,0 +1,5 @@
+This directory contains API documentation. See the developer's wiki for a
+general design overview.
+
+API documentation is generated by using the pytondoc tool.
+See http://effbot.org/zone/pythondoc.htm to obtain the tool.
diff --git a/docs/geniwrapper.doc b/docs/geniwrapper.doc
new file mode 100644
index 00000000..e42b9af8
Binary files /dev/null and b/docs/geniwrapper.doc differ
diff --git a/docs/geniwrapper.pdf b/docs/geniwrapper.pdf
new file mode 100644
index 00000000..e0cca428
Binary files /dev/null and b/docs/geniwrapper.pdf differ
diff --git a/docs/pythondoc-cert.html b/docs/pythondoc-cert.html
new file mode 100644
index 00000000..efff287f
--- /dev/null
+++ b/docs/pythondoc-cert.html
@@ -0,0 +1,307 @@
+
+
+
+
+The cert Module
+
+
+The Certificate Class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if this_certificate was signed by trusted_certs:
+ return
+ else
+ return verify_chain(parent, trusted_certs)
+
+
+At each recursion, the parent is tested to ensure that it did sign the
+child. If a parent did not sign a child, then an exception is thrown. If
+the bottom of the recursion is reached and the certificate does not match
+a trusted root, then an exception is thrown.
+
+
Public-private key pairs are implemented by the Keypair class. +A Keypair object may represent both a public and private key pair, or it +may represent only a public key (this usage is consistent with OpenSSL).
+Creates a Keypair object
+Return the private key in PEM format.
+Create a RSA public/private key pair and store it inside the keypair object
+Return an OpenSSL pkey object
+Given another Keypair object, return TRUE if the two keys are the same.
+Load the private key from a file. Implicity the private key includes the public key.
+Load the private key from a string. Implicitly the private key includes the public key.
+Load the public key from a string. No private key is loaded.
+Load the public key from a string. No private key is loaded.
+Save the private key to a file
+Geni Component Wrapper + +This wrapper implements the Geni Component Interface
+ComponentManager is a GeniServer that serves component interface requests.
+For more information about this class, see The ComponentManager Class.
+ComponentManager is a GeniServer that serves component interface requests.
+Examine the ticket that was provided by the caller, check that it is +signed and verified correctly. Throw an exception if something is +wrong with the ticket. + +This is similar to geniserver.decode_authentication
+Delete a slice.
+Convert a geni ticket into a PLC record that can be submitted to the +node manager. This involves some minor reformatting of the fields +contained in the ticket.
+List the slices on a component.
+Reboot the component.
+Redeem a ticket. + +The ticket is submitted to the node manager, and the slice is instantiated +or updated as appropriate. + +TODO: This operation should return a sliver credential and indicate +whether or not the component will accept only sliver credentials, or +will accept both sliver and slice credentials.
+Register the server RPCs for the component
+Reset a slice.
+Start a slice.
+Stop a slice.
+Geniwrapper Configuration Info + +This module holds configuration parameters for geniwrapper. There are two +main pieces of information that are used: the database connection and +the PLCAPI connection
+Geniwrapper uses a MYSQL database to store records. This database may be +co-located with the PLC database, or it may be a separate database. The +following parameters define the connection to the database. + +Note that Geniwrapper does not access any of the PLC databases directly via +a mysql connection; All PLC databases are accessed via PLCAPI.
+Geniwrapper uses a PLCAPI connection to perform operations on the registry, +such as creating and deleting slices. This connection requires an account +on the PLC server with full administrator access. + +The Url parameter controls whether the connection uses PLCAPI directly (i.e. +Geniwrapper is located on the same machine as PLC), or uses a XMLRPC connection +to the PLC machine. If you wish to use the API directly, then remove the Url +field from the dictionary.
+Implements Geni Credentials + +Credentials are layered on top of certificates, and are essentially a +certificate that stores a tuple of parameters.
+Credential is a tuple: + (GIDCaller, GIDObject, LifeTime, Privileges, Delegate) + +These fields are encoded using xmlrpc into the subjectAltName field of the +x509 certificate.
+For more information about this class, see The Credential Class.
+Credential is a tuple: + (GIDCaller, GIDObject, LifeTime, Privileges, Delegate) + +These fields are encoded using xmlrpc into the subjectAltName field of the +x509 certificate. Note: Call encode() once the fields have been filled in +to perform this encoding.
+Create a Credential object
+determine whether the credential allows a particular operation to be +performed
+Retrieve the attributes of the credential from the alt-subject-name field +of the X509 certificate. This is automatically done by the various +get_* methods of this class and should not need to be called explicitly.
+Dump the contents of a credential to stdout in human-readable format
+Encode the attributes of the credential into a string and store that +string in the alt-subject-name field of the X509 object. This should be +done immediately before signing the credential.
+get the delegate bit
+get the GID of the object
+get the GID of the object
+get the lifetime of the credential
+return the privileges as a RightList object
+set the delegate bit
+set the GID of the caller
+set the GID of the object
+set the lifetime of this credential
+set the privileges
+Verify that a chain of credentials is valid (see cert.py:verify). In +addition to the checks for ordinary certificates, verification also +ensures that the delegate bit was set by each parent in the chain. If +a delegate bit was not set, then an exception is thrown. + +Each credential must be a subset of the rights of the parent.
+This module implements the client-side of the Geni API. Stubs are provided +that convert the supplied parameters to the necessary format and send them +via XMLRPC to a Geni Server. + +TODO: Investigate ways to combine this with existing PLC API?
+The GeniClient class provides stubs for executing Geni operations.
+For more information about this class, see The GeniClient Class.
+For more information about this class, see The GeniTransport Class.
+ServerException, ExceptionUnmarshaller + +Used to convert server exception strings back to an exception.
+For more information about this class, see The ServerException Class.
+The GeniClient class provides stubs for executing Geni operations. A given +client object connects to one server. To connect to multiple servers, create +multiple GeniClient objects. + +The Geni protocol uses an HTTPS connection, and the client's side of the +connection uses his private key. Generally, this private key must match the +public key that is containing in the GID that the client is providing for +those functions that take a GID.
+Create a new GID. For MAs and SAs that are physically located on the +registry, this allows a owner/operator/PI to create a new GID and have it +signed by his respective authority.
+Delete a slice.
+Retrieve a credential for an object. + +If cred==None, then the behavior reverts to get_self_credential()
+Retrieve the GID for an object. This function looks up a record in the +registry and returns the GID of the record if it exists. +TODO: Is this function needed? It's a shortcut for Resolve()
+Get_self_credential a degenerate version of get_credential used by a +client to get his initial credential when he doesn't have one. This is +the same as get_credential(..., cred=None,...). + +The registry ensures that the client is the principal that is named by +(type, name) by comparing the public key in the record's GID to the +private key used to encrypt the client-side of the HTTPS connection. Thus +it is impossible for one principal to retrieve another principal's +credential without having the appropriate private key.
+Retrieve a ticket. This operation is currently implemented on the +registry (see SFA, engineering decisions), and is not implemented on +components. + +The ticket is filled in with information from the PLC database. This +information includes resources, and attributes such as user keys and +initscripts.
+List the records in an authority. The objectGID in the supplied credential +should name the authority that will be listed.
+List the slices on a component.
+Redeem a ticket. This operation is currently implemented on the +component. + +The ticket is submitted to the node manager, and the slice is instantiated +or updated as appropriate. + +TODO: This operation should return a sliver credential and indicate +whether or not the component will accept only sliver credentials, or +will accept both sliver and slice credentials.
+Register an object with the registry. In addition to being stored in the +Geni database, the appropriate records will also be created in the +PLC databases. + +The geni_info and/or pl_info fields must in the record must be filled +out correctly depending on the type of record that is being registered. + +TODO: The geni_info member of the record should be parsed and the pl_info +adjusted as necessary (add/remove users from a slice, etc)
+Remove an object from the registry. If the object represents a PLC object, +then the PLC records will also be removed.
+Reset a slice.
+Resolve an object in the registry. A given HRN may have multiple records +associated with it, and therefore multiple records may be returned. The +caller should check the type fields of the records to find the one that +he is interested in.
+Start a slice.
+Stop a slice.
+Update an object in the registry. Currently, this only updates the +PLC information associated with the record. The Geni fields (name, type, +GID) are fixed. + +The record is expected to have the pl_info field filled in with the data +that should be updated. + +TODO: The geni_info member of the record should be parsed and the pl_info +adjusted as necessary (add/remove users from a slice, etc)
+GeniTransport + +A transport for XMLRPC that works on top of HTTPS
+ServerException, ExceptionUnmarshaller + +Used to convert server exception strings back to an exception. + from usenet, Raghuram Devarakonda
+This module implements a general-purpose server layer for geni. +The same basic server should be usable on the registry, component, or +other interfaces. + +TODO: investigate ways to combine this with existing PLC server?
+Implements an HTTPS XML-RPC server.
+For more information about this class, see The GeniServer Class.
+taken from the web (XXX find reference).
+For more information about this class, see The SecureXMLRpcRequestHandler Class.
+Taken from the web (XXX find reference).
+For more information about this class, see The SecureXMLRPCServer Class.
+Verification callback for pyOpenSSL. We do our own checking of keys because +we have our own authentication spec. Thus we disable several of the normal +prohibitions that OpenSSL places on certificates
+Implements an HTTPS XML-RPC server. Generally it is expected that GENI +functions will take a credential string, which is passed to +decode_authentication. Decode_authentication() will verify the validity of +the credential, and verify that the user is using the key that matches the +GID supplied in the credential.
+Decode the credential string that was submitted by the caller. Several +checks are performed to ensure that the credential is valid, and that the +callerGID included in the credential matches the caller that is +connected to the HTTPS connection.
+Sample no-op server function. The no-op function decodes the credential +that was passed to it.
+Register functions that will be served by the XMLRPC server. This +function should be overrided by each descendant class.
+Execute the server, serving requests forever.
+taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler
+Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
+Implements GENI GID. GIDs are based on certificates, and the GID class is a +descendant of the certificate class.
+Create a new uuid. Returns the UUID as a string.
+GID is a tuplie: + (uuid, hrn, public_key) + +UUID is a unique identifier and is created by the python uuid module + (or the utility function create_uuid() in gid.py).
+For more information about this class, see The GID Class.
+GID is a tuplie: + (uuid, hrn, public_key) + +UUID is a unique identifier and is created by the python uuid module + (or the utility function create_uuid() in gid.py). + +HRN is a human readable name. It is a dotted form similar to a backward domain + name. For example, planetlab.us.arizona.bakers. + +PUBLIC_KEY is the public key of the principal identified by the UUID/HRN. +It is a Keypair object as defined in the cert.py module. + +It is expected that there is a one-to-one pairing between UUIDs and HRN, +but it is uncertain how this would be inforced or if it needs to be enforced. + +These fields are encoded using xmlrpc into the subjectAltName field of the +x509 certificate. Note: Call encode() once the fields have been filled in +to perform this encoding.
+Create a new GID object
+Decode the subject-alt-name field of the X509 certificate into the +fields of the GID. This is automatically called by the various get_*() +functions in this class.
+Dump the credential to stdout.
+Encode the GID fields and package them into the subject-alt-name field +of the X509 certificate. This must be called prior to signing the +certificate. It may only be called once per certificate.
+Verify the chain of authenticity of the GID. First perform the checks +of the certificate class (verifying that each parent signs the child, +etc). In addition, GIDs also confirm that the parent's HRN is a prefix +of the child's HRN. + +Verifying these prefixes prevents a rogue authority from signing a GID +for a principal that is not a member of that authority. For example, +planetlab.us.arizona cannot sign a GID for planetlab.us.princeton.foo.
+This module implements a hierarchy of authorities and performs a similar +function as the "tree" module of the original geniwrapper prototype. An HRN +is assumed to be a string of authorities separated by dots. For example, +"planetlab.us.arizona.bakers". Each component of the HRN is a different +authority, with the last component being a leaf in the tree. + +Each authority is stored in a subdirectory on the registry. Inside this +subdirectory are several files: + *.GID - GID file + *.PKEY - private key file + *.DBINFO - database info
+The AuthInfo class contains the information for an authority.
+For more information about this class, see The AuthInfo Class.
+The Hierarchy class is responsible for managing the tree of authorities.
+For more information about this class, see The Hierarchy Class.
+The AuthInfo class contains the information for an authority. This information +includes the GID, private key, and database connection information.
+Initialize and authority object.
+Get the dbinfo in the form of a dictionary
+Get the GID in the form of a GID object
+Get the private key in the form of a Keypair object
+Set the filename of the GID
+Replace the GID with a new one. The file specified by gid_filename is +overwritten with the new GID object
+The Hierarchy class is responsible for managing the tree of authorities. +Each authority is a node in the tree and exists as an AuthInfo object. + +The tree is stored on disk in a hierarchical manner than reflects the +structure of the tree. Each authority is a subdirectory, and each subdirectory +contains the GID, pkey, and dbinfo files for that authority (as well as +subdirectories for each sub-authority)
+Check to see if an authority exists. An authority exists if it's disk +files exist.
+Create an authority. A private key for the authority and the associated +GID are created and signed by the parent authority.
+Create a new GID. The GID will be signed by the authority that is it's +immediate parent in the hierarchy (and recursively, the parents' GID +will be signed by its parent)
+Retrieve an authority credential for an authority. The authority +credential will contain the authority privilege and will be signed by +the authority's parent.
+Given a hrn, return the filenames of the GID, private key, and dbinfo +files.
+Return the AuthInfo object for the specified authority. If the authority +does not exist, then an exception is thrown. As a side effect, disk files +and a subdirectory may be created to store the authority.
+Retrieve an authority ticket. An authority ticket is not actually a +redeemable ticket, but only serves the purpose of being included as the +parent of another ticket, in order to provide a chain of authentication +for a ticket. + +This looks almost the same as get_auth_cred, but works for tickets +XXX does similarity imply there should be more code re-use?
+Refresh a GID. The primary use of this function is to refresh the +the expiration time of the GID. It may also be used to change the HRN, +UUID, or Public key of the GID.
+Import PLC records into the Geni database. It is indended that this tool be +run once to create Geni records that reflect the current state of the +planetlab database. + +The import tool assumes that the existing PLC hierarchy should all be part +of "planetlab.us" (see the root_auth and level1_auth variables below). + +Public keys are extracted from the users' SSH keys automatically and used to +create GIDs. This is relatively experimental as a custom tool had to be +written to perform conversion from SSH to OpenSSL format. It only supports +RSA keys at this time, not DSA keys.
+Two authorities are specified: the root authority and the level1 authority.
+Implements support for geni records + +TODO: Use existing PLC database methods? or keep this separate?
+The GeniRecord class implements a Geni Record.
+For more information about this class, see The GeniRecord Class.
+The GeniRecord class implements a Geni Record. A GeniRecord is a tuple +(Name, GID, Type, Info). + +Name specifies the HRN of the object +GID is the GID of the object +Type is user | sa | ma | slice | component + +Info is comprised of the following sub-fields + pointer = a pointer to the record in the PL database + pl_info = planetlab-specific info (when talking to client) + geni_info = geni-specific info (when talking to client) + +The pointer is interpreted depending on the type of the record. For example, +if the type=="user", then pointer is assumed to be a person_id that indexes +into the persons table. + +A given HRN may have more than one record, provided that the records are +of different types. For example, planetlab.us.arizona may have both an SA +and a MA record, but cannot have two SA records.
+Return the record in the form of a dictionary
+Dump the record to stdout
+Returns a list of field names in this record. pl_info, geni_info are not +included because they are not part of the record that is stored in the +database, but are rather computed values from other entities
+Given a field name ("name", "gid", ...) return the value of that field.
+Given a list of field names, return a list of values for those fields.
+Return the geni_info of the record, or an empty dictionary if none exists
+Return the GID of the record, in the form of a GID object +TODO: not the best name for the function, because we have things called +gidObjects in the Cred
+Return a key that uniquely identifies this record among all records in +Geni. This key is used to uniquely identify the record in the Geni +database.
+Return the name (HRN) of the record
+Return the pl_info of the record, or an empty dictionary if none exists
+Return the pointer of the record. The pointer is an integer that may be +used to look up the record in the PLC database. The evaluation of pointer +depends on the type of the record
+Return the type of the record
+Set the geni info the record
+Set the GID of the record
+Set the name of the record
+Set the PLC info of the record
+Set the pointer of the record
+Set the type of the record
+Geni Registry Wrapper + +This wrapper implements the Geni Registry. + +There are several items that need to be done before starting the registry. + +1) Update util/config.py to match the parameters of your PLC installation. + +2) Import the existing planetlab database, creating the +appropriate geni records. This is done by running the "import.py" tool. + +3) Create a "trusted_roots" directory and place the certificate of the root +authority in that directory. Given the defaults in import.py, this certificate +would be named "planetlab.gid". For example, + + mkdir trusted_roots; cp authorities/planetlab.gid trusted_roots/
+Convert geni fields to PLC fields for use when registering up updating +registry record in the PLC database
+Registry is a GeniServer that serves registry requests.
+For more information about this class, see The Registry Class.
+Registry is a GeniServer that serves registry requests. It also serves +component and slice operations that are implemented on the registry +due to SFA engineering decisions
+Connect to a local shell via local API functions
+Connect to a remote shell via XMLRPC
+GENI_API: Create_gid + +Create a new GID. For MAs and SAs that are physically located on the +registry, this allows a owner/operator/PI to create a new GID and have it +signed by his respective authority.
+Determine tje rights that an object should have. The rights are entirely +dependent on the type of the object. For example, users automatically +get "refresh", "resolve", and "info".
+Fill in the geni-specific fields of the record. + +Note: It is assumed the fill_record_pl_info() has already been performed +on the record.
+Given a Geni record, fill in the PLC-specific and Geni-specific fields +in the record.
+Fill in the planetlab-specific fields of a Geni record. This involves +calling the appropriate PLC methods to retrieve the database record for +the object. + +PLC data is filled into the pl_info field of the record.
+Given an authority name, return the information for that authority. This +is basically a stub that calls the hierarchy module.
+Given an authority name, return the database table for that authority. If +the database table does not exist, then one will be automatically +created.
+GENI API: Get_credential + +Retrieve a credential for an object. + +If cred==None, then the behavior reverts to get_self_credential()
+GENI API: get_gid + +Retrieve the GID for an object. This function looks up a record in the +registry and returns the GID of the record if it exists. +TODO: Is this function needed? It's a shortcut for Resolve()
+GENI API: Get_self_credential + +Get_self_credential a degenerate version of get_credential used by a +client to get his initial credential when he doesn't have one. This is +the same as get_credential(..., cred=None,...). + +The registry ensures that the client is the principal that is named by +(type, name) by comparing the public key in the record's GID to the +private key used to encrypt the client-side of the HTTPS connection. Thus +it is impossible for one principal to retrieve another principal's +credential without having the appropriate private key.
+GENI API: get_ticket + +Retrieve a ticket. This operation is currently implemented on the +registry (see SFA, engineering decisions), and is not implemented on +components. + +The ticket is filled in with information from the PLC database. This +information includes resources, and attributes such as user keys and +initscripts.
+List the records in an authority. The objectGID in the supplied credential +should name the authority that will be listed. + +TODO: List doesn't take an hrn and uses the hrn contained in the + objectGid of the credential. Does this mean the only way to list an + authority is by having a credential for that authority?
+Look up user records given PLC user-ids. This is used as part of the +process for reverse-mapping PLC records into Geni records.
+Convert a PLC record into the slice information that will be stored in +a ticket. There are two parts to this information: attributes and +rspec. + +Attributes are non-resource items, such as keys and the initscript +RSpec is a set of resource specifications
+GENI API: register + +Register an object with the registry. In addition to being stored in the +Geni database, the appropriate records will also be created in the +PLC databases
+Register the server RPCs for the registry
+GENI API: remove + +Remove an object from the registry. If the object represents a PLC object, +then the PLC records will also be removed.
+GENI API: Resolve + +This is a wrapper around resolve_raw that converts records objects into +dictionaries before returning them to the user.
+Resolve a record. This is an internal version of the Resolve API call +and returns records in record object format rather than dictionaries +that may be sent over XMLRPC.
+GENI API: Register + +Update an object in the registry. Currently, this only updates the +PLC information associated with the record. The Geni fields (name, type, +GID) are fixed. + +The record is expected to have the pl_info field filled in with the data +that should be updated. + +TODO: The geni_info member of the record should be parsed and the pl_info +adjusted as necessary (add/remove users from a slice, etc)
+Verify that an authority belongs to this registry. This is basically left +up to the implementation of the hierarchy module. If the specified name +does not belong to this registry, an exception is thrown indicating the +caller should contact someone else.
+Verify that an object belongs to this registry. By extension, this implies +that the authority that owns the object belongs to this registry. If the +object does not belong to this registry, then an exception is thrown.
+Verify that the object_gid that was specified in the credential allows +permission to the object 'name'. This is done by a simple prefix test. +For example, an object_gid for planetlab.us.arizona would match the +objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
+This Module implements rights and lists of rights for the Geni wrapper. Rights +are implemented by two classes: + +Right - represents a single right + +RightList - represents a list of rights + +A right may allow several different operations. For example, the "info" right +allows "listslices", "listcomponentresources", etc.
+privilege_table is a list of priviliges and what operations are allowed +per privilege.
+The Right class represents a single privilege.
+For more information about this class, see The Right Class.
+A RightList object represents a list of privileges.
+For more information about this class, see The RightList Class.
+The Right class represents a single privilege.
+Test to see if this right object is allowed to perform an operation. +Returns True if the operation is allowed, False otherwise.
+Test to see if this right is a superset of a child right. A right is a +superset if every operating that is allowed by the child is also allowed +by this object.
+A RightList object represents a list of privileges.
+Add a right to this list
+Check to see if some right in this list allows an operation. This is +done by evaluating the can_perform function of each operation in the +list.
+Check to see if all of the rights in this rightlist are a superset +of all the rights in a child rightlist. A rightlist is a superset +if there is no operation in the child rightlist that cannot be +performed in the parent rightlist.
+Load the rightlist object from a string
+Save the rightlist object to a string. It is saved in the format of a +comma-separated list.
+Please use XML-RPC or SOAP to access the SFA API.
+ +""") + return apache.OK + + # Read request + request = req.read(int(req.headers_in['content-length'])) + + # mod_python < 3.2: The IP address portion of remote_addr is + # incorrect (always 0.0.0.0) when IPv6 is enabled. + # http://issues.apache.org/jira/browse/MODPYTHON-64?page=all + (remote_ip, remote_port) = req.connection.remote_addr + remote_addr = (req.connection.remote_ip, remote_port) + + # Handle request + response = api.handle(remote_addr, request) + + # Write response + req.content_type = "text/xml; charset=" + api.encoding + req.send_http_header() + req.write(response) + + return apache.OK + + except Exception, err: + # Log error in /var/log/httpd/(ssl_)?error_log + print >> log, err, traceback.format_exc() + return apache.HTTP_INTERNAL_SERVER_ERROR diff --git a/sfa/server/modpython/SfaRegistryModPython.py b/sfa/server/modpython/SfaRegistryModPython.py new file mode 100755 index 00000000..a9044ebb --- /dev/null +++ b/sfa/server/modpython/SfaRegistryModPython.py @@ -0,0 +1,69 @@ +# +# Apache mod_python interface +# +# Aaron KlingamanPlease use XML-RPC or SOAP to access the SFA API.
+ +""") + return apache.OK + + # Read request + request = req.read(int(req.headers_in['content-length'])) + + # mod_python < 3.2: The IP address portion of remote_addr is + # incorrect (always 0.0.0.0) when IPv6 is enabled. + # http://issues.apache.org/jira/browse/MODPYTHON-64?page=all + (remote_ip, remote_port) = req.connection.remote_addr + remote_addr = (req.connection.remote_ip, remote_port) + + # Handle request + response = api.handle(remote_addr, request) + + # Write response + req.content_type = "text/xml; charset=" + api.encoding + req.send_http_header() + req.write(response) + + return apache.OK + + except Exception, err: + # Log error in /var/log/httpd/(ssl_)?error_log + print >> log, err, traceback.format_exc() + return apache.HTTP_INTERNAL_SERVER_ERROR diff --git a/sfa/server/modpython/SfaSliceMgrModPython.py b/sfa/server/modpython/SfaSliceMgrModPython.py new file mode 100755 index 00000000..7ad8e0d4 --- /dev/null +++ b/sfa/server/modpython/SfaSliceMgrModPython.py @@ -0,0 +1,70 @@ +# +# Apache mod_python interface +# +# Aaron KlingamanPlease use XML-RPC or SOAP to access the SFA API.
+ +""") + return apache.OK + + # Read request + request = req.read(int(req.headers_in['content-length'])) + + # mod_python < 3.2: The IP address portion of remote_addr is + # incorrect (always 0.0.0.0) when IPv6 is enabled. + # http://issues.apache.org/jira/browse/MODPYTHON-64?page=all + (remote_ip, remote_port) = req.connection.remote_addr + remote_addr = (req.connection.remote_ip, remote_port) + + # Handle request + response = api.handle(remote_addr, request) + + # Write response + req.content_type = "text/xml; charset=" + api.encoding + req.send_http_header() + req.write(response) + + return apache.OK + + except Exception, err: + # Log error in /var/log/httpd/(ssl_)?error_log + print >> log, err, traceback.format_exc() + return apache.HTTP_INTERNAL_SERVER_ERROR diff --git a/sfa/server/modpython/sfa.aggregate.httpd.conf b/sfa/server/modpython/sfa.aggregate.httpd.conf new file mode 100644 index 00000000..2e6f4f44 --- /dev/null +++ b/sfa/server/modpython/sfa.aggregate.httpd.conf @@ -0,0 +1,24 @@ +Listen 12346 + +Please use XML-RPC or SOAP to access the PLCAPI.
+ +""") + return apache.OK + + # Read request + request = req.read(int(req.headers_in['content-length'])) + + # mod_python < 3.2: The IP address portion of remote_addr is + # incorrect (always 0.0.0.0) when IPv6 is enabled. + # http://issues.apache.org/jira/browse/MODPYTHON-64?page=all + (remote_ip, remote_port) = req.connection.remote_addr + remote_addr = (req.connection.remote_ip, remote_port) + + # Handle request + response = api.handle(remote_addr, request) + + # Write response + req.content_type = "text/xml; charset=" + api.encoding + req.send_http_header() + req.write(response) + + return apache.OK + + except Exception, err: + # Log error in /var/log/httpd/(ssl_)?error_log + print >> log, err, traceback.format_exc() + return apache.HTTP_INTERNAL_SERVER_ERROR diff --git a/sfa/server/modpythonapi/TestApi.py b/sfa/server/modpythonapi/TestApi.py new file mode 100755 index 00000000..11daed51 --- /dev/null +++ b/sfa/server/modpythonapi/TestApi.py @@ -0,0 +1,19 @@ +from AuthenticatedApi import AuthenticatedApi, BadRequestHash + +class RemoteApi(AuthenticatedApi): + def __init__(self, encoding="utf-8", trustedRootsDir="/usr/local/testapi/var/trusted_roots"): + return AuthenticatedApi.__init__(self, encoding) + + def get_log_name(self): + return "/usr/local/testapi/var/logfile.txt" + + def register_functions(self): + AuthenticatedApi.register_functions(self) + self.register_function(self.typeError) + self.register_function(self.badRequestHash) + + def typeError(self): + raise TypeError() + + def badRequestHash(self): + raise BadRequestHash("somehashvalue") diff --git a/sfa/server/modpythonapi/installTest.sh b/sfa/server/modpythonapi/installTest.sh new file mode 100755 index 00000000..1a6514eb --- /dev/null +++ b/sfa/server/modpythonapi/installTest.sh @@ -0,0 +1,37 @@ +SFA_SRC_DIR=/home/smbaker/projects/sfa/trunk + +mkdir -p /usr/local/testapi/bin +mkdir -p /usr/local/testapi/bin/sfa/trust +mkdir -p /usr/local/testapi/bin/sfa/util +mkdir -p /usr/local/testapi/var/trusted_roots +mkdir -p /repository/testapi + +# source code for the API +cp BaseApi.py /usr/local/testapi/bin/ +cp AuthenticatedApi.py /usr/local/testapi/bin/ +cp TestApi.py /usr/local/testapi/bin/API.py +cp ModPython.py /usr/local/testapi/bin/ +cp ApiExceptionCodes.py /usr/local/testapi/bin/ + +# trusted root certificates that match gackstestuser.* +cp trusted_roots/*.gid /usr/local/testapi/var/trusted_roots/ + +# apache config file to enable the api +cp testapi.conf /etc/httpd/conf.d/ + +# copy over sfa stuff that we need +echo > /usr/local/testapi/bin/sfa/__init__.py +echo > /usr/local/testapi/bin/sfa/trust/__init__.py +echo > /usr/local/testapi/bin/sfa/util/__init__.py +cp $SFA_SRC_DIR/sfa/trust/gid.py /usr/local/testapi/bin/sfa/trust/ +cp $SFA_SRC_DIR/sfa/trust/certificate.py /usr/local/testapi/bin/sfa/trust/ +cp $SFA_SRC_DIR/sfa/trust/trustedroot.py /usr/local/testapi/bin/sfa/trust/ +cp $SFA_SRC_DIR/sfa/trust/credential.py /usr/local/testapi/bin/sfa/trust/ +cp $SFA_SRC_DIR/sfa/trust/rights.py /usr/local/testapi/bin/sfa/trust/ +cp $SFA_SRC_DIR/sfa/util/faults.py /usr/local/testapi/bin/sfa/util/ + +# make everything owned by apache +chown -R apache /usr/local/testapi +chown apache /etc/httpd/conf.d/testapi.conf + +/etc/init.d/httpd restart diff --git a/sfa/server/modpythonapi/test.py b/sfa/server/modpythonapi/test.py new file mode 100755 index 00000000..d3fafed9 --- /dev/null +++ b/sfa/server/modpythonapi/test.py @@ -0,0 +1,44 @@ +import sys +import traceback + +from BaseClient import BaseClient, EnableVerboseExceptions +from AuthenticatedClient import AuthenticatedClient + +EnableVerboseExceptions(True) + +HOST = "localhost" +URL = "http://" + HOST + "/TESTAPI/" +SURL = "https://" + HOST + "/TESTAPI/" + +print "*** testing some valid ops; these should print \"Hello, World\" ***" + +bc = BaseClient(URL) +print "HTTP noop:", bc.noop("Hello, World") + +ac = AuthenticatedClient(URL, "gackstestuser.pkey", "gackstestuser.gid") +print "HTTP gidNoop:", ac.gidNoop("Hello, World") + +bc = BaseClient(SURL) +print "HTTPS noop:", bc.noop("Hello, World") + +ac = AuthenticatedClient(URL, "gackstestuser.pkey", "gackstestuser.gid") +print "HTTPS gidNoop:", ac.gidNoop("Hello, World") + +print +print "*** testing some exception handling: ***" + +bc = BaseClient(URL) +print "HTTP typeError:", +try: + result = bc.server.typeError() + print result +except Exception, e: + print ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) + +print "HTTP badrequesthash:", +try: + result = bc.server.badRequestHash() + print result +except: + print ''.join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) + diff --git a/sfa/server/modpythonapi/test.sh b/sfa/server/modpythonapi/test.sh new file mode 100755 index 00000000..cafa0fb8 --- /dev/null +++ b/sfa/server/modpythonapi/test.sh @@ -0,0 +1,3 @@ +export PYTHONPATH=/home/smbaker/projects/sfa/trunk + +python ./test.py diff --git a/sfa/server/modpythonapi/testapi.conf b/sfa/server/modpythonapi/testapi.conf new file mode 100644 index 00000000..5495fd24 --- /dev/null +++ b/sfa/server/modpythonapi/testapi.conf @@ -0,0 +1,5 @@ ++ # if this_certificate was signed by trusted_certs: + # return + # else + # return verify_chain(parent, trusted_certs) + #+ # + # At each recursion, the parent is tested to ensure that it did sign the + # child. If a parent did not sign a child, then an exception is thrown. If + # the bottom of the recursion is reached and the certificate does not match + # a trusted root, then an exception is thrown. + # + # @param Trusted_certs is a list of certificates that are trusted. + # + + def verify_chain(self, trusted_certs = None): + # Verify a chain of certificates. Each certificate must be signed by + # the public key contained in it's parent. The chain is recursed + # until a certificate is found that is signed by a trusted root. + # TODO: verify expiration time + #print "====Verify Chain=====" + # if this cert is signed by a trusted_cert, then we are set + for trusted_cert in trusted_certs: + #print "***************" + # TODO: verify expiration of trusted_cert ? + #print "CLIENT CERT", self.dump() + #print "TRUSTED CERT", trusted_cert.dump() + #print "Client is signed by Trusted?", self.is_signed_by_cert(trusted_cert) + if self.is_signed_by_cert(trusted_cert): + logger.debug("Cert %s signed by trusted cert %s", self.get_subject(), trusted_cert.get_subject()) + return trusted_cert + + # if there is no parent, then no way to verify the chain + if not self.parent: + #print self.get_subject(), "has no parent" + raise CertMissingParent(self.get_subject()) + + # if it wasn't signed by the parent... + if not self.is_signed_by_cert(self.parent): + #print self.get_subject(), "is not signed by parent" + return CertNotSignedByParent(self.get_subject()) + + # if the parent isn't verified... + self.parent.verify_chain(trusted_certs) + + return diff --git a/sfa/trust/credential.py b/sfa/trust/credential.py new file mode 100644 index 00000000..e082f756 --- /dev/null +++ b/sfa/trust/credential.py @@ -0,0 +1,851 @@ +#---------------------------------------------------------------------- +# Copyright (c) 2008 Board of Trustees, Princeton University +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and/or hardware specification (the "Work") to +# deal in the Work without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Work, and to permit persons to whom the Work +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Work. +# +# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS +# IN THE WORK. +#---------------------------------------------------------------------- +## +# Implements SFA Credentials +# +# Credentials are signed XML files that assign a subject gid privileges to an object gid +## + +### $Id: credential.py 18654 2010-08-23 18:54:57Z tmack $ +### $URL: http://svn.planet-lab.org/svn/sfa/trunk/sfa/trust/credential.py $ + +import os +import datetime +from xml.dom.minidom import Document, parseString +from tempfile import mkstemp +from sfa.trust.certificate import Keypair +from sfa.trust.credential_legacy import CredentialLegacy +from sfa.trust.rights import * +from sfa.trust.gid import * +from sfa.util.faults import * + +from sfa.util.sfalogging import logger +from dateutil.parser import parse + + + +# Two years, in seconds +DEFAULT_CREDENTIAL_LIFETIME = 60 * 60 * 24 * 365 * 2 + + +# TODO: +# . make privs match between PG and PL +# . Need to add support for other types of credentials, e.g. tickets + + +signature_template = \ +''' +