pax_global_header00006660000000000000000000000064136253722570014526gustar00rootroot0000000000000052 comment=34a78c2feb471f37ec723e1bc1076cbc80ad357a golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/000077500000000000000000000000001362537225700205555ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/.gitignore000066400000000000000000000004401362537225700225430ustar00rootroot00000000000000*.keytab *.cap *.raw # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/.travis.yml000066400000000000000000000043711362537225700226730ustar00rootroot00000000000000language: go go: - 1.7.x - 1.8.x - 1.9.x - 1.10.x - master go_import_path: gopkg.in/jcmturner/gokrb5.v5 sudo: required services: - docker before_install: - sudo apt-get install -yq krb5-user - sudo chmod 666 /etc/krb5.conf - go get github.com/golang/lint/golint - docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -e "TEST_KDC_ADDR=127.0.0.1" -p 53:53 -p 53:53/udp --name dns jcmturner/gokrb5:dns - docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 88:88 -p 88:88/udp -p 464:464 -p 464:464/udp --name krb5kdc jcmturner/gokrb5:kdc-centos-default - docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 78:88 -p 78:88/udp --name krb5kdc-old jcmturner/gokrb5:kdc-older - docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 98:88 -p 98:88/udp --name krb5kdc-latest jcmturner/gokrb5:kdc-latest - docker run -d -h kdc.resdom.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 188:88 -p 188:88/udp --name krb5kdc-resdom jcmturner/gokrb5:kdc-resdom - docker run -d -h kdc.test.gokrb5 -v /etc/localtime:/etc/localtime:ro -p 58:88 -p 58:88/udp --name krb5kdc-shorttickets jcmturner/gokrb5:kdc-shorttickets - docker run -d --add-host host.test.gokrb5:127.0.0.88 -v /etc/localtime:/etc/localtime:ro -p 80:80 -p 443:443 --name gokrb5-http jcmturner/gokrb5:http before_script: - GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/) - sudo sed -i 's/nameserver .*/nameserver 127.0.0.1/g' /etc/resolv.conf env: - TEST_KDC_ADDR=127.0.0.1 TEST_HTTP_URL="http://host.test.gokrb5/index.html" DNSUTILS_OVERRIDE_NS="127.0.0.1:53" DEBIAN_FRONTEND=noninteractive script: - test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g') # Fail if a .go file hasn't been formatted with gofmt - go vet ./... # go vet is the official Go static analyzer #- golint -set_exit_status $(go list ./...) # golint to be added - go test -v -race -tags="integration dns" ./... # Run all the tests with the race detector enabled and integration tests - GOARCH=386 go test -v -tags="integration dns" ./... # 32bit tests - go run -tags="examples" examples/example.go addons: hosts: - host.test.gokrb5 - kdc.test.gokrb5 - kdc.resdom.gokrb5 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/CONTRIBUTING.md000066400000000000000000000117701362537225700230140ustar00rootroot00000000000000# Contribution Guide to gokrb5 If you're reading this then I guess you are interested in contributing to gokrb5 which is brilliant! Thank you for your interest and taking the time to read this guide. The information below should help you successfully contribute and by following these guidelines you are expressing the respect you have for the project and other contributors. In return I will endeavour to respond promptly to issues and pull requests but please have patience as I do this in my spare time. Therefore it can take a few weeks for me to get round to some items. A variety of contribution types are welcome: * Raising bug reports. * Suggesting enhancements. * Updating and adding to documentation. * Fixing bugs. * Coding enhancements. * Extending test coverage. This project is not intended to be a support forum for general Kerberos issues and troubleshooting specific environment configurations. ## Ground Rules Above all, be respectful and considerate of others. This means: * Assume miss-communication is mistake not malice. * Be patient. Many of those contributing are doing this out of good will and in their own time. If something is really important to you, describe why and what the impact is so that others can understand and relate to it. * Provide feedback in a constructive manner. ### Code Contribution Ground Rules When contributing code please adhere to these responsibilities: * Create issues (if one does not already exist) for any changes and enhancements that you wish to make. Discuss how you intend to fix the bug or implement the enhancement to give the community a chance to comment and get us to the best solution. * Do not create any new packages unless absolutely necessary. * Do not alter the existing exported functions and constants unless absolutely necessary. This would require a major (vX.\_.\_) version update. * Only add new exported functions and constants if absolutely necessary. This would require a minor (v\_.X.\_) version update. * Keep your code platform agnostic. * Ensure that any functions added or updated are covered by tests. * Ensure tests pass. * Ensure godoc comments are created or updated as required for any new or updated code. * Ensure your contributions are formatted correctly with gofmt. The travis build will test this. * Do not use external package dependencies. As gokrb5 is designed to be a core library used in other applications it is best to avoid dependencies the project has no control over, other than the Go standard library, as issues with any dependency could have large knock on effects. * Provide useful commit messages. * Pull requests must address one issue only and keep to the scope of the issue. This makes it easier to review and merge, so your contribution will get incorporated faster this way. * Pull requests must have a message obeying this format: ``` More detailed explanatory text. This should reference the related issue. ``` This to adhere to the [git best practice](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project) and mirror the [contribution guidelines for the Go standard libarary](https://golang.org/doc/contribute.html). An Example: ``` update to the godoc comments for the function Blah The godoc comments to function subpkg.Blah have been updated to make it clearer as to what the function is for. ``` ### Issue Raising Ground Rules A good rule of thumb: The easier you make it for the reader of an issue to help the more help you'll get. #### Bugs When raising bugs please include the following items in your issue: * The version of gokrb5 being used (vX.Y.Z or master or branch name). * The version of Go being used (output of the ```go version``` command is handy). * Details of the environment in which you are seeing the issue. For example, what is being used as the KDC, what the krb5.conf contains, etc. * Details on how to re-create the issue. * Details on what you are experiencing that indicates the issue. * What you expected to see. * In which gokrb5 package(s) you think the issue arises from. * If the bug relates to compliance with an RFC please specify the RFC number and section you are referring to. #### Enhancements When raising enhancement requests or suggestions please include the following: * What the enhancment is or would do. * Why you need the enhancement or why you think it would be a good idea. * Any suggestions you may have on how to implement. ## Tips ### Running Tests Running the tests without any particular switches runs only the unit tests. It is recommended to run tests with the ```-race``` argument. There are integration tests that run against various other network services such as KDCs, HTTP web servers, DNS servers, etc. To run these pass ```-tags=integration``` as an argument to the go test command. There are vagrant and docker resources available to spin up these network services. See the [readme](https://github.com/jcmturner/gokrb5/blob/master/testenv/README.md) in the testenv directory for instructions. golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/LICENSE000066400000000000000000000261351362537225700215710ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/README.md000066400000000000000000000322001362537225700220310ustar00rootroot00000000000000# gokrb5 [![GoDoc](https://godoc.org/gopkg.in/jcmturner/gokrb5.v5?status.svg)](https://godoc.org/gopkg.in/jcmturner/gokrb5.v5) [![Go Report Card](https://goreportcard.com/badge/gopkg.in/jcmturner/gokrb5.v5)](https://goreportcard.com/report/gopkg.in/jcmturner/gokrb5.v5) [![Build Status](https://travis-ci.org/jcmturner/gokrb5.svg?branch=master)](https://travis-ci.org/jcmturner/gokrb5) To get the package, execute: ``` go get -d gopkg.in/jcmturner/gokrb5.v5/... ``` To import this package, add the following line to your code: ```go import "gopkg.in/jcmturner/gokrb5.v5/" ``` ## Features * Pure Go - no dependency on external libraries * No platform specific code * Server Side * HTTP handler wrapper implements SPNEGO Kerberos authentication * HTTP handler wrapper decodes Microsoft AD PAC authorization data * Client Side * Client that can authenticate to an SPNEGO Kerberos authenticated web service * Ability to change client's password * General * Kerberos libraries for custom integration * Parsing Keytab files * Parsing krb5.conf files * Parsing client credentials cache files such as `/tmp/krb5cc_$(id -u $(whoami))` #### Implemented Encryption & Checksum Types | Implementation | Encryption ID | Checksum ID | RFC | |-------|-------------|------------|------| | des3-cbc-sha1-kd | 16 | 12 | 3961 | | aes128-cts-hmac-sha1-96 | 17 | 15 | 3962 | | aes256-cts-hmac-sha1-96 | 18 | 16 | 3962 | | aes128-cts-hmac-sha256-128 | 19 | 19 | 8009 | | aes256-cts-hmac-sha384-192 | 20 | 20 | 8009 | | rc4-hmac | 23 | -138 | 4757 | Currently the following is working/tested: * Tested against MIT KDC (1.6.3 is the oldest version tested against) and Microsoft Active Directory (Windows 2008 R2) * Tested against a KDC that supports PA-FX-FAST. * Tested against users that have pre-authentication required using PA-ENC-TIMESTAMP. * Microsoft PAC Authorization Data is processed and exposed in the HTTP request context. Available if Microsoft Active Directory is used as the KDC. ## Contributing If you are interested in contributing to gokrb5, great! Please read the [contribution guidelines](https://github.com/jcmturner/gokrb5/blob/master/CONTRIBUTING.md). ## Usage --- ### Configuration The gokrb5 libraries use the same krb5.conf configuration file format as MIT Kerberos, described [here](https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html). Config instances can be created by loading from a file path or by passing a string, io.Reader or bufio.Scanner to the relevant method: ```go import "gopkg.in/jcmturner/gokrb5.v5/config" cfg, err := config.Load("/path/to/config/file") cfg, err := config.NewConfigFromString(krb5Str) //String must have appropriate newline separations cfg, err := config.NewConfigFromReader(reader) cfg, err := config.NewConfigFromScanner(scanner) ``` ### Keytab files Standard keytab files can be read from a file or from a slice of bytes: ```go import "gopkg.in/jcmturner/gokrb5.v5/keytab" ktFromFile, err := keytab.Load("/path/to/file.keytab") ktFromBytes, err := keytab.Parse(b) ``` --- ### Kerberos Client Create a client instance with either a password or a keytab: ```go import "gopkg.in/jcmturner/gokrb5.v5/client" cl := client.NewClientWithPassword("username", "REALM.COM", "password") cl := client.NewClientWithKeytab("username", "REALM.COM", kt) ``` Provide configuration to the client: ```go cl.WithConfig(cfg) ``` Login: ```go err := cl.Login() ``` Kerberos Ticket Granting Tickets (TGT) will be automatically renewed unless the client was created from a CCache. A client can be destroyed with the following method: ```go cl.Destroy() ``` #### Active Directory KDC and FAST negotiation Active Directory does not commonly support FAST negotiation so you will need to disable this on the client. If this is the case you will see this error: ```KDC did not respond appropriately to FAST negotiation``` To resolve this disable PA-FX-Fast on the client before performing Login() with the line below. ```go cl.GoKrb5Conf.DisablePAFXFast = true ``` #### Authenticate to a Service ##### HTTP SPNEGO Create the HTTP request object and then call the client's SetSPNEGOHeader method passing the Service Principal Name (SPN) or to auto generate the SPN from the request object pass a null string "" ```go r, _ := http.NewRequest("GET", "http://host.test.gokrb5/index.html", nil) spn := "" cl.SetSPNEGOHeader(r, spn) HTTPResp, err := http.DefaultClient.Do(r) ``` ##### Generic Kerberos Client To authenticate to a service a client will need to request a service ticket for a Service Principal Name (SPN) and form into an AP_REQ message along with an authenticator encrypted with the session key that was delivered from the KDC along with the service ticket. The steps below outline how to do this. * Get the service ticket and session key for the service the client is authenticating to. The following method will use the client's cache either returning a valid cached ticket, renewing a cached ticket with the KDC or requesting a new ticket from the KDC. Therefore the GetServiceTicket method can be continually used for the most efficient interaction with the KDC. ```go tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5") ``` The steps after this will be specific to the application protocol but it will likely involve a client/server Authentication Protocol exchange (AP exchange). This will involve these steps: * Generate a new Authenticator and generate a sequence number and subkey: ```go auth, _ := types.NewAuthenticator(cl.Credentials.Realm, cl.Credentials.CName) etype, _ := crypto.GetEtype(key.KeyType) auth.GenerateSeqNumberAndSubKey(key.KeyType, etype.GetKeyByteSize()) ``` * Set the checksum on the authenticator The checksum is an application specific value. Set as follows: ```go auth.Cksum = types.Checksum{ CksumType: checksumIDint, Checksum: checksumBytesSlice, } ``` * Create the AP_REQ: ```go APReq, err := messages.NewAPReq(tkt, key, auth) ``` Now send the AP_REQ to the service. How this is done will be specific to the application use case. #### Changing a Client Password This feature uses the Microsoft Kerberos Password Change protocol (RFC 3244). This is implemented in Microsoft Active Directory and in MIT krb5kdc as of version 1.7. Typically the kpasswd server listens on port 464. Below is example code for how to use this feature: ```go cfg, err := config.Load("/path/to/config/file") if err != nil { panic(err.Error()) } kt, err := keytab.Load("/path/to/file.keytab") if err != nil { panic(err.Error()) } cl := client.NewClientWithKeytab("username", "REALM.COM", kt) cl.WithConfig(cfg) ok, err := cl.ChangePasswd("newpassword") if err != nil { panic(err.Error()) } if !ok { panic("failed to change password") } ``` The client kerberos config (krb5.conf) will need to have either the kpassd_server or admin_server defined in the relevant [realms] section. For example: ``` REALM.COM = { kdc = 127.0.0.1:88 kpasswd_server = 127.0.0.1:464 default_domain = realm.com } ``` See https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms for more information. --- ### Kerberised Service #### SPNEGO/Kerberos HTTP Service A HTTP handler wrapper can be used to implement Kerberos SPNEGO authentication for web services. To configure the wrapper the keytab for the SPN and a Logger are required: ```go kt, err := keytab.Load("/path/to/file.keytab") l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile) ``` Create a handler function of the application's handling method (apphandler in the example below): ```go h := http.HandlerFunc(apphandler) ``` Configure the HTTP handler: ```go serviceAccountName = "" http.Handler("/", service.SPNEGOKRB5Authenticate(h, kt, serviceAccountName, l)) ``` The serviceAccountName needs to be defined when using Active Directory where the SPN is mapped to a user account. If this is not required it should be set to an empty string "". If authentication succeeds then the request's context will have the following values added so they can be accessed within the application's handler: * service.CTXKeyAuthenticated - Boolean indicating if the user is authenticated. Use of this value should also handle that this value may not be set and should assume "false" in that case. * service.CTXKeyCredentials - The authenticated user's credentials. If Microsoft Active Directory is used as the KDC then additional ADCredentials are available in the credentials.Attributes map under the key credentials.AttributeKeyADCredentials. For example the SIDs of the users group membership are available and can be used by your application for authorization. Access the credentials within your application: ```go ctx := r.Context() if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser { if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok { if ADCreds, ok := creds.Attributes[credentials.AttributeKeyADCredentials].(credentials.ADCredentials); ok { // Now access the fields of the ADCredentials struct. For example: groupSids := ADCreds.GroupMembershipSIDs } } } ``` #### Generic Kerberised Service - Validating Client Details To validate the AP_REQ sent by the client on the service side call this method: ```go import "gopkg.in/jcmturner/gokrb5.v5/service" var ktprinc string //The SPN of the service to find the key in the keytab. var requireHostAddr bool //Whether to force requiring the ticket to contain host addresses to check the client against. if ok, creds, err := service.ValidateAPREQ(mt.APReq, kt, ktprinc, r.RemoteAddr, requireHostAddr); ok { // Perform application specifc actions // creds object has details about the client identity } ``` --- ## References * [RFC 3244 Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols](https://tools.ietf.org/html/rfc3244) * [RFC 4120 The Kerberos Network Authentication Service (V5)](https://tools.ietf.org/html/rfc4120) * [RFC 3961 Encryption and Checksum Specifications for Kerberos 5](https://tools.ietf.org/html/rfc3961) * [RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5](https://tools.ietf.org/html/rfc3962) * [RFC 4121 The Kerberos Version 5 GSS-API Mechanism](https://tools.ietf.org/html/rfc4121) * [RFC 4178 The Simple and Protected Generic Security Service Application Program Interface (GSS-API) Negotiation Mechanism](https://tools.ietf.org/html/rfc4178.html) * [RFC 4559 SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows](https://tools.ietf.org/html/rfc4559.html) * [RFC 4757 The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows](https://tools.ietf.org/html/rfc4757) * [RFC 6806 Kerberos Principal Name Canonicalization and Cross-Realm Referrals](https://tools.ietf.org/html/rfc6806.html) * [RFC 6113 A Generalized Framework for Kerberos Pre-Authentication](https://tools.ietf.org/html/rfc6113.html) * [RFC 8009 AES Encryption with HMAC-SHA2 for Kerberos 5](https://tools.ietf.org/html/rfc8009) * [IANA Assigned Kerberos Numbers](http://www.iana.org/assignments/kerberos-parameters/kerberos-parameters.xhtml) * [HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol - Part 1](https://msdn.microsoft.com/en-us/library/ms995329.aspx) * [HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol - Part 2](https://msdn.microsoft.com/en-us/library/ms995330.aspx) * [Microsoft PAC Validation](https://blogs.msdn.microsoft.com/openspecification/2009/04/24/understanding-microsoft-kerberos-pac-validation/) * [Microsoft Kerberos Protocol Extensions](https://msdn.microsoft.com/en-us/library/cc233855.aspx) * [Windows Data Types](https://msdn.microsoft.com/en-us/library/cc230273.aspx) ### Useful Links * https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing ## Thanks * Greg Hudson from the MIT Consortium for Kerberos and Internet Trust for providing useful advice. ## Contributing Thank you for your interest in contributing to gokrb5 please read the [contribution guide](https://github.com/jcmturner/gokrb5/blob/master/CONTRIBUTING.md) as it should help you get started. ## Known Issues | Issue | Worked around? | References | |-------|-------------|------------| | The Go standard library's encoding/asn1 package cannot unmarshal into slice of asn1.RawValue | Yes | https://github.com/golang/go/issues/17321 | | The Go standard library's encoding/asn1 package cannot marshal into a GeneralString | Yes - using https://github.com/jcmturner/gofork/tree/master/encoding/asn1 | https://github.com/golang/go/issues/18832 | | The Go standard library's encoding/asn1 package cannot marshal into slice of strings and pass stringtype parameter tags to members | Yes - using https://github.com/jcmturner/gofork/tree/master/encoding/asn1 | https://github.com/golang/go/issues/18834 | | The Go standard library's encoding/asn1 package cannot marshal with application tags | Yes | | | The Go standard library's x/crypto/pbkdf2.Key function uses the int type for iteraction count limiting meaning the 4294967296 count specified in https://tools.ietf.org/html/rfc3962 section 4 cannot be met on 32bit systems | Yes - using https://github.com/jcmturner/gofork/tree/master/x/crypto/pbkdf2 | https://go-review.googlesource.com/c/crypto/+/85535 | golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/asn1tools/000077500000000000000000000000001362537225700225005ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/asn1tools/tools.go000066400000000000000000000057011362537225700241720ustar00rootroot00000000000000// Package asn1tools provides tools for managing ASN1 marshaled data. package asn1tools import ( "github.com/jcmturner/gofork/encoding/asn1" ) // MarshalLengthBytes returns the ASN1 encoded bytes for the length 'l' // // There are two forms: short (for lengths between 0 and 127), and long definite (for lengths between 0 and 2^1008 -1). // // Short form: One octet. Bit 8 has value "0" and bits 7-1 give the length. // // Long form: Two to 127 octets. Bit 8 of first octet has value "1" and bits 7-1 give the number of additional length octets. Second and following octets give the length, base 256, most significant digit first. func MarshalLengthBytes(l int) []byte { if l <= 127 { return []byte{byte(l)} } var b []byte p := 1 for i := 1; i < 127; { b = append([]byte{byte((l % (p * 256)) / p)}, b...) p = p * 256 l = l - l%p if l <= 0 { break } } return append([]byte{byte(128 + len(b))}, b...) } // GetLengthFromASN returns the length of a slice of ASN1 encoded bytes from the ASN1 length header it contains. func GetLengthFromASN(b []byte) int { if int(b[1]) <= 127 { return int(b[1]) } // The bytes that indicate the length lb := b[2 : 2+int(b[1])-128] base := 1 l := 0 for i := len(lb) - 1; i >= 0; i-- { l += int(lb[i]) * base base = base * 256 } return l } // GetNumberBytesInLengthHeader returns the number of bytes in the ASn1 header that indicate the length. func GetNumberBytesInLengthHeader(b []byte) int { if int(b[1]) <= 127 { return 1 } // The bytes that indicate the length return 1 + int(b[1]) - 128 } // AddASNAppTag adds an ASN1 encoding application tag value to the raw bytes provided. func AddASNAppTag(b []byte, tag int) []byte { r := asn1.RawValue{ Class: asn1.ClassApplication, IsCompound: true, Tag: tag, Bytes: b, } ab, _ := asn1.Marshal(r) return ab } /* // The Marshal method of golang's asn1 package does not enable you to define wrapping the output in an application tag. // This method adds that wrapping tag. func AddASNAppTag(b []byte, tag int) []byte { // The ASN1 wrapping consists of 2 bytes: // 1st byte -> Identifier Octet - Application Tag // 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here. // Application Tag: //| Bit: | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | //| Value: | 0 | 1 | 1 | From the RFC spec 4120 | //| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value | // Therefore the value of the byte is an integer = ( Application tag value + 96 ) //b = append(MarshalLengthBytes(int(b[1])+2), b...) b = append(MarshalLengthBytes(len(b)), b...) b = append([]byte{byte(96 + tag)}, b...) return b } */ golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/000077500000000000000000000000001362537225700220335ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/ASExchange.go000066400000000000000000000130031362537225700243250ustar00rootroot00000000000000package client import ( "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // ASExchange performs an AS exchange for the client to retrieve a TGT. func (cl *Client) ASExchange(realm string, ASReq messages.ASReq, referral int) (messages.ASRep, error) { if ok, err := cl.IsConfigured(); !ok { return messages.ASRep{}, krberror.Errorf(err, krberror.ConfigError, "AS Exchange cannot be preformed") } b, err := ASReq.Marshal() if err != nil { return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ") } var ASRep messages.ASRep rb, err := cl.SendToKDC(b, realm) if err != nil { if e, ok := err.(messages.KRBError); ok { switch e.ErrorCode { case errorcode.KDC_ERR_PREAUTH_REQUIRED: // From now on assume this client will need to do this pre-auth and set the PAData cl.GoKrb5Conf.AssumePAEncTimestampRequired = true err = setPAData(cl, e, &ASReq) if err != nil { return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required") } b, err := ASReq.Marshal() if err != nil { return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData") } rb, err = cl.SendToKDC(b, realm) if err != nil { if _, ok := err.(messages.KRBError); ok { return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC") } return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC") } case errorcode.KDC_ERR_WRONG_REALM: // Client referral https://tools.ietf.org/html/rfc6806.html#section-7 if referral > 5 { return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded") } referral++ return cl.ASExchange(e.CRealm, ASReq, referral) default: return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC") } } else { return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC") } } err = ASRep.Unmarshal(rb) if err != nil { return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP") } if ok, err := ASRep.IsValid(cl.Config, cl.Credentials, ASReq); !ok { return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect") } return ASRep, nil } func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) error { if !cl.GoKrb5Conf.DisablePAFXFast { pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP} ASReq.PAData = append(ASReq.PAData, pa) } if cl.GoKrb5Conf.AssumePAEncTimestampRequired { paTSb, err := types.GetPAEncTSEncAsnMarshalled() if err != nil { return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication") } var et etype.EType if krberr.ErrorCode == 0 { // This is not in response to an error from the KDC. It is preemptive et, err = crypto.GetEtype(ASReq.ReqBody.EType[0]) // Take the first as preference if err != nil { return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption") } } else { // Get the etype to use from the PA data in the KRBError e-data et, err = preAuthEType(krberr) if err != nil { return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption") } } key, err := cl.Key(et, krberr) if err != nil { return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials") } paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1) if err != nil { return krberror.Errorf(err, krberror.EncryptingError, "error encrypting pre-authentication timestamp") } pb, err := paEncTS.Marshal() if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data") } pa := types.PAData{ PADataType: patype.PA_ENC_TIMESTAMP, PADataValue: pb, } ASReq.PAData = append(ASReq.PAData, pa) } return nil } func preAuthEType(krberr messages.KRBError) (etype etype.EType, err error) { var etypeID int32 var pas types.PADataSequence e := pas.Unmarshal(krberr.EData) if e != nil { err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data") return } for _, pa := range pas { switch pa.PADataType { case patype.PA_ETYPE_INFO2: info, e := pa.GetETypeInfo2() if e != nil { err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data") return } etypeID = info[0].EType break case patype.PA_ETYPE_INFO: info, e := pa.GetETypeInfo() if e != nil { err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO data") return } etypeID = info[0].EType } } etype, e = crypto.GetEtype(etypeID) if e != nil { err = krberror.Errorf(e, krberror.EncryptingError, "error creating etype") return } return etype, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/TGSExchange.go000066400000000000000000000104071362537225700244640ustar00rootroot00000000000000package client import ( "time" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // TGSExchange performs a TGS exchange to retrieve a ticket to the specified SPN. // The ticket retrieved is added to the client's cache. func (cl *Client) TGSExchange(spn types.PrincipalName, kdcRealm string, tkt messages.Ticket, sessionKey types.EncryptionKey, renewal bool, referral int) (tgsReq messages.TGSReq, tgsRep messages.TGSRep, err error) { //// Check what sessions we have for this SPN. //// Will get the session to the default realm if one does not exist for requested SPN //sess, err := cl.GetSessionFromPrincipalName(spn) //if err != nil { // return tgsReq, tgsRep, err //} tgsReq, err = messages.NewTGSReq(cl.Credentials.CName, kdcRealm, cl.Config, tkt, sessionKey, spn, renewal) if err != nil { return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "TGS Exchange Error: failed to generate a new TGS_REQ") } b, err := tgsReq.Marshal() if err != nil { return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to generate a new TGS_REQ") } r, err := cl.SendToKDC(b, kdcRealm) if err != nil { if _, ok := err.(messages.KRBError); ok { return tgsReq, tgsRep, krberror.Errorf(err, krberror.KDCError, "TGS Exchange Error: kerberos error response from KDC") } return tgsReq, tgsRep, krberror.Errorf(err, krberror.NetworkingError, "TGS Exchange Error: issue sending TGS_REQ to KDC") } err = tgsRep.Unmarshal(r) if err != nil { return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP") } err = tgsRep.DecryptEncPart(sessionKey) if err != nil { return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP") } // TODO should this check the first element is krbtgt rather than the nametype? if tgsRep.Ticket.SName.NameType == nametype.KRB_NT_SRV_INST && !tgsRep.Ticket.SName.Equal(spn) { if referral > 5 { return tgsReq, tgsRep, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of referrals exceeded") } // Server referral https://tools.ietf.org/html/rfc6806.html#section-8 // The TGS Rep contains a TGT for another domain as the service resides in that domain. if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok { return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid") } cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart) realm := tgsRep.Ticket.SName.NameString[1] referral++ return cl.TGSExchange(spn, realm, tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, false, referral) } if ok, err := tgsRep.IsValid(cl.Config, tgsReq); !ok { return tgsReq, tgsRep, krberror.Errorf(err, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid") } return tgsReq, tgsRep, nil } // GetServiceTicket makes a request to get a service ticket for the SPN specified // SPN format: / Eg. HTTP/www.example.com // The ticket will be added to the client's ticket cache func (cl *Client) GetServiceTicket(spn string) (messages.Ticket, types.EncryptionKey, error) { var tkt messages.Ticket var skey types.EncryptionKey if tkt, skey, ok := cl.GetCachedTicket(spn); ok { // Already a valid ticket in the cache return tkt, skey, nil } princ := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn) sess, err := cl.GetSessionFromPrincipalName(princ) if err != nil { return tkt, skey, err } // Ensure TGT still valid if time.Now().UTC().After(sess.EndTime) { _, err := cl.updateSession(sess) if err != nil { return tkt, skey, err } // Get the session again as it could have been replaced by the update sess, err = cl.GetSessionFromPrincipalName(princ) if err != nil { return tkt, skey, err } } _, tgsRep, err := cl.TGSExchange(princ, sess.Realm, sess.TGT, sess.SessionKey, false, 0) if err != nil { return tkt, skey, err } cl.Cache.addEntry( tgsRep.Ticket, tgsRep.DecryptedEncPart.AuthTime, tgsRep.DecryptedEncPart.StartTime, tgsRep.DecryptedEncPart.EndTime, tgsRep.DecryptedEncPart.RenewTill, tgsRep.DecryptedEncPart.Key, ) return tgsRep.Ticket, tgsRep.DecryptedEncPart.Key, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/cache.go000066400000000000000000000054461362537225700234360ustar00rootroot00000000000000package client import ( "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" "strings" "sync" "time" ) // Cache for client tickets. type Cache struct { Entries map[string]CacheEntry mux sync.RWMutex } // CacheEntry holds details for a client cache entry. type CacheEntry struct { Ticket messages.Ticket AuthTime time.Time StartTime time.Time EndTime time.Time RenewTill time.Time SessionKey types.EncryptionKey } // NewCache creates a new client ticket cache instance. func NewCache() *Cache { return &Cache{ Entries: map[string]CacheEntry{}, } } // GetEntry returns a cache entry that matches the SPN. func (c *Cache) getEntry(spn string) (CacheEntry, bool) { c.mux.RLock() defer c.mux.RUnlock() e, ok := (*c).Entries[spn] return e, ok } // AddEntry adds a ticket to the cache. func (c *Cache) addEntry(tkt messages.Ticket, authTime, startTime, endTime, renewTill time.Time, sessionKey types.EncryptionKey) CacheEntry { spn := strings.Join(tkt.SName.NameString, "/") c.mux.Lock() defer c.mux.Unlock() (*c).Entries[spn] = CacheEntry{ Ticket: tkt, AuthTime: authTime, StartTime: startTime, EndTime: endTime, RenewTill: renewTill, SessionKey: sessionKey, } return c.Entries[spn] } // Clear deletes all the cache entries func (c *Cache) clear() { c.mux.Lock() defer c.mux.Unlock() for k := range c.Entries { delete(c.Entries, k) } } // RemoveEntry removes the cache entry for the defined SPN. func (c *Cache) RemoveEntry(spn string) { c.mux.Lock() defer c.mux.Unlock() delete(c.Entries, spn) } // GetCachedTicket returns a ticket from the cache for the SPN. // Only a ticket that is currently valid will be returned. func (cl *Client) GetCachedTicket(spn string) (messages.Ticket, types.EncryptionKey, bool) { if e, ok := cl.Cache.getEntry(spn); ok { //If within time window of ticket return it if time.Now().UTC().After(e.StartTime) && time.Now().UTC().Before(e.EndTime) { return e.Ticket, e.SessionKey, true } else if time.Now().UTC().Before(e.RenewTill) { e, err := cl.renewTicket(e) if err != nil { return e.Ticket, e.SessionKey, false } return e.Ticket, e.SessionKey, true } } var tkt messages.Ticket var key types.EncryptionKey return tkt, key, false } // renewTicket renews a cache entry ticket. // To renew from outside the client package use GetCachedTicket func (cl *Client) renewTicket(e CacheEntry) (CacheEntry, error) { spn := e.Ticket.SName _, tgsRep, err := cl.TGSExchange(spn, e.Ticket.Realm, e.Ticket, e.SessionKey, true, 0) if err != nil { return e, err } e = cl.Cache.addEntry( tgsRep.Ticket, tgsRep.DecryptedEncPart.AuthTime, tgsRep.DecryptedEncPart.StartTime, tgsRep.DecryptedEncPart.EndTime, tgsRep.DecryptedEncPart.RenewTill, tgsRep.DecryptedEncPart.Key, ) return e, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client.go000066400000000000000000000163541362537225700236510ustar00rootroot00000000000000// Package client provides a client library and methods for Kerberos 5 authentication. package client import ( "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // Client side configuration and state. type Client struct { Credentials *credentials.Credentials Config *config.Config GoKrb5Conf *Config sessions *sessions Cache *Cache } // Config struct holds GoKRB5 specific client configurations. // Set Disable_PA_FX_FAST to true to force this behaviour off. // Set Assume_PA_ENC_TIMESTAMP_Required to send the PA_ENC_TIMESTAMP pro-actively rather than waiting for a KRB_ERROR response from the KDC indicating it is required. type Config struct { DisablePAFXFast bool AssumePAEncTimestampRequired bool } // NewClientWithPassword creates a new client from a password credential. // Set the realm to empty string to use the default realm from config. func NewClientWithPassword(username, realm, password string) Client { creds := credentials.NewCredentials(username, realm) return Client{ Credentials: creds.WithPassword(password), Config: config.NewConfig(), GoKrb5Conf: &Config{}, sessions: &sessions{ Entries: make(map[string]*session), }, Cache: NewCache(), } } // NewClientWithKeytab creates a new client from a keytab credential. func NewClientWithKeytab(username, realm string, kt keytab.Keytab) Client { creds := credentials.NewCredentials(username, realm) return Client{ Credentials: creds.WithKeytab(kt), Config: config.NewConfig(), GoKrb5Conf: &Config{}, sessions: &sessions{ Entries: make(map[string]*session), }, Cache: NewCache(), } } // NewClientFromCCache create a client from a populated client cache. // // WARNING: A client created from CCache does not automatically renew TGTs and a failure will occur after the TGT expires. func NewClientFromCCache(c credentials.CCache) (Client, error) { cl := Client{ Credentials: c.GetClientCredentials(), Config: config.NewConfig(), GoKrb5Conf: &Config{}, sessions: &sessions{ Entries: make(map[string]*session), }, Cache: NewCache(), } spn := types.PrincipalName{ NameType: nametype.KRB_NT_SRV_INST, NameString: []string{"krbtgt", c.DefaultPrincipal.Realm}, } cred, ok := c.GetEntry(spn) if !ok { return cl, errors.New("TGT not found in CCache") } var tgt messages.Ticket err := tgt.Unmarshal(cred.Ticket) if err != nil { return cl, fmt.Errorf("TGT bytes in cache are not valid: %v", err) } cl.sessions.Entries[c.DefaultPrincipal.Realm] = &session{ Realm: c.DefaultPrincipal.Realm, AuthTime: cred.AuthTime, EndTime: cred.EndTime, RenewTill: cred.RenewTill, TGT: tgt, SessionKey: cred.Key, } for _, cred := range c.GetEntries() { var tkt messages.Ticket err = tkt.Unmarshal(cred.Ticket) if err != nil { return cl, fmt.Errorf("cache entry ticket bytes are not valid: %v", err) } cl.Cache.addEntry( tkt, cred.AuthTime, cred.StartTime, cred.EndTime, cred.RenewTill, cred.Key, ) } return cl, nil } // WithConfig sets the Kerberos configuration for the client. func (cl *Client) WithConfig(cfg *config.Config) *Client { cl.Config = cfg return cl } // WithKeytab adds a keytab to the client func (cl *Client) WithKeytab(kt keytab.Keytab) *Client { cl.Credentials.WithKeytab(kt) return cl } // WithPassword adds a password to the client func (cl *Client) WithPassword(password string) *Client { cl.Credentials.WithPassword(password) return cl } // Key returns a key for the client. Preferably from a keytab and then generated from the password. // The KRBError would have been returned from the KDC and must be of type KDC_ERR_PREAUTH_REQUIRED. // If a KRBError is not available pass messages.KRBError{} and a key will be returned from the credentials keytab. func (cl *Client) Key(etype etype.EType, krberr messages.KRBError) (types.EncryptionKey, error) { if cl.Credentials.HasKeytab() && etype != nil { return cl.Credentials.Keytab.GetEncryptionKey(cl.Credentials.CName.NameString, cl.Credentials.Realm, 0, etype.GetETypeID()) } else if cl.Credentials.HasPassword() { if krberr.ErrorCode == errorcode.KDC_ERR_PREAUTH_REQUIRED { var pas types.PADataSequence err := pas.Unmarshal(krberr.EData) if err != nil { return types.EncryptionKey{}, fmt.Errorf("could not get PAData from KRBError to generate key from password: %v", err) } key, _, err := crypto.GetKeyFromPassword(cl.Credentials.Password, krberr.CName, krberr.CRealm, etype.GetETypeID(), pas) return key, err } key, _, err := crypto.GetKeyFromPassword(cl.Credentials.Password, cl.Credentials.CName, cl.Credentials.Realm, etype.GetETypeID(), types.PADataSequence{}) return key, err } return types.EncryptionKey{}, errors.New("credential has neither keytab or password to generate key") } // LoadConfig loads the Kerberos configuration for the client from file path specified. func (cl *Client) LoadConfig(cfgPath string) (*Client, error) { cfg, err := config.Load(cfgPath) if err != nil { return cl, err } cl.Config = cfg return cl, nil } // IsConfigured indicates if the client has the values required set. func (cl *Client) IsConfigured() (bool, error) { if cl.Credentials.Username == "" { return false, errors.New("client does not have a username") } if cl.Credentials.Realm == "" { return false, errors.New("client does not have a define realm") } // Client needs to have either a password, keytab or a session already (later when loading from CCache) if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() { sess, err := cl.GetSessionFromRealm(cl.Credentials.Realm) if err != nil || sess.AuthTime.IsZero() { return false, errors.New("client has neither a keytab nor a password set and no session") } } if !cl.Config.LibDefaults.DNSLookupKDC { for _, r := range cl.Config.Realms { if r.Realm == cl.Credentials.Realm { if len(r.KDC) > 0 { return true, nil } return false, errors.New("client krb5 config does not have any defined KDCs for the default realm") } } } return true, nil } // Login the client with the KDC via an AS exchange. func (cl *Client) Login() error { if ok, err := cl.IsConfigured(); !ok { return err } ASReq, err := messages.NewASReqForTGT(cl.Credentials.Realm, cl.Config, cl.Credentials.CName) if err != nil { return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AS_REQ") } err = setPAData(cl, messages.KRBError{}, &ASReq) if err != nil { return krberror.Errorf(err, krberror.KRBMsgError, "failed setting AS_REQ PAData") } ASRep, err := cl.ASExchange(cl.Credentials.Realm, ASReq, 0) if err != nil { return err } cl.AddSession(ASRep.Ticket, ASRep.DecryptedEncPart) return nil } // Destroy stops the auto-renewal of all sessions and removes the sessions and cache entries from the client. func (cl *Client) Destroy() { creds := credentials.NewCredentials("", "") cl.sessions.destroy() cl.Cache.clear() cl.Credentials = &creds } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_ad_integration_test.go000066400000000000000000000120141362537225700277440ustar00rootroot00000000000000// +build adintegration // To turn on this test use -tags=integration in go test command package client import ( "encoding/hex" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" "testing" ) func TestClient_SuccessfulLogin_AD(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_AD} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("Error on login: %v\n", err) } } func TestClient_GetServiceTicket_AD(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_AD} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("Error on login: %v\n", err) } spn := "HTTP/host.test.gokrb5" tkt, key, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("Error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) assert.Equal(t, int32(18), key.KeyType) } func TestClient_SuccessfulLogin_AD_TRUST_USER_DOMAIN(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN} c.LibDefaults.DefaultRealm = "USER.GOKRB5" cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt) cl.WithConfig(c) cl.GoKrb5Conf.DisablePAFXFast = true err := cl.Login() if err != nil { t.Fatalf("Error on login: %v\n", err) } } func TestClient_GetServiceTicket_AD_TRUST_USER_DOMAIN(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN} c.LibDefaults.DefaultRealm = "USER.GOKRB5" c.LibDefaults.Canonicalize = true cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt) c.LibDefaults.DefaultTktEnctypes = []string{"rc4-hmac"} c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]} c.LibDefaults.DefaultTGSEnctypes = []string{"rc4-hmac"} c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]} cl.WithConfig(c) cl.GoKrb5Conf.DisablePAFXFast = true err := cl.Login() if err != nil { t.Fatalf("Error on login: %v\n", err) } spn := "HTTP/host.res.gokrb5" tkt, key, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("Error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) assert.Equal(t, etypeID.ETypesByName["rc4-hmac"], key.KeyType) b, _ = hex.DecodeString(testdata.SYSHTTP_RESGOKRB5_AD_KEYTAB) skt, _ := keytab.Parse(b) err = tkt.DecryptEncPart(skt, "sysHTTP") if err != nil { t.Errorf("error decrypting ticket with service keytab: %v", err) } isPAC, pac, err := tkt.GetPACType(skt, "sysHTTP") if err != nil { t.Errorf("error getting PAC: %v", err) } assert.True(t, isPAC, "Did not find PAC in service ticket") assert.Equal(t, "testuser1", pac.KerbValidationInfo.EffectiveName.Value, "PAC value not parsed") } func TestClient_GetServiceTicket_AD_USER_DOMAIN(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_USERKRB5_AD_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_AD_TRUST_USER_DOMAIN} c.LibDefaults.DefaultRealm = "USER.GOKRB5" c.LibDefaults.Canonicalize = true cl := NewClientWithKeytab("testuser1", "USER.GOKRB5", kt) c.LibDefaults.DefaultTktEnctypes = []string{"rc4-hmac"} c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]} c.LibDefaults.DefaultTGSEnctypes = []string{"rc4-hmac"} c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["rc4-hmac"]} cl.WithConfig(c) cl.GoKrb5Conf.DisablePAFXFast = true err := cl.Login() if err != nil { t.Fatalf("Error on login: %v\n", err) } spn := "HTTP/user2.user.gokrb5" tkt, _, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("Error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) //assert.Equal(t, etypeID.ETypesByName["rc4-hmac"], key.KeyType) b, _ = hex.DecodeString(testdata.TESTUSER2_USERKRB5_AD_KEYTAB) skt, _ := keytab.Parse(b) err = tkt.DecryptEncPart(skt, "testuser2") if err != nil { t.Errorf("error decrypting ticket with service keytab: %v", err) } isPAC, pac, err := tkt.GetPACType(skt, "testuser2") if err != nil { t.Errorf("error getting PAC: %v", err) } assert.True(t, isPAC, "Did not find PAC in service ticket") assert.Equal(t, "testuser1", pac.KerbValidationInfo.EffectiveName.Value, "PAC value not parsed") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_dns_test.go000066400000000000000000000040111362537225700255370ustar00rootroot00000000000000// +build dns package client import ( "encoding/hex" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" "testing" ) func TestResolveKDC(t *testing.T) { //ns := os.Getenv("DNSUTILS_OVERRIDE_NS") //if ns == "" { // os.Setenv("DNSUTILS_OVERRIDE_NS", testdata.TEST_NS) //} c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.LibDefaults.DNSLookupKDC = true var cl Client cl.WithConfig(c) count, res, err := cl.Config.GetKDCs(c.LibDefaults.DefaultRealm, true) if err != nil { t.Errorf("error resolving KDC via DNS TCP: %v", err) } assert.Equal(t, 5, count, "Number of SRV records not as expected: %v", res) assert.Equal(t, count, len(res), "Map size does not match: %v", res) expected := []string{ "kdc.test.gokrb5:88", "kdc1a.test.gokrb5:88", "kdc2a.test.gokrb5:88", "kdc1b.test.gokrb5:88", "kdc2b.test.gokrb5:88", } for _, s := range expected { var found bool for _, v := range res { if s == v { found = true break } } assert.True(t, found, "Record %s not found in results", s) } c.LibDefaults.DNSLookupKDC = false _, res, err = cl.Config.GetKDCs(c.LibDefaults.DefaultRealm, true) if err != nil { t.Errorf("error resolving KDCs from config: %v", err) } assert.Equal(t, "127.0.0.1:88", res[1], "KDC not read from config as expected") } func TestClient_Login_DNSKDCs(t *testing.T) { //ns := os.Getenv("DNSUTILS_OVERRIDE_NS") //if ns == "" { // os.Setenv("DNSUTILS_OVERRIDE_NS", testdata.TEST_NS) //} c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) // Set to lookup KDCs in DNS c.LibDefaults.DNSLookupKDC = true //Blank out the KDCs to ensure they are not being used c.Realms = []config.Realm{} b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on logging in using DNS lookup of KDCs: %v\n", err) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/client_integration_test.go000066400000000000000000000526041362537225700273110ustar00rootroot00000000000000// +build integration // To turn on this test use -tags=integration in go test command package client import ( "bytes" "encoding/hex" "io" "io/ioutil" "net/http" "os" "os/exec" "os/user" "runtime" "testing" "time" "errors" "fmt" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" "strings" "sync" ) func TestClient_SuccessfulLogin_Keytab(t *testing.T) { addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) var tests = []string{ testdata.TEST_KDC, testdata.TEST_KDC_OLD, testdata.TEST_KDC_LASTEST, } for _, test := range tests { c.Realms[0].KDC = []string{addr + ":" + test} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on logging in with KDC %s: %v\n", test, err) } } } func TestClient_SuccessfulLogin_Password(t *testing.T) { addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) var tests = []string{ testdata.TEST_KDC, testdata.TEST_KDC_OLD, testdata.TEST_KDC_LASTEST, } for _, test := range tests { c.Realms[0].KDC = []string{addr + ":" + test} cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue") cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on logging in with KDC %s: %v\n", test, err) } } } func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} c.LibDefaults.UDPPreferenceLimit = 1 cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } } func TestClient_ASExchange_TGSExchange_EncTypes_Keytab(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST} var tests = []string{ "des3-cbc-sha1-kd", "aes128-cts-hmac-sha1-96", "aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha256-128", "aes256-cts-hmac-sha384-192", "rc4-hmac", } for _, test := range tests { c.LibDefaults.DefaultTktEnctypes = []string{test} c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]} c.LibDefaults.DefaultTGSEnctypes = []string{test} c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on login using enctype %s: %v\n", test, err) } tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5") if err != nil { t.Errorf("error in TGS exchange using enctype %s: %v", test, err) } assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test) assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test) } } func TestClient_ASExchange_TGSExchange_EncTypes_Password(t *testing.T) { c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_LASTEST} var tests = []string{ "des3-cbc-sha1-kd", "aes128-cts-hmac-sha1-96", "aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha256-128", "aes256-cts-hmac-sha384-192", "rc4-hmac", } for _, test := range tests { c.LibDefaults.DefaultTktEnctypes = []string{test} c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName[test]} c.LibDefaults.DefaultTGSEnctypes = []string{test} c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName[test]} cl := NewClientWithPassword("testuser1", "TEST.GOKRB5", "passwordvalue") cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on login using enctype %s: %v\n", test, err) } tkt, key, err := cl.GetServiceTicket("HTTP/host.test.gokrb5") if err != nil { t.Errorf("error in TGS exchange using enctype %s: %v", test, err) } assert.Equal(t, "TEST.GOKRB5", tkt.Realm, "Realm in ticket not as expected for %s test", test) assert.Equal(t, etypeID.ETypesByName[test], key.KeyType, "Key is not for enctype %s", test) } } func TestClient_FailedLogin(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_WRONGPASSWD) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err == nil { t.Fatal("login with incorrect password did not error") } } func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } } func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER2_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} c.LibDefaults.UDPPreferenceLimit = 1 cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } } func TestClient_NetworkTimeout(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{testdata.TEST_KDC_BADADDR + ":88"} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err == nil { t.Fatal("login with incorrect KDC address did not error") } } func TestClient_GetServiceTicket(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } spn := "HTTP/host.test.gokrb5" tkt, key, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) assert.Equal(t, int32(18), key.KeyType) //Check cache use - should get the same values back again tkt2, key2, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("error getting service ticket: %v\n", err) } assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher) assert.Equal(t, key.KeyValue, key2.KeyValue) } func TestClient_GetServiceTicket_InvalidSPN(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } spn := "host.test.gokrb5" _, _, err = cl.GetServiceTicket(spn) assert.NotNil(t, err, "Expected unknown principal error") assert.True(t, strings.Contains(err.Error(), "KDC_ERR_S_PRINCIPAL_UNKNOWN"), "Error text not as expected") } func TestClient_GetServiceTicket_OlderKDC(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_OLD} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } spn := "HTTP/host.test.gokrb5" tkt, key, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) assert.Equal(t, int32(18), key.KeyType) } func TestClient_SetSPNEGOHeader(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on AS_REQ: %v\n", err) } url := os.Getenv("TEST_HTTP_URL") if url == "" { url = testdata.TEST_HTTP_URL } r, _ := http.NewRequest("GET", url, nil) httpResp, err := http.DefaultClient.Do(r) if err != nil { t.Fatalf("request error: %v\n", err) } assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected") err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { t.Fatalf("error setting client SPNEGO header: %v", err) } httpResp, err = http.DefaultClient.Do(r) if err != nil { t.Fatalf("request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected") } func TestMultiThreadedClientUse(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) var wg sync.WaitGroup wg.Add(5) for i := 0; i < 5; i++ { go func() { defer wg.Done() err := cl.Login() if err != nil { panic(err) } }() } wg.Wait() var wg2 sync.WaitGroup wg2.Add(5) for i := 0; i < 5; i++ { go func() { defer wg2.Done() err := spnegoGet(&cl) if err != nil { panic(err) } }() } wg2.Wait() } func TestMultiThreadedClientSession(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("failed to log in: %v", err) } s, err := cl.GetSessionFromRealm("TEST.GOKRB5") if err != nil { t.Fatalf("error initially getting session: %v", err) } go func() { for { err := cl.renewTGT(s) if err != nil { t.Logf("error renewing TGT: %v", err) } time.Sleep(time.Millisecond * 100) } }() var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() s, err := cl.GetSessionFromRealm("TEST.GOKRB5") if err != nil { t.Logf("error getting session: %v", err) } fmt.Fprintf(ioutil.Discard, "%v", s.RenewTill) }() time.Sleep(time.Second) } wg.Wait() } func spnegoGet(cl *Client) error { url := os.Getenv("TEST_HTTP_URL") if url == "" { url = testdata.TEST_HTTP_URL } r, _ := http.NewRequest("GET", url, nil) httpResp, err := http.DefaultClient.Do(r) if err != nil { return fmt.Errorf("request error: %v\n", err) } if httpResp.StatusCode != http.StatusUnauthorized { return errors.New("did not get unauthorized code when no SPNEGO header set") } err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { return fmt.Errorf("error setting client SPNEGO header: %v", err) } httpResp, err = http.DefaultClient.Do(r) if err != nil { return fmt.Errorf("request error: %v\n", err) } if httpResp.StatusCode != http.StatusOK { return errors.New("did not get OK code when SPNEGO header set") } return nil } func TestNewClientFromCCache(t *testing.T) { b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatalf("error decoding test data") } cc, err := credentials.ParseCCache(b) if err != nil { t.Fatal("error getting test CCache") } cl, err := NewClientFromCCache(cc) if err != nil { t.Fatalf("error creating client from CCache: %v", err) } c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl.WithConfig(c) if ok, err := cl.IsConfigured(); !ok { t.Fatalf("client was not configured from CCache: %v", err) } } // Login to the TEST.GOKRB5 domain and request service ticket for resource in the RESDOM.GOKRB5 domain. // There is a trust between the two domains. func TestClient_GetServiceTicket_Trusted_Resource_Domain(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } for i, r := range c.Realms { if r.Realm == "TEST.GOKRB5" { c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC} } if r.Realm == "RESDOM.GOKRB5" { c.Realms[i].KDC = []string{addr + ":" + testdata.TEST_KDC_RESDOM} } } c.LibDefaults.DefaultRealm = "TEST.GOKRB5" cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha1-96"} c.LibDefaults.DefaultTktEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]} c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha1-96"} c.LibDefaults.DefaultTGSEnctypeIDs = []int32{etypeID.ETypesByName["aes256-cts-hmac-sha1-96"]} cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } spn := "HTTP/host.resdom.gokrb5" tkt, key, err := cl.GetServiceTicket(spn) if err != nil { t.Fatalf("error getting service ticket: %v\n", err) } assert.Equal(t, spn, tkt.SName.GetPrincipalNameString()) assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], key.KeyType) b, _ = hex.DecodeString(testdata.SYSHTTP_RESDOM_KEYTAB) skt, _ := keytab.Parse(b) err = tkt.DecryptEncPart(skt, "") if err != nil { t.Errorf("error decrypting ticket with service keytab: %v", err) } } const ( kinitCmd = "kinit" kvnoCmd = "kvno" spn = "HTTP/host.test.gokrb5" ) func login() error { file, err := os.Create("/etc/krb5.conf") if err != nil { return fmt.Errorf("cannot open krb5.conf: %v", err) } defer file.Close() fmt.Fprintf(file, testdata.TEST_KRB5CONF) cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5") stdinR, stdinW := io.Pipe() stderrR, stderrW := io.Pipe() cmd.Stdin = stdinR cmd.Stderr = stderrW err = cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kinitCmd, err) } go func() { io.WriteString(stdinW, "passwordvalue") stdinW.Close() }() errBuf := new(bytes.Buffer) go func() { io.Copy(errBuf, stderrR) stderrR.Close() }() err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes())) } return nil } func getServiceTkt() error { cmd := exec.Command(kvnoCmd, spn) err := cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kvnoCmd, err) } err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err) } return nil } func loadCCache() (credentials.CCache, error) { usr, _ := user.Current() cpath := "/tmp/krb5cc_" + usr.Uid return credentials.LoadCCache(cpath) } func TestGetServiceTicketFromCCacheTGT(t *testing.T) { err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } cfg, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } cfg.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} cl, err := NewClientFromCCache(c) if err != nil { t.Fatalf("error generating client from ccache: %v", err) } cl.WithConfig(cfg) url := os.Getenv("TEST_HTTP_URL") if url == "" { url = testdata.TEST_HTTP_URL } r, _ := http.NewRequest("GET", url, nil) err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { t.Fatalf("error setting client SPNEGO header: %v", err) } httpResp, err := http.DefaultClient.Do(r) if err != nil { t.Fatalf("request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected") } func TestGetServiceTicketFromCCacheWithoutKDC(t *testing.T) { err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } err = getServiceTkt() if err != nil { t.Fatalf("error getting service ticket: %v", err) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } cfg, _ := config.NewConfigFromString("...") cl, err := NewClientFromCCache(c) if err != nil { t.Fatalf("error generating client from ccache: %v", err) } cl.WithConfig(cfg) url := os.Getenv("TEST_HTTP_URL") if url == "" { url = testdata.TEST_HTTP_URL } r, _ := http.NewRequest("GET", url, nil) err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { t.Fatalf("error setting client SPNEGO header: %v", err) } httpResp, err := http.DefaultClient.Do(r) if err != nil { t.Fatalf("request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "status code in response to client SPNEGO request not as expected") } func TestClient_ChangePasswd(t *testing.T) { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC} c.Realms[0].KPasswdServer = []string{addr + ":464"} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) ok, err := cl.ChangePasswd("newpassword") if err != nil { t.Fatalf("error changing password: %v", err) } assert.True(t, ok, "password was not changed") cl = NewClientWithPassword("testuser1", "TEST.GOKRB5", "newpassword") cl.WithConfig(c) ok, err = cl.ChangePasswd(testdata.TESTUSER1_PASSWORD) if err != nil { t.Fatalf("error changing password: %v", err) } assert.True(t, ok, "password was not changed back") } func TestClient_AutoRenew_Goroutine_Count(t *testing.T) { // Tests that the auto renew of client credentials is not spawning goroutines out of control. addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Errorf("error on logging in: %v\n", err) } n := runtime.NumGoroutine() for i := 0; i < 6; i++ { time.Sleep(time.Second * 20) if runtime.NumGoroutine() > n { t.Fatalf("number of goroutines is increasing: should not be more than %d, is %d", n, runtime.NumGoroutine()) } } } func TestClient_Destroy(t *testing.T) { addr := os.Getenv("TEST_KDC_ADDR") if addr == "" { addr = testdata.TEST_KDC_ADDR } b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.Realms[0].KDC = []string{addr + ":" + testdata.TEST_KDC_SHORTTICKETS} cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) err := cl.Login() if err != nil { t.Fatalf("error on login: %v\n", err) } spn := "HTTP/host.test.gokrb5" _, _, err = cl.GetServiceTicket(spn) if err != nil { t.Fatalf("error getting service ticket: %v\n", err) } n := runtime.NumGoroutine() time.Sleep(time.Second * 60) cl.Destroy() time.Sleep(time.Second * 5) assert.True(t, runtime.NumGoroutine() < n, "auto-renewal goroutine was not stopped when client destroyed") is, _ := cl.IsConfigured() assert.False(t, is, "client is still configured after it was destroyed") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/http.go000066400000000000000000000027531362537225700233500ustar00rootroot00000000000000package client import ( "encoding/base64" "fmt" "net/http" "strings" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/gssapi" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. // To auto generate the SPN from the request object pass a null string "". func (cl *Client) SetSPNEGOHeader(r *http.Request, spn string) error { if spn == "" { spn = "HTTP/" + strings.SplitN(r.Host, ":", 2)[0] } tkt, skey, err := cl.GetServiceTicket(spn) if err != nil { return fmt.Errorf("could not get service ticket: %v", err) } err = SetSPNEGOHeader(*cl.Credentials, tkt, skey, r) if err != nil { return err } return nil } // SetSPNEGOHeader sets the provided ticket as the SPNEGO authorization header on HTTP request object. func SetSPNEGOHeader(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, r *http.Request) error { SPNEGOToken, err := gssapi.GetSPNEGOKrbNegTokenInit(creds, tkt, sessionKey) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "could not generate SPNEGO negotiation token") } nb, err := SPNEGOToken.Marshal() if err != nil { return krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO") } hs := "Negotiate " + base64.StdEncoding.EncodeToString(nb) r.Header.Set("Authorization", hs) return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/network.go000066400000000000000000000122221362537225700240520ustar00rootroot00000000000000package client import ( "bytes" "encoding/binary" "errors" "fmt" "io" "net" "time" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/messages" ) // SendToKDC performs network actions to send data to the KDC. func (cl *Client) SendToKDC(b []byte, realm string) ([]byte, error) { var rb []byte if cl.Config.LibDefaults.UDPPreferenceLimit == 1 { //1 means we should always use TCP rb, errtcp := cl.sendKDCTCP(realm, b) if errtcp != nil { if e, ok := errtcp.(messages.KRBError); ok { return rb, e } return rb, fmt.Errorf("communication error with KDC via TCP: %v", errtcp) } return rb, nil } if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit { //Try UDP first, TCP second rb, errudp := cl.sendKDCUDP(realm, b) if errudp != nil { if e, ok := errudp.(messages.KRBError); ok && e.ErrorCode != errorcode.KRB_ERR_RESPONSE_TOO_BIG { // Got a KRBError from KDC // If this is not a KRB_ERR_RESPONSE_TOO_BIG we will return immediately otherwise will try TCP. return rb, e } // Try TCP r, errtcp := cl.sendKDCTCP(realm, b) if errtcp != nil { if e, ok := errtcp.(messages.KRBError); ok { // Got a KRBError return r, e } return r, fmt.Errorf("failed to communicate with KDC. Attempts made with UDP (%v) and then TCP (%v)", errudp, errtcp) } rb = r } return rb, nil } //Try TCP first, UDP second rb, errtcp := cl.sendKDCTCP(realm, b) if errtcp != nil { if e, ok := errtcp.(messages.KRBError); ok { // Got a KRBError from KDC so returning and not trying UDP. return rb, e } rb, errudp := cl.sendKDCUDP(realm, b) if errudp != nil { if e, ok := errudp.(messages.KRBError); ok { // Got a KRBError return rb, e } return rb, fmt.Errorf("failed to communicate with KDC. Attempts made with TCP (%v) and then UDP (%v)", errtcp, errudp) } } return rb, nil } func dialKDCUDP(count int, kdcs map[int]string) (conn *net.UDPConn, err error) { i := 1 for i <= count { udpAddr, e := net.ResolveUDPAddr("udp", kdcs[i]) if e != nil { err = fmt.Errorf("error resolving KDC address: %v", e) return } conn, err = net.DialUDP("udp", nil, udpAddr) if err == nil { conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second))) return } i++ } err = errors.New("error in getting a UDP connection to any of the KDCs") return } func dialKDCTCP(count int, kdcs map[int]string) (conn *net.TCPConn, err error) { i := 1 for i <= count { tcpAddr, e := net.ResolveTCPAddr("tcp", kdcs[i]) if e != nil { err = fmt.Errorf("error resolving KDC address: %v", e) return } conn, err = net.DialTCP("tcp", nil, tcpAddr) if err == nil { conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second))) return } i++ } err = errors.New("error in getting a TCP connection to any of the KDCs") return } // Send the bytes to the KDC over UDP. func (cl *Client) sendKDCUDP(realm string, b []byte) ([]byte, error) { var r []byte count, kdcs, err := cl.Config.GetKDCs(realm, false) if err != nil { return r, err } conn, err := dialKDCUDP(count, kdcs) if err != nil { return r, err } r, err = cl.sendUDP(conn, b) if err != nil { return r, err } return checkForKRBError(r) } func (cl *Client) sendKDCTCP(realm string, b []byte) ([]byte, error) { var r []byte count, kdcs, err := cl.Config.GetKDCs(realm, true) if err != nil { return r, err } conn, err := dialKDCTCP(count, kdcs) if err != nil { return r, err } rb, err := cl.sendTCP(conn, b) if err != nil { return r, err } return checkForKRBError(rb) } // Send the bytes over UDP. func (cl *Client) sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) { var r []byte defer conn.Close() _, err := conn.Write(b) if err != nil { return r, fmt.Errorf("error sending to (%s): %v", conn.RemoteAddr().String(), err) } udpbuf := make([]byte, 4096) n, _, err := conn.ReadFrom(udpbuf) r = udpbuf[:n] if err != nil { return r, fmt.Errorf("sending over UDP failed to %s: %v", conn.RemoteAddr().String(), err) } if len(r) < 1 { return r, fmt.Errorf("no response data from %s", conn.RemoteAddr().String()) } return r, nil } // Send the bytes over TCP. func (cl *Client) sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) { defer conn.Close() var r []byte /* RFC https://tools.ietf.org/html/rfc4120#section-7.2.2 NB: network byte order == big endian */ var buf bytes.Buffer binary.Write(&buf, binary.BigEndian, uint32(len(b))) b = append(buf.Bytes(), b...) _, err := conn.Write(b) if err != nil { return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err) } sh := make([]byte, 4, 4) _, err = conn.Read(sh) if err != nil { return r, fmt.Errorf("error reading response size header: %v", err) } s := binary.BigEndian.Uint32(sh) rb := make([]byte, s, s) _, err = io.ReadFull(conn, rb) if err != nil { return r, fmt.Errorf("error reading response: %v", err) } if len(rb) < 1 { return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String()) } return rb, nil } func checkForKRBError(b []byte) ([]byte, error) { var KRBErr messages.KRBError if err := KRBErr.Unmarshal(b); err == nil { return b, KRBErr } return b, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/passwd.go000066400000000000000000000044321362537225700236660ustar00rootroot00000000000000package client import ( "fmt" "net" "gopkg.in/jcmturner/gokrb5.v5/kadmin" "gopkg.in/jcmturner/gokrb5.v5/messages" ) // Kpasswd server response codes. const ( KRB5_KPASSWD_SUCCESS = 0 KRB5_KPASSWD_MALFORMED = 1 KRB5_KPASSWD_HARDERROR = 2 KRB5_KPASSWD_AUTHERROR = 3 KRB5_KPASSWD_SOFTERROR = 4 KRB5_KPASSWD_ACCESSDENIED = 5 KRB5_KPASSWD_BAD_VERSION = 6 KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7 ) // ChangePasswd changes the password of the client to the value provided. func (cl *Client) ChangePasswd(newPasswd string) (bool, error) { ASReq, err := messages.NewASReqForChgPasswd(cl.Credentials.Realm, cl.Config, cl.Credentials.CName) if err != nil { return false, err } ASRep, err := cl.ASExchange(cl.Credentials.Realm, ASReq, 0) if err != nil { return false, err } msg, key, err := kadmin.ChangePasswdMsg(cl.Credentials.CName, cl.Credentials.Realm, newPasswd, ASRep.Ticket, ASRep.DecryptedEncPart.Key) if err != nil { return false, err } r, err := cl.sendToKPasswd(msg) if err != nil { return false, err } err = r.Decrypt(key) if err != nil { return false, err } if r.ResultCode != KRB5_KPASSWD_SUCCESS { return false, fmt.Errorf("error response from kdamin: %s", r.Result) } return true, nil } func (cl *Client) sendToKPasswd(msg kadmin.Request) (r kadmin.Reply, err error) { _, kps, err := cl.Config.GetKpasswdServers(cl.Credentials.Realm, true) if err != nil { return } addr := kps[1] b, err := msg.Marshal() if err != nil { return } if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit { return cl.sendKPasswdUDP(b, addr) } return cl.sendKPasswdTCP(b, addr) } func (cl *Client) sendKPasswdTCP(b []byte, kadmindAddr string) (r kadmin.Reply, err error) { tcpAddr, err := net.ResolveTCPAddr("tcp", kadmindAddr) if err != nil { return } conn, err := net.DialTCP("tcp", nil, tcpAddr) if err != nil { return } rb, err := cl.sendTCP(conn, b) err = r.Unmarshal(rb) return } func (cl *Client) sendKPasswdUDP(b []byte, kadmindAddr string) (r kadmin.Reply, err error) { udpAddr, err := net.ResolveUDPAddr("udp", kadmindAddr) if err != nil { return } conn, err := net.DialUDP("udp", nil, udpAddr) if err != nil { return } rb, err := cl.sendUDP(conn, b) err = r.Unmarshal(rb) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/client/session.go000066400000000000000000000123301362537225700240440ustar00rootroot00000000000000package client import ( "fmt" "sync" "time" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // Sessions keyed on the realm name type sessions struct { Entries map[string]*session mux sync.RWMutex } func (s *sessions) destroy() { s.mux.Lock() defer s.mux.Unlock() for k, e := range s.Entries { e.destroy() delete(s.Entries, k) } } // Client session struct. type session struct { Realm string AuthTime time.Time EndTime time.Time RenewTill time.Time TGT messages.Ticket SessionKey types.EncryptionKey SessionKeyExpiration time.Time cancel chan bool mux sync.RWMutex } func (s *session) update(tkt messages.Ticket, dep messages.EncKDCRepPart) { s.mux.Lock() defer s.mux.Unlock() s.AuthTime = dep.AuthTime s.EndTime = dep.EndTime s.RenewTill = dep.RenewTill s.TGT = tkt s.SessionKey = dep.Key s.SessionKeyExpiration = dep.KeyExpiration } func (s *session) destroy() { s.mux.Lock() defer s.mux.Unlock() s.cancel <- true s.EndTime = time.Now().UTC() s.RenewTill = s.EndTime s.SessionKeyExpiration = s.EndTime } // AddSession adds a session for a realm with a TGT to the client's session cache. // A goroutine is started to automatically renew the TGT before expiry. func (cl *Client) AddSession(tkt messages.Ticket, dep messages.EncKDCRepPart) { cl.sessions.mux.Lock() defer cl.sessions.mux.Unlock() s := &session{ Realm: tkt.SName.NameString[1], AuthTime: dep.AuthTime, EndTime: dep.EndTime, RenewTill: dep.RenewTill, TGT: tkt, SessionKey: dep.Key, SessionKeyExpiration: dep.KeyExpiration, cancel: make(chan bool, 1), } // if a session already exists for this, cancel its auto renew. if i, ok := cl.sessions.Entries[tkt.SName.NameString[1]]; ok { i.cancel <- true } cl.sessions.Entries[tkt.SName.NameString[1]] = s cl.enableAutoSessionRenewal(s) } // enableAutoSessionRenewal turns on the automatic renewal for the client's TGT session. func (cl *Client) enableAutoSessionRenewal(s *session) { var timer *time.Timer go func(s *session) { for { s.mux.RLock() w := (s.EndTime.Sub(time.Now().UTC()) * 5) / 6 s.mux.RUnlock() if w < 0 { return } timer = time.NewTimer(w) select { case <-timer.C: renewal, err := cl.updateSession(s) if !renewal && err == nil { // end this goroutine as there will have been a new login and new auto renewal goroutine created. return } case <-s.cancel: // cancel has been called. Stop the timer and exit. timer.Stop() return } } }(s) } // RenewTGT renews the client's TGT session. func (cl *Client) renewTGT(s *session) error { spn := types.PrincipalName{ NameType: nametype.KRB_NT_SRV_INST, NameString: []string{"krbtgt", s.Realm}, } _, tgsRep, err := cl.TGSExchange(spn, s.TGT.Realm, s.TGT, s.SessionKey, true, 0) if err != nil { return krberror.Errorf(err, krberror.KRBMsgError, "error renewing TGT") } s.update(tgsRep.Ticket, tgsRep.DecryptedEncPart) return nil } // updateSession updates either through renewal or creating a new login. // The boolean indicates if the update was a renewal. func (cl *Client) updateSession(s *session) (bool, error) { if time.Now().UTC().Before(s.RenewTill) { err := cl.renewTGT(s) return true, err } err := cl.Login() return false, err } func (cl *Client) getSessionFromRemoteRealm(realm string) (*session, error) { cl.sessions.mux.RLock() sess, ok := cl.sessions.Entries[cl.Credentials.Realm] cl.sessions.mux.RUnlock() if !ok { return nil, fmt.Errorf("client does not have a session for realm %s, login first", cl.Credentials.Realm) } spn := types.PrincipalName{ NameType: nametype.KRB_NT_SRV_INST, NameString: []string{"krbtgt", realm}, } _, tgsRep, err := cl.TGSExchange(spn, cl.Credentials.Realm, sess.TGT, sess.SessionKey, false, 0) if err != nil { return nil, err } cl.AddSession(tgsRep.Ticket, tgsRep.DecryptedEncPart) cl.sessions.mux.RLock() defer cl.sessions.mux.RUnlock() return cl.sessions.Entries[realm], nil } // GetSessionFromRealm returns the session for the realm provided. func (cl *Client) GetSessionFromRealm(realm string) (sess *session, err error) { cl.sessions.mux.RLock() s, ok := cl.sessions.Entries[realm] cl.sessions.mux.RUnlock() if !ok { // Try to request TGT from trusted remote Realm s, err = cl.getSessionFromRemoteRealm(realm) if err != nil { return } } // Create another session to return to prevent race condition. sess = &session{ Realm: s.Realm, AuthTime: s.AuthTime, EndTime: s.EndTime, RenewTill: s.RenewTill, TGT: s.TGT, SessionKey: s.SessionKey, SessionKeyExpiration: s.SessionKeyExpiration, } return } // GetSessionFromPrincipalName returns the session for the realm of the principal provided. func (cl *Client) GetSessionFromPrincipalName(spn types.PrincipalName) (*session, error) { realm := cl.Config.ResolveRealm(spn.NameString[len(spn.NameString)-1]) return cl.GetSessionFromRealm(realm) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/000077500000000000000000000000001362537225700220225ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/hosts.go000066400000000000000000000065171362537225700235220ustar00rootroot00000000000000package config import ( "fmt" "math/rand" "net" "strconv" "strings" "gopkg.in/jcmturner/dnsutils.v1" ) // GetKDCs returns the count of KDCs available and a map of KDC host names keyed on preference order. func (c *Config) GetKDCs(realm string, tcp bool) (int, map[int]string, error) { if realm == "" { realm = c.LibDefaults.DefaultRealm } kdcs := make(map[int]string) var count int // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf. if c.LibDefaults.DNSLookupKDC { proto := "udp" if tcp { proto = "tcp" } c, addrs, err := dnsutils.OrderedSRV("kerberos", proto, realm) if err != nil { return count, kdcs, err } if len(addrs) < 1 { return count, kdcs, fmt.Errorf("no KDC SRV records found for realm %s", realm) } count = c for k, v := range addrs { kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port)) } } else { // Get the KDCs from the krb5.conf an order them randomly for preference. var ks []string for _, r := range c.Realms { if r.Realm == realm { ks = r.KDC break } } count = len(ks) if count < 1 { return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm) } kdcs = randServOrder(ks) } return count, kdcs, nil } // GetKpasswdServers returns the count of kpasswd servers available and a map of kpasswd host names keyed on preference order. // https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms - see kpasswd_server section func (c *Config) GetKpasswdServers(realm string, tcp bool) (int, map[int]string, error) { kdcs := make(map[int]string) var count int // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf. if c.LibDefaults.DNSLookupKDC { proto := "udp" if tcp { proto = "tcp" } c, addrs, err := dnsutils.OrderedSRV("kpasswd", proto, realm) if err != nil { return count, kdcs, err } if c < 1 { c, addrs, err = dnsutils.OrderedSRV("kerberos-adm", proto, realm) if err != nil { return count, kdcs, err } } if len(addrs) < 1 { return count, kdcs, fmt.Errorf("no kpasswd or kadmin SRV records found for realm %s", realm) } count = c for k, v := range addrs { kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port)) } } else { // Get the KDCs from the krb5.conf an order them randomly for preference. var ks []string var ka []string for _, r := range c.Realms { if r.Realm == realm { ks = r.KPasswdServer ka = r.AdminServer break } } if len(ks) < 1 { for _, k := range ka { h, _, err := net.SplitHostPort(k) if err != nil { continue } ks = append(ks, h+":464") } } count = len(ks) if count < 1 { return count, kdcs, fmt.Errorf("no kpasswd or kadmin defined in configuration for realm %s", realm) } kdcs = randServOrder(ks) } return count, kdcs, nil } func randServOrder(ks []string) map[int]string { kdcs := make(map[int]string) count := len(ks) i := 1 if count > 1 { l := len(ks) for l > 0 { ri := rand.Intn(l) kdcs[i] = ks[ri] if l > 1 { // Remove the entry from the source slice by swapping with the last entry and truncating ks[len(ks)-1], ks[ri] = ks[ri], ks[len(ks)-1] ks = ks[:len(ks)-1] l = len(ks) } else { l = 0 } i++ } } else { kdcs[i] = ks[0] } return kdcs } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/krb5conf.go000066400000000000000000000524411362537225700240700ustar00rootroot00000000000000// Package config implements KRB5 client and service configuration as described at https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html package config import ( "bufio" "encoding/hex" "errors" "fmt" "io" "net" "os" "os/user" "regexp" "strconv" "strings" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // Config represents the KRB5 configuration. type Config struct { LibDefaults *LibDefaults Realms []Realm DomainRealm DomainRealm //CaPaths //AppDefaults //Plugins } // WeakETypeList is a list of encryption types that have been deemed weak. const WeakETypeList = "des-cbc-crc des-cbc-md4 des-cbc-md5 des-cbc-raw des3-cbc-raw des-hmac-sha1 arcfour-hmac-exp rc4-hmac-exp arcfour-hmac-md5-exp des" // NewConfig creates a new config struct instance. func NewConfig() *Config { d := make(DomainRealm) return &Config{ LibDefaults: newLibDefaults(), DomainRealm: d, } } // LibDefaults represents the [libdefaults] section of the configuration. type LibDefaults struct { AllowWeakCrypto bool //default false // ap_req_checksum_type int //unlikely to support this Canonicalize bool //default false CCacheType int //default is 4. unlikely to implement older Clockskew time.Duration //max allowed skew in seconds, default 300 //Default_ccache_name string // default /tmp/krb5cc_%{uid} //Not implementing as will hold in memory DefaultClientKeytabName string //default /usr/local/var/krb5/user/%{euid}/client.keytab DefaultKeytabName string //default /etc/krb5.keytab DefaultRealm string DefaultTGSEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 DefaultTktEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 DefaultTGSEnctypeIDs []int32 //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 DefaultTktEnctypeIDs []int32 //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 DNSCanonicalizeHostname bool //default true DNSLookupKDC bool //default false DNSLookupRealm bool ExtraAddresses []net.IP //Not implementing yet Forwardable bool //default false IgnoreAcceptorHostname bool //default false K5LoginAuthoritative bool //default false K5LoginDirectory string //default user's home directory. Must be owned by the user or root KDCDefaultOptions asn1.BitString //default 0x00000010 (KDC_OPT_RENEWABLE_OK) KDCTimeSync int //default 1 //kdc_req_checksum_type int //unlikely to implement as for very old KDCs NoAddresses bool //default true PermittedEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 PermittedEnctypeIDs []int32 //plugin_base_dir string //not supporting plugins PreferredPreauthTypes []int //default “17, 16, 15, 14”, which forces libkrb5 to attempt to use PKINIT if it is supported Proxiable bool //default false RDNS bool //default true RealmTryDomains int //default -1 RenewLifetime time.Duration //default 0 SafeChecksumType int //default 8 TicketLifetime time.Duration //default 1 day UDPPreferenceLimit int // 1 means to always use tcp. MIT krb5 has a default value of 1465, and it prevents user setting more than 32700. VerifyAPReqNofail bool //default false } // Create a new LibDefaults struct. func newLibDefaults() *LibDefaults { uid := "0" var hdir string usr, _ := user.Current() if usr != nil { uid = usr.Uid hdir = usr.HomeDir } opts := asn1.BitString{} opts.Bytes, _ = hex.DecodeString("00000010") opts.BitLength = len(opts.Bytes) * 8 return &LibDefaults{ CCacheType: 4, Clockskew: time.Duration(300) * time.Second, DefaultClientKeytabName: fmt.Sprintf("/usr/local/var/krb5/user/%s/client.keytab", uid), DefaultKeytabName: "/etc/krb5.keytab", DefaultTGSEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, DefaultTktEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, DNSCanonicalizeHostname: true, K5LoginDirectory: hdir, KDCDefaultOptions: opts, KDCTimeSync: 1, NoAddresses: true, PermittedEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, RDNS: true, RealmTryDomains: -1, SafeChecksumType: 8, TicketLifetime: time.Duration(24) * time.Hour, UDPPreferenceLimit: 1465, PreferredPreauthTypes: []int{17, 16, 15, 14}, } } // Parse the lines of the [libdefaults] section of the configuration into the LibDefaults struct. func (l *LibDefaults) parseLines(lines []string) error { for _, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } if strings.Contains(line, "v4_") { return errors.New("v4 configurations are not supported in Realms section") } if !strings.Contains(line, "=") { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } p := strings.Split(line, "=") key := strings.TrimSpace(strings.ToLower(p[0])) switch key { case "allow_weak_crypto": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.AllowWeakCrypto = v case "canonicalize": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.Canonicalize = v case "ccache_type": p[1] = strings.TrimSpace(p[1]) v, err := strconv.ParseUint(p[1], 10, 32) if err != nil || v < 0 || v > 4 { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.CCacheType = int(v) case "clockskew": d, err := parseDuration(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.Clockskew = d case "default_client_keytab_name": l.DefaultClientKeytabName = strings.TrimSpace(p[1]) case "default_keytab_name": l.DefaultKeytabName = strings.TrimSpace(p[1]) case "default_realm": l.DefaultRealm = strings.TrimSpace(p[1]) case "default_tgs_enctypes": l.DefaultTGSEnctypes = strings.Fields(p[1]) case "default_tkt_enctypes": l.DefaultTktEnctypes = strings.Fields(p[1]) case "dns_canonicalize_hostname": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.DNSCanonicalizeHostname = v case "dns_lookup_kdc": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.DNSLookupKDC = v case "dns_lookup_realm": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.DNSLookupRealm = v case "extra_addresses": ipStr := strings.TrimSpace(p[1]) for _, ip := range strings.Split(ipStr, ",") { if eip := net.ParseIP(ip); eip != nil { l.ExtraAddresses = append(l.ExtraAddresses, eip) } } case "forwardable": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.Forwardable = v case "ignore_acceptor_hostname": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.IgnoreAcceptorHostname = v case "k5login_authoritative": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.K5LoginAuthoritative = v case "k5login_directory": l.K5LoginDirectory = strings.TrimSpace(p[1]) case "kdc_default_options": v := strings.TrimSpace(p[1]) v = strings.Replace(v, "0x", "", -1) b, err := hex.DecodeString(v) if err != nil { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.KDCDefaultOptions.Bytes = b l.KDCDefaultOptions.BitLength = len(b) * 8 case "kdc_timesync": p[1] = strings.TrimSpace(p[1]) v, err := strconv.ParseInt(p[1], 10, 32) if err != nil || v < 0 { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.KDCTimeSync = int(v) case "noaddresses": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.NoAddresses = v case "permitted_enctypes": l.PermittedEnctypes = strings.Fields(p[1]) case "preferred_preauth_types": p[1] = strings.TrimSpace(p[1]) t := strings.Split(p[1], ",") var v []int for _, s := range t { i, err := strconv.ParseInt(s, 10, 32) if err != nil { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } v = append(v, int(i)) } l.PreferredPreauthTypes = v case "proxiable": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.Proxiable = v case "rdns": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.RDNS = v case "realm_try_domains": p[1] = strings.TrimSpace(p[1]) v, err := strconv.ParseInt(p[1], 10, 32) if err != nil || v < -1 { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.RealmTryDomains = int(v) case "renew_lifetime": d, err := parseDuration(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.RenewLifetime = d case "safe_checksum_type": p[1] = strings.TrimSpace(p[1]) v, err := strconv.ParseInt(p[1], 10, 32) if err != nil || v < 0 { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.SafeChecksumType = int(v) case "ticket_lifetime": d, err := parseDuration(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.TicketLifetime = d case "udp_preference_limit": p[1] = strings.TrimSpace(p[1]) v, err := strconv.ParseUint(p[1], 10, 32) if err != nil || v > 32700 { return fmt.Errorf("libdefaults configuration line invalid: %s", line) } l.UDPPreferenceLimit = int(v) case "verify_ap_req_nofail": v, err := parseBoolean(p[1]) if err != nil { return fmt.Errorf("libdefaults configuration line invalid. %v: %s", err, line) } l.VerifyAPReqNofail = v default: //Ignore the line continue } } l.DefaultTGSEnctypeIDs = parseETypes(l.DefaultTGSEnctypes, l.AllowWeakCrypto) l.DefaultTktEnctypeIDs = parseETypes(l.DefaultTktEnctypes, l.AllowWeakCrypto) l.PermittedEnctypeIDs = parseETypes(l.PermittedEnctypes, l.AllowWeakCrypto) return nil } // Realm represents an entry in the [realms] section of the configuration. type Realm struct { Realm string AdminServer []string //auth_to_local //Not implementing for now //auth_to_local_names //Not implementing for now DefaultDomain string KDC []string KPasswdServer []string //default admin_server:464 MasterKDC []string } // Parse the lines of a [realms] entry into the Realm struct. func (r *Realm) parseLines(name string, lines []string) error { r.Realm = name var adminServerFinal bool var KDCFinal bool var kpasswdServerFinal bool var masterKDCFinal bool for _, line := range lines { if strings.TrimSpace(line) == "" { continue } if !strings.Contains(line, "=") { return fmt.Errorf("realm configuration line invalid: %s", line) } p := strings.Split(line, "=") key := strings.TrimSpace(strings.ToLower(p[0])) v := strings.TrimSpace(p[1]) switch key { case "admin_server": appendUntilFinal(&r.AdminServer, v, &adminServerFinal) case "default_domain": r.DefaultDomain = v case "kdc": if !strings.Contains(v, ":") { // No port number specified default to 88 if strings.HasSuffix(v, `*`) { v = strings.TrimSpace(strings.TrimSuffix(v, `*`)) + ":88*" } else { v = strings.TrimSpace(v) + ":88" } } appendUntilFinal(&r.KDC, v, &KDCFinal) case "kpasswd_server": appendUntilFinal(&r.KPasswdServer, v, &kpasswdServerFinal) case "master_kdc": appendUntilFinal(&r.MasterKDC, v, &masterKDCFinal) default: //Ignore the line continue } } //default for Kpasswd_server = admin_server:464 if len(r.KPasswdServer) < 1 { for _, a := range r.AdminServer { s := strings.Split(a, ":") r.KPasswdServer = append(r.KPasswdServer, s[0]+":464") } } return nil } // Parse the lines of the [realms] section of the configuration into an slice of Realm structs. func parseRealms(lines []string) ([]Realm, error) { var realms []Realm start := -1 var name string for i, l := range lines { if strings.TrimSpace(l) == "" { continue } if strings.Contains(l, "v4_") { return nil, errors.New("v4 configurations are not supported in Realms section") } if strings.Contains(l, "{") { if start >= 0 { // already started a block!!! return nil, errors.New("invalid Realms section in configuration") } start = i if !strings.Contains(l, "=") { return nil, fmt.Errorf("realm configuration line invalid: %s", l) } p := strings.Split(l, "=") name = strings.TrimSpace(p[0]) } if strings.Contains(l, "}") { if start < 0 { // but not started a block!!! return nil, errors.New("invalid Realms section in configuration") } var r Realm r.parseLines(name, lines[start+1:i]) realms = append(realms, r) start = -1 } } return realms, nil } // DomainRealm maps the domains to realms representing the [domain_realm] section of the configuration. type DomainRealm map[string]string // Parse the lines of the [domain_realm] section of the configuration and add to the mapping. func (d *DomainRealm) parseLines(lines []string) error { for _, line := range lines { if strings.TrimSpace(line) == "" { continue } if !strings.Contains(line, "=") { return fmt.Errorf("realm configuration line invalid: %s", line) } p := strings.Split(line, "=") domain := strings.TrimSpace(strings.ToLower(p[0])) realm := strings.TrimSpace(strings.ToUpper(p[1])) d.addMapping(domain, realm) } return nil } // Add a domain to realm mapping. func (d *DomainRealm) addMapping(domain, realm string) { (*d)[domain] = realm } // Delete a domain to realm mapping. func (d *DomainRealm) deleteMapping(domain, realm string) { delete(*d, domain) } // ResolveRealm resolves the kerberos realm for the specified domain name from the domain to realm mapping. // The most specific mapping is returned. func (c *Config) ResolveRealm(domainName string) string { domainName = strings.TrimSuffix(domainName, ".") // Try to match the entire hostname first if r, ok := c.DomainRealm[domainName]; ok { return r } // Try to match all DNS domain parts periods := strings.Count(domainName, ".") + 1 for i := 2; i <= periods; i++ { z := strings.SplitN(domainName, ".", i) if r, ok := c.DomainRealm["."+z[len(z)-1]]; ok { return r } } return c.LibDefaults.DefaultRealm } // Load the KRB5 configuration from the specified file path. func Load(cfgPath string) (*Config, error) { fh, err := os.Open(cfgPath) if err != nil { return nil, errors.New("configuration file could not be opened: " + cfgPath + " " + err.Error()) } defer fh.Close() scanner := bufio.NewScanner(fh) return NewConfigFromScanner(scanner) } // NewConfigFromString creates a new Config struct from a string. func NewConfigFromString(s string) (*Config, error) { reader := strings.NewReader(s) return NewConfigFromReader(reader) } // NewConfigFromReader creates a new Config struct from an io.Reader. func NewConfigFromReader(r io.Reader) (*Config, error) { scanner := bufio.NewScanner(r) return NewConfigFromScanner(scanner) } // NewConfigFromScanner creates a new Config struct from a bufio.Scanner. func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) { c := NewConfig() sections := make(map[int]string) var sectionLineNum []int var lines []string for scanner.Scan() { // Skip comments and blank lines if matched, _ := regexp.MatchString(`^\s*(#|;|\n)`, scanner.Text()); matched { continue } if matched, _ := regexp.MatchString(`^\s*\[libdefaults\]\s*`, scanner.Text()); matched { sections[len(lines)] = "libdefaults" sectionLineNum = append(sectionLineNum, len(lines)) continue } if matched, _ := regexp.MatchString(`^\s*\[realms\]\s*`, scanner.Text()); matched { sections[len(lines)] = "realms" sectionLineNum = append(sectionLineNum, len(lines)) continue } if matched, _ := regexp.MatchString(`^\s*\[domain_realm\]\s*`, scanner.Text()); matched { sections[len(lines)] = "domain_realm" sectionLineNum = append(sectionLineNum, len(lines)) continue } if matched, _ := regexp.MatchString(`^\s*\[.*\]\s*`, scanner.Text()); matched { sections[len(lines)] = "unknown_section" sectionLineNum = append(sectionLineNum, len(lines)) continue } lines = append(lines, scanner.Text()) } for i, start := range sectionLineNum { var end int if i+1 >= len(sectionLineNum) { end = len(lines) } else { end = sectionLineNum[i+1] } switch section := sections[start]; section { case "libdefaults": err := c.LibDefaults.parseLines(lines[start:end]) if err != nil { return nil, fmt.Errorf("error processing libdefaults section: %v", err) } case "realms": realms, err := parseRealms(lines[start:end]) if err != nil { return nil, fmt.Errorf("error processing realms section: %v", err) } c.Realms = realms case "domain_realm": err := c.DomainRealm.parseLines(lines[start:end]) if err != nil { return nil, fmt.Errorf("error processing domaain_realm section: %v", err) } default: continue } } return c, nil } // Parse a space delimited list of ETypes into a list of EType numbers optionally filtering out weak ETypes. func parseETypes(s []string, w bool) []int32 { var eti []int32 for _, et := range s { if !w { var weak bool for _, wet := range strings.Fields(WeakETypeList) { if et == wet { weak = true break } } if weak { continue } } i := etypeID.EtypeSupported(et) if i != 0 { eti = append(eti, i) } } return eti } // Parse a time duration string in the configuration to a golang time.Duration. func parseDuration(s string) (time.Duration, error) { s = strings.Replace(strings.TrimSpace(s), " ", "", -1) // handle Nd[NmNs] if strings.Contains(s, "d") { ds := strings.SplitN(s, "d", 2) dn, err := strconv.ParseUint(ds[0], 10, 32) if err != nil { return time.Duration(0), errors.New("invalid time duration") } d := time.Duration(dn*24) * time.Hour if ds[1] != "" { dp, err := time.ParseDuration(ds[1]) if err != nil { return time.Duration(0), errors.New("invalid time duration") } d = d + dp } return d, nil } // handle Nm[Ns] d, err := time.ParseDuration(s) if err == nil { return d, nil } // handle N v, err := strconv.ParseUint(s, 10, 32) if err == nil && v > 0 { return time.Duration(v) * time.Second, nil } // handle h:m[:s] if strings.Contains(s, ":") { t := strings.Split(s, ":") if 2 > len(t) || len(t) > 3 { return time.Duration(0), errors.New("invalid time duration value") } var i []int for _, n := range t { j, err := strconv.ParseInt(n, 10, 16) if err != nil { return time.Duration(0), errors.New("invalid time duration value") } i = append(i, int(j)) } d := time.Duration(i[0])*time.Hour + time.Duration(i[1])*time.Minute if len(i) == 3 { d = d + time.Duration(i[2])*time.Second } return d, nil } return time.Duration(0), errors.New("invalid time duration value") } // Parse possible boolean values to golang bool. func parseBoolean(s string) (bool, error) { s = strings.TrimSpace(s) v, err := strconv.ParseBool(s) if err == nil { return v, nil } switch strings.ToLower(s) { case "yes": return true, nil case "y": return true, nil case "no": return false, nil case "n": return false, nil } return false, errors.New("invalid boolean value") } // Parse array of strings but stop if an asterisk is placed at the end of a line. func appendUntilFinal(s *[]string, value string, final *bool) { if *final { return } if last := len(value) - 1; last >= 0 && value[last] == '*' { *final = true value = value[:len(value)-1] } *s = append(*s, value) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/config/krb5conf_test.go000066400000000000000000000364561362537225700251370ustar00rootroot00000000000000package config import ( "io/ioutil" "os" "testing" "time" "github.com/stretchr/testify/assert" ) const ( krb5Conf = ` [logging] default = FILE:/var/log/kerberos/krb5libs.log kdc = FILE:/var/log/kerberos/krb5kdc.log admin_server = FILE:/var/log/kerberos/kadmind.log [libdefaults] default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false #dns_lookup_kdc = true ;dns_lookup_kdc = true #dns_lookup_kdc = true ;dns_lookup_kdc = true ticket_lifetime = 10h forwardable = yes default_keytab_name = FILE:/etc/krb5.keytab default_client_keytab_name = FILE:/home/gokrb5/client.keytab default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 [realms] TEST.GOKRB5 = { kdc = 10.80.88.88:88 kdc = assume.port.num kdc = some.other.port:1234 kdc = 10.80.88.88* kdc = 10.1.2.3.4:88 admin_server = 10.80.88.88:749 default_domain = test.gokrb5 } EXAMPLE.COM = { kdc = kerberos.example.com kdc = kerberos-1.example.com admin_server = kerberos.example.com auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*// } [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 .example.com = EXAMPLE.COM hostname1.example.com = EXAMPLE.COM hostname2.example.com = TEST.GOKRB5 [appdefaults] pam = { debug = false ticket_lifetime = 36000 renew_lifetime = 36000 forwardable = true krb4_convert = false } ` krb5Conf2 = ` [logging] default = FILE:/var/log/kerberos/krb5libs.log kdc = FILE:/var/log/kerberos/krb5kdc.log admin_server = FILE:/var/log/kerberos/kadmind.log [libdefaults] noaddresses = true default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false #dns_lookup_kdc = true ;dns_lookup_kdc = true #dns_lookup_kdc = true ;dns_lookup_kdc = true ticket_lifetime = 10h forwardable = yes default_keytab_name = FILE:/etc/krb5.keytab default_client_keytab_name = FILE:/home/gokrb5/client.keytab default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 [appdefaults] pam = { debug = false ticket_lifetime = 36000 renew_lifetime = 36000 forwardable = true krb4_convert = false } [realms] TEST.GOKRB5 = { kdc = 10.80.88.88:88 kdc = assume.port.num kdc = some.other.port:1234 kdc = 10.80.88.88* kdc = 10.1.2.3.4:88 admin_server = 10.80.88.88:749 default_domain = test.gokrb5 } EXAMPLE.COM = { kdc = kerberos.example.com kdc = kerberos-1.example.com admin_server = kerberos.example.com } ` krb5ConfNoBlankLines = ` [logging] default = FILE:/var/log/kerberos/krb5libs.log kdc = FILE:/var/log/kerberos/krb5kdc.log admin_server = FILE:/var/log/kerberos/kadmind.log [libdefaults] default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false #dns_lookup_kdc = true ;dns_lookup_kdc = true #dns_lookup_kdc = true ;dns_lookup_kdc = true ticket_lifetime = 10h forwardable = yes default_keytab_name = FILE:/etc/krb5.keytab default_client_keytab_name = FILE:/home/gokrb5/client.keytab default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 [realms] TEST.GOKRB5 = { kdc = 10.80.88.88:88 kdc = assume.port.num kdc = some.other.port:1234 kdc = 10.80.88.88* kdc = 10.1.2.3.4:88 admin_server = 10.80.88.88:749 default_domain = test.gokrb5 } EXAMPLE.COM = { kdc = kerberos.example.com kdc = kerberos-1.example.com admin_server = kerberos.example.com auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*// } [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 ` krb5ConfTabs = ` [logging] default = FILE:/var/log/kerberos/krb5libs.log kdc = FILE:/var/log/kerberos/krb5kdc.log admin_server = FILE:/var/log/kerberos/kadmind.log [libdefaults] default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false #dns_lookup_kdc = true ;dns_lookup_kdc = true #dns_lookup_kdc = true ;dns_lookup_kdc = true ticket_lifetime = 10h forwardable = yes default_keytab_name = FILE:/etc/krb5.keytab default_client_keytab_name = FILE:/home/gokrb5/client.keytab default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 [realms] TEST.GOKRB5 = { kdc = 10.80.88.88:88 kdc = assume.port.num kdc = some.other.port:1234 kdc = 10.80.88.88* kdc = 10.1.2.3.4:88 admin_server = 10.80.88.88:749 default_domain = test.gokrb5 } EXAMPLE.COM = { kdc = kerberos.example.com kdc = kerberos-1.example.com admin_server = kerberos.example.com auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*// } [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 .example.com = EXAMPLE.COM hostname1.example.com = EXAMPLE.COM hostname2.example.com = TEST.GOKRB5 [appdefaults] pam = { debug = false ticket_lifetime = 36000 renew_lifetime = 36000 forwardable = true krb4_convert = false }` ) func TestLoad(t *testing.T) { t.Parallel() cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5.conf") defer os.Remove(cf.Name()) cf.WriteString(krb5Conf) c, err := Load(cf.Name()) if err != nil { t.Fatalf("Error loading config: %v", err) } assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected") assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected") assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected") assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected") assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected") assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected") assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected") assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd") assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd") assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd") assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected") } func TestLoad2(t *testing.T) { t.Parallel() c, err := NewConfigFromString(krb5Conf2) if err != nil { t.Fatalf("Error loading config: %v", err) } assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected") assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected") assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected") assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected") assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected") assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected") assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected") assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd") assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd") assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd") assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected") assert.True(t, c.LibDefaults.NoAddresses, "No address not set as true") } func TestLoadNoBlankLines(t *testing.T) { t.Parallel() c, err := NewConfigFromString(krb5ConfNoBlankLines) if err != nil { t.Fatalf("Error loading config: %v", err) } assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected") assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected") assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected") assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected") assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected") assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected") assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected") assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd") assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd") assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd") assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected") } func TestLoadTabs(t *testing.T) { t.Parallel() cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5.conf") defer os.Remove(cf.Name()) cf.WriteString(krb5ConfTabs) c, err := Load(cf.Name()) if err != nil { t.Fatalf("Error loading config: %v", err) } assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected") assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected") assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected") assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected") assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected") assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected") assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected") assert.Equal(t, 2, len(c.Realms), "Number of realms not as expected") assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd") assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd") assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd") assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd") assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected") assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected") } func TestParseDuration(t *testing.T) { t.Parallel() // https://web.mit.edu/kerberos/krb5-1.12/doc/basic/date_format.html#duration hms, _ := time.ParseDuration("12h30m15s") hm, _ := time.ParseDuration("12h30m") h, _ := time.ParseDuration("12h") var tests = []struct { timeStr string duration time.Duration }{ {"100", time.Duration(100) * time.Second}, {"12:30", hm}, {"12:30:15", hms}, {"1d12h30m15s", time.Duration(24)*time.Hour + hms}, {"1d12h30m", time.Duration(24)*time.Hour + hm}, {"1d12h", time.Duration(24)*time.Hour + h}, {"1d", time.Duration(24) * time.Hour}, } for _, test := range tests { d, err := parseDuration(test.timeStr) if err != nil { t.Errorf("error parsing %s: %v", test.timeStr, err) } assert.Equal(t, test.duration, d, "Duration not as expected for: "+test.timeStr) } } func TestResolveRealm(t *testing.T) { t.Parallel() c, err := NewConfigFromString(krb5Conf) if err != nil { t.Fatalf("Error loading config: %v", err) } tests := []struct { domainName string want string }{ {"unknown.com", "TEST.GOKRB5"}, {"hostname1.example.com", "EXAMPLE.COM"}, {"hostname2.example.com", "TEST.GOKRB5"}, {"one.two.three.example.com", "EXAMPLE.COM"}, {".test.gokrb5", "TEST.GOKRB5"}, } for _, tt := range tests { t.Run(tt.domainName, func(t *testing.T) { if got := c.ResolveRealm(tt.domainName); got != tt.want { t.Errorf("config.ResolveRealm() = %v, want %v", got, tt.want) } }) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/000077500000000000000000000000001362537225700230525ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache.go000066400000000000000000000211261362537225700246110ustar00rootroot00000000000000package credentials import ( "bytes" "encoding/binary" "errors" "io/ioutil" "strings" "time" "unsafe" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( headerFieldTagKDCOffset = 1 ) // CCache is the file credentials cache as define here: https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html type CCache struct { Version uint8 Header header DefaultPrincipal principal Credentials []credential Path string } type header struct { length uint16 fields []headerField } type headerField struct { tag uint16 length uint16 value []byte } // Credential cache entry principal struct. type principal struct { Realm string PrincipalName types.PrincipalName } type credential struct { Client principal Server principal Key types.EncryptionKey AuthTime time.Time StartTime time.Time EndTime time.Time RenewTill time.Time IsSKey bool TicketFlags asn1.BitString Addresses []types.HostAddress AuthData []types.AuthorizationDataEntry Ticket []byte SecondTicket []byte } // LoadCCache loads a credential cache file into a CCache type. func LoadCCache(cpath string) (CCache, error) { k, err := ioutil.ReadFile(cpath) if err != nil { return CCache{}, err } c, err := ParseCCache(k) c.Path = cpath return c, err } // ParseCCache byte slice of credential cache data into CCache type. func ParseCCache(b []byte) (c CCache, err error) { p := 0 //The first byte of the file always has the value 5 if int8(b[p]) != 5 { err = errors.New("Invalid credential cache data. First byte does not equal 5") return } p++ //Get credential cache version //The second byte contains the version number (1 to 4) c.Version = uint8(b[p]) if c.Version < 1 || c.Version > 4 { err = errors.New("Invalid credential cache data. Keytab version is not within 1 to 4") if err != nil { return } } p++ //Version 1 or 2 of the file format uses native byte order for integer representations. Versions 3 & 4 always uses big-endian byte order var endian binary.ByteOrder endian = binary.BigEndian if (c.Version == 1 || c.Version == 2) && isNativeEndianLittle() { endian = binary.LittleEndian } if c.Version == 4 { err = parseHeader(b, &p, &c, &endian) if err != nil { return } } c.DefaultPrincipal = parsePrincipal(b, &p, &c, &endian) for p < len(b) { cred, e := parseCredential(b, &p, &c, &endian) if e != nil { err = e return } c.Credentials = append(c.Credentials, cred) } return } func parseHeader(b []byte, p *int, c *CCache, e *binary.ByteOrder) error { if c.Version != 4 { return errors.New("Credentials cache version is not 4 so there is no header to parse.") } h := header{} h.length = uint16(readInt16(b, p, e)) for *p <= int(h.length) { f := headerField{} f.tag = uint16(readInt16(b, p, e)) f.length = uint16(readInt16(b, p, e)) f.value = b[*p : *p+int(f.length)] *p += int(f.length) if !f.valid() { return errors.New("Invalid credential cache header found") } h.fields = append(h.fields, f) } c.Header = h return nil } // Parse the Keytab bytes of a principal into a Keytab entry's principal. func parsePrincipal(b []byte, p *int, c *CCache, e *binary.ByteOrder) (princ principal) { if c.Version != 1 { //Name Type is omitted in version 1 princ.PrincipalName.NameType = int32(readInt32(b, p, e)) } nc := int(readInt32(b, p, e)) if c.Version == 1 { //In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2 nc-- } lenRealm := readInt32(b, p, e) princ.Realm = string(readBytes(b, p, int(lenRealm), e)) for i := 0; i < int(nc); i++ { l := readInt32(b, p, e) princ.PrincipalName.NameString = append(princ.PrincipalName.NameString, string(readBytes(b, p, int(l), e))) } return princ } func parseCredential(b []byte, p *int, c *CCache, e *binary.ByteOrder) (cred credential, err error) { cred.Client = parsePrincipal(b, p, c, e) cred.Server = parsePrincipal(b, p, c, e) key := types.EncryptionKey{} key.KeyType = int32(readInt16(b, p, e)) if c.Version == 3 { //repeated twice in version 3 key.KeyType = int32(readInt16(b, p, e)) } key.KeyValue = readData(b, p, e) cred.Key = key cred.AuthTime = readTimestamp(b, p, e) cred.StartTime = readTimestamp(b, p, e) cred.EndTime = readTimestamp(b, p, e) cred.RenewTill = readTimestamp(b, p, e) if ik := readInt8(b, p, e); ik == 0 { cred.IsSKey = false } else { cred.IsSKey = true } cred.TicketFlags = types.NewKrbFlags() cred.TicketFlags.Bytes = readBytes(b, p, 4, e) l := int(readInt32(b, p, e)) cred.Addresses = make([]types.HostAddress, l, l) for i := range cred.Addresses { cred.Addresses[i] = readAddress(b, p, e) } l = int(readInt32(b, p, e)) cred.AuthData = make([]types.AuthorizationDataEntry, l, l) for i := range cred.AuthData { cred.AuthData[i] = readAuthDataEntry(b, p, e) } cred.Ticket = readData(b, p, e) cred.SecondTicket = readData(b, p, e) return } // GetClientPrincipalName returns a PrincipalName type for the client the credentials cache is for. func (c *CCache) GetClientPrincipalName() types.PrincipalName { return c.DefaultPrincipal.PrincipalName } // GetClientRealm returns the reals of the client the credentials cache is for. func (c *CCache) GetClientRealm() string { return c.DefaultPrincipal.Realm } // GetClientCredentials returns a Credentials object representing the client of the credentials cache. func (c *CCache) GetClientCredentials() *Credentials { return &Credentials{ Username: c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(), Realm: c.GetClientRealm(), CName: c.DefaultPrincipal.PrincipalName, } } // Contains tests if the cache contains a credential for the provided server PrincipalName func (c *CCache) Contains(p types.PrincipalName) bool { for _, cred := range c.Credentials { if cred.Server.PrincipalName.Equal(p) { return true } } return false } // GetEntry returns a specific credential for the PrincipalName provided. func (c *CCache) GetEntry(p types.PrincipalName) (credential, bool) { var cred credential var found bool for i := range c.Credentials { if c.Credentials[i].Server.PrincipalName.Equal(p) { cred = c.Credentials[i] found = true break } } if !found { return cred, false } return cred, true } // GetEntries filters out configuration entries an returns a slice of credentials. func (c *CCache) GetEntries() []credential { var creds []credential for _, cred := range c.Credentials { // Filter out configuration entries if strings.HasPrefix(cred.Server.Realm, "X-CACHECONF") { continue } creds = append(creds, cred) } return creds } func (h *headerField) valid() bool { // Done as a switch in case other tag values are added in the future. switch h.tag { case headerFieldTagKDCOffset: if h.length != 8 || len(h.value) != 8 { return false } return true } return false } func readData(b []byte, p *int, e *binary.ByteOrder) []byte { l := readInt32(b, p, e) return readBytes(b, p, int(l), e) } func readAddress(b []byte, p *int, e *binary.ByteOrder) types.HostAddress { a := types.HostAddress{} a.AddrType = int32(readInt16(b, p, e)) a.Address = readData(b, p, e) return a } func readAuthDataEntry(b []byte, p *int, e *binary.ByteOrder) types.AuthorizationDataEntry { a := types.AuthorizationDataEntry{} a.ADType = int32(readInt16(b, p, e)) a.ADData = readData(b, p, e) return a } // Read bytes representing a timestamp. func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time { return time.Unix(int64(readInt32(b, p, e)), 0) } // Read bytes representing an eight bit integer. func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) { buf := bytes.NewBuffer(b[*p : *p+1]) binary.Read(buf, *e, &i) *p++ return } // Read bytes representing a sixteen bit integer. func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) { buf := bytes.NewBuffer(b[*p : *p+2]) binary.Read(buf, *e, &i) *p += 2 return } // Read bytes representing a thirty two bit integer. func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) { buf := bytes.NewBuffer(b[*p : *p+4]) binary.Read(buf, *e, &i) *p += 4 return } func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte { buf := bytes.NewBuffer(b[*p : *p+s]) r := make([]byte, s) binary.Read(buf, *e, &r) *p += s return r } func isNativeEndianLittle() bool { var x = 0x012345678 var p = unsafe.Pointer(&x) var bp = (*[4]byte)(p) var endian bool if 0x01 == bp[0] { endian = false } else if (0x78 & 0xff) == (bp[0] & 0xff) { endian = true } else { // Default to big endian endian = false } return endian } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache_integration_test.go000066400000000000000000000071041362537225700302530ustar00rootroot00000000000000// +build integration // To turn on this test use -tags=integration in go test command package credentials import ( "bufio" "bytes" "fmt" "io" "os" "os/exec" "os/user" "sync" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( kinitCmd = "kinit" kvnoCmd = "kvno" klistCmd = "klist" spn = "HTTP/host.test.gokrb5" ) type output struct { buf *bytes.Buffer lines []string *sync.Mutex } func newOutput() *output { return &output{ buf: &bytes.Buffer{}, lines: []string{}, Mutex: &sync.Mutex{}, } } func (rw *output) Write(p []byte) (int, error) { rw.Lock() defer rw.Unlock() return rw.buf.Write(p) } func (rw *output) Lines() []string { rw.Lock() defer rw.Unlock() s := bufio.NewScanner(rw.buf) for s.Scan() { rw.lines = append(rw.lines, s.Text()) } return rw.lines } func login() error { file, err := os.Create("/etc/krb5.conf") if err != nil { return fmt.Errorf("cannot open krb5.conf: %v", err) } defer file.Close() fmt.Fprintf(file, testdata.TEST_KRB5CONF) cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5") stdinR, stdinW := io.Pipe() stderrR, stderrW := io.Pipe() cmd.Stdin = stdinR cmd.Stderr = stderrW err = cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kinitCmd, err) } go func() { io.WriteString(stdinW, "passwordvalue") stdinW.Close() }() errBuf := new(bytes.Buffer) go func() { io.Copy(errBuf, stderrR) stderrR.Close() }() err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes())) } return nil } func getServiceTkt() error { cmd := exec.Command(kvnoCmd, spn) err := cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kvnoCmd, err) } err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err) } return nil } func klist() ([]string, error) { cmd := exec.Command(klistCmd, "-Aef") stdout := newOutput() cmd.Stdout = stdout err := cmd.Start() if err != nil { return []string{}, fmt.Errorf("could not start %s command: %v", klistCmd, err) } err = cmd.Wait() if err != nil { return []string{}, fmt.Errorf("%s did not run successfully: %v", klistCmd, err) } return stdout.Lines(), nil } func loadCCache() (CCache, error) { usr, _ := user.Current() cpath := "/tmp/krb5cc_" + usr.Uid return LoadCCache(cpath) } func TestLoadCCache(t *testing.T) { err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } pn := c.GetClientPrincipalName() assert.Equal(t, "testuser1", pn.GetPrincipalNameString(), "principal not as expected") assert.Equal(t, "TEST.GOKRB5", c.GetClientRealm(), "realm not as expected") } func TestCCacheEntries(t *testing.T) { err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } err = getServiceTkt() if err != nil { t.Fatalf("error getting service ticket: %v", err) } clist, _ := klist() t.Log("OS Creds Cache contents:") for _, l := range clist { t.Log(l) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } creds := c.GetEntries() var found bool n := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn) for _, cred := range creds { if cred.Server.PrincipalName.Equal(n) { found = true break } } if !found { t.Errorf("Entry for %s not found in CCache", spn) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/ccache_test.go000066400000000000000000000075541362537225700256610ustar00rootroot00000000000000package credentials import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestParse(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } assert.Equal(t, uint8(4), c.Version, "Version not as expected") assert.Equal(t, 1, len(c.Header.fields), "Number of header fields not as expected") assert.Equal(t, uint16(1), c.Header.fields[0].tag, "Header tag not as expected") assert.Equal(t, uint16(8), c.Header.fields[0].length, "Length of header not as expected") assert.Equal(t, "TEST.GOKRB5", c.DefaultPrincipal.Realm, "Default client principal realm not as expected") assert.Equal(t, "testuser1", c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(), "Default client principaal name not as expected") assert.Equal(t, 3, len(c.Credentials), "Number of credentials not as expected") tgtpn := types.PrincipalName{ NameType: nametype.KRB_NT_SRV_INST, NameString: []string{"krbtgt", "TEST.GOKRB5"}, } assert.True(t, c.Contains(tgtpn), "Cache does not contain TGT credential") httppn := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } assert.True(t, c.Contains(httppn), "Cache does not contain HTTP SPN credential") } func TestCCache_GetClientPrincipalName(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } pn := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"testuser1"}, } assert.Equal(t, pn, c.GetClientPrincipalName(), "Client PrincipalName not as expected") } func TestCCache_GetClientCredentials(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } pn := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"testuser1"}, } cred := c.GetClientCredentials() assert.Equal(t, "TEST.GOKRB5", cred.Realm, "Client realm in credential not as expected") assert.Equal(t, pn, cred.CName, "Client Principal Name not as expected") assert.Equal(t, "testuser1", cred.Username, "Username not as expected") } func TestCCache_GetClientRealm(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } assert.Equal(t, "TEST.GOKRB5", c.GetClientRealm(), "Client realm not as expected") } func TestCCache_GetEntry(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } httppn := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } cred, ok := c.GetEntry(httppn) if !ok { t.Fatal("Could not get entry from CCache as not found") } assert.Equal(t, httppn, cred.Server.PrincipalName, "Credential does not have the right server principal name") } func TestCCache_GetEntries(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.CCACHE_TEST) if err != nil { t.Fatal("Error decoding test data") } c, err := ParseCCache(b) if err != nil { t.Fatalf("Error parsing cache: %v", err) } creds := c.GetEntries() assert.Equal(t, 2, len(creds), "Number of credentials entries not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/credentials.go000066400000000000000000000153131362537225700257010ustar00rootroot00000000000000// Package credentials provides credentials management for Kerberos 5 authentication. package credentials import ( "time" "github.com/hashicorp/go-uuid" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( // AttributeKeyADCredentials assigned number for AD credentials. AttributeKeyADCredentials = 1 ) // Credentials struct for a user. // Contains either a keytab, password or both. // Keytabs are used over passwords if both are defined. type Credentials struct { Username string displayName string Realm string CName types.PrincipalName Keytab keytab.Keytab Password string Attributes map[int]interface{} ValidUntil time.Time authenticated bool human bool authTime time.Time groupMembership map[string]bool sessionID string } // ADCredentials contains information obtained from the PAC. type ADCredentials struct { EffectiveName string FullName string UserID int PrimaryGroupID int LogOnTime time.Time LogOffTime time.Time PasswordLastSet time.Time GroupMembershipSIDs []string LogonDomainName string LogonDomainID string LogonServer string } // NewCredentials creates a new Credentials instance. func NewCredentials(username string, realm string) Credentials { uid, err := uuid.GenerateUUID() if err != nil { uid = "00unique-sess-ions-uuid-unavailable0" } return Credentials{ Username: username, displayName: username, Realm: realm, CName: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, username), Keytab: keytab.NewKeytab(), Attributes: make(map[int]interface{}), sessionID: uid, } } // NewCredentialsFromPrincipal creates a new Credentials instance with the user details provides as a PrincipalName type. func NewCredentialsFromPrincipal(cname types.PrincipalName, realm string) Credentials { uid, err := uuid.GenerateUUID() if err != nil { uid = "00unique-sess-ions-uuid-unavailable0" } return Credentials{ Username: cname.GetPrincipalNameString(), displayName: cname.GetPrincipalNameString(), Realm: realm, CName: cname, Keytab: keytab.NewKeytab(), Attributes: make(map[int]interface{}), groupMembership: make(map[string]bool), sessionID: uid, } } // WithKeytab sets the Keytab in the Credentials struct. func (c *Credentials) WithKeytab(kt keytab.Keytab) *Credentials { c.Keytab = kt return c } // WithPassword sets the password in the Credentials struct. func (c *Credentials) WithPassword(password string) *Credentials { c.Password = password return c } // HasKeytab queries if the Credentials has a keytab defined. func (c *Credentials) HasKeytab() bool { if len(c.Keytab.Entries) > 0 { return true } return false } // SetValidUntil sets the expiry time of the credentials func (c *Credentials) SetValidUntil(t time.Time) { c.ValidUntil = t } // HasPassword queries if the Credentials has a password defined. func (c *Credentials) HasPassword() bool { if c.Password != "" { return true } return false } // SetADCredentials adds ADCredentials attributes to the credentials func (c *Credentials) SetADCredentials(a ADCredentials) { c.Attributes[AttributeKeyADCredentials] = a if a.FullName != "" { c.SetDisplayName(a.FullName) } if a.EffectiveName != "" { c.SetUserName(a.EffectiveName) } if a.LogonDomainName != "" { c.SetDomain(a.LogonDomainName) } for i := range a.GroupMembershipSIDs { c.AddAuthzAttribute(a.GroupMembershipSIDs[i]) } } // Methods to implement goidentity.Identity interface // UserName returns the credential's username. func (c *Credentials) UserName() string { return c.Username } // SetUserName sets the username value on the credential. func (c *Credentials) SetUserName(s string) { c.Username = s } // Domain returns the credential's domain. func (c *Credentials) Domain() string { return c.Realm } // SetDomain sets the domain value on the credential. func (c *Credentials) SetDomain(s string) { c.Realm = s } // DisplayName returns the credential's display name. func (c *Credentials) DisplayName() string { return c.displayName } // SetDisplayName sets the display name value on the credential. func (c *Credentials) SetDisplayName(s string) { c.displayName = s } // Human returns if the credential represents a human or not. func (c *Credentials) Human() bool { return c.human } // SetHuman sets the credential as human. func (c *Credentials) SetHuman(b bool) { c.human = b } // AuthTime returns the time the credential was authenticated. func (c *Credentials) AuthTime() time.Time { return c.authTime } // SetAuthTime sets the time the credential was authenticated. func (c *Credentials) SetAuthTime(t time.Time) { c.authTime = t } // AuthzAttributes returns the credentials authorizing attributes. func (c *Credentials) AuthzAttributes() []string { s := make([]string, len(c.groupMembership)) i := 0 for a := range c.groupMembership { s[i] = a i++ } return s } // Authenticated indicates if the credential has been successfully authenticated or not. func (c *Credentials) Authenticated() bool { return c.authenticated } // SetAuthenticated sets the credential as having been successfully authenticated. func (c *Credentials) SetAuthenticated(b bool) { c.authenticated = b } // AddAuthzAttribute adds an authorization attribute to the credential. func (c *Credentials) AddAuthzAttribute(a string) { c.groupMembership[a] = true } // RemoveAuthzAttribute removes an authorization attribute from the credential. func (c *Credentials) RemoveAuthzAttribute(a string) { if _, ok := c.groupMembership[a]; !ok { return } delete(c.groupMembership, a) } // EnableAuthzAttribute toggles an authorization attribute to an enabled state on the credential. func (c *Credentials) EnableAuthzAttribute(a string) { if enabled, ok := c.groupMembership[a]; ok && !enabled { c.groupMembership[a] = true } } // DisableAuthzAttribute toggles an authorization attribute to a disabled state on the credential. func (c *Credentials) DisableAuthzAttribute(a string) { if enabled, ok := c.groupMembership[a]; ok && enabled { c.groupMembership[a] = false } } // Authorized indicates if the credential has the specified authorizing attribute. func (c *Credentials) Authorized(a string) bool { if enabled, ok := c.groupMembership[a]; ok && enabled { return true } return false } // SessionID returns the credential's session ID. func (c *Credentials) SessionID() string { return c.sessionID } // Expired indicates if the credential has expired. func (c *Credentials) Expired() bool { if !c.ValidUntil.IsZero() && time.Now().UTC().After(c.ValidUntil) { return true } return false } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/credentials/credentials_test.go000066400000000000000000000005161362537225700267370ustar00rootroot00000000000000package credentials import ( "github.com/stretchr/testify/assert" goidentity "gopkg.in/jcmturner/goidentity.v2" "testing" ) func TestImplementsInterface(t *testing.T) { t.Parallel() u := new(Credentials) i := new(goidentity.Identity) assert.Implements(t, i, u, "Credentials type does not implement the Identity interface") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/000077500000000000000000000000001362537225700220755ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha1-96.go000066400000000000000000000105271362537225700262170ustar00rootroot00000000000000package crypto import ( "crypto/aes" "crypto/hmac" "crypto/sha1" "hash" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // RFC 3962 // Aes128CtsHmacSha96 implements Kerberos encryption type aes128-cts-hmac-sha1-96 type Aes128CtsHmacSha96 struct { } // GetETypeID returns the EType ID number. func (e Aes128CtsHmacSha96) GetETypeID() int32 { return etypeID.AES128_CTS_HMAC_SHA1_96 } // GetHashID returns the checksum type ID number. func (e Aes128CtsHmacSha96) GetHashID() int32 { return chksumtype.HMAC_SHA1_96_AES128 } // GetKeyByteSize returns the number of bytes for key of this etype. func (e Aes128CtsHmacSha96) GetKeyByteSize() int { return 128 / 8 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e Aes128CtsHmacSha96) GetKeySeedBitLength() int { return e.GetKeyByteSize() * 8 } // GetHashFunc returns the hash function for this etype. func (e Aes128CtsHmacSha96) GetHashFunc() func() hash.Hash { return sha1.New } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e Aes128CtsHmacSha96) GetMessageBlockByteSize() int { return 1 } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e Aes128CtsHmacSha96) GetDefaultStringToKeyParams() string { return "00001000" } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e Aes128CtsHmacSha96) GetConfounderByteSize() int { return aes.BlockSize } // GetHMACBitLength returns the bit count size of the integrity hash. func (e Aes128CtsHmacSha96) GetHMACBitLength() int { return 96 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e Aes128CtsHmacSha96) GetCypherBlockBitLength() int { return aes.BlockSize * 8 } // StringToKey returns a key derived from the string provided. func (e Aes128CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { return rfc3962.StringToKey(secret, salt, s2kparams, e) } // RandomToKey returns a key from the bytes provided. func (e Aes128CtsHmacSha96) RandomToKey(b []byte) []byte { return rfc3961.RandomToKey(b) } // EncryptData encrypts the data provided. func (e Aes128CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) { return rfc3962.EncryptData(key, data, e) } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e Aes128CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { return rfc3962.EncryptMessage(key, message, usage, e) } // DecryptData decrypts the data provided. func (e Aes128CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) { return rfc3962.DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e Aes128CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc3962.DecryptMessage(key, ciphertext, usage, e) } // DeriveKey derives a key from the protocol key based on the usage value. func (e Aes128CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) { return rfc3961.DeriveKey(protocolKey, usage, e) } // DeriveRandom generates data needed for key generation. func (e Aes128CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { return rfc3961.DeriveRandom(protocolKey, usage, e) } // VerifyIntegrity checks the integrity of the plaintext message. func (e Aes128CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e Aes128CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e Aes128CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { c, err := e.GetChecksumHash(protocolKey, data, usage) if err != nil { return false } return hmac.Equal(chksum, c) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha1-96_test.go000066400000000000000000000036451362537225700272610ustar00rootroot00000000000000package crypto import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962" ) func TestAes128CtsHmacSha196_StringToKey(t *testing.T) { t.Parallel() // Test vectors from RFC 3962 Appendix B b, _ := hex.DecodeString("1234567878563412") s := string(b) b, _ = hex.DecodeString("f09d849e") s2 := string(b) var tests = []struct { iterations int64 phrase string salt string pbkdf2 string key string }{ {1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b2563515", "42263c6e89f4fc28b8df68ee09799f15"}, {2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935d", "c651bf29e2300ac27fa469d693bdda13"}, {1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512b", "4c01cd46d632d01e6dbe230a01ed642a"}, {5, "password", s, "d1daa78615f287e6a1c8b120d7062a49", "e9b23d52273747dd5c35cb55be619d8e"}, {1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9", "59d1bb789a828b1aa54ef9c2883f69ed"}, {1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be61", "cb8005dc5f90179a7f02104c0018751d"}, {50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39", "f149c1f2e154a73452d43e7fe62a56e5"}, } var e Aes128CtsHmacSha96 for i, test := range tests { assert.Equal(t, test.pbkdf2, hex.EncodeToString(rfc3962.StringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected") k, err := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(uint32(test.iterations))) if err != nil { t.Errorf("error in processing string to key for test %d: %v", i, err) } assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha256-128.go000066400000000000000000000111651362537225700264460ustar00rootroot00000000000000package crypto import ( "crypto/aes" "crypto/hmac" "crypto/sha256" "hash" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // RFC https://tools.ietf.org/html/rfc8009 // Aes128CtsHmacSha256128 implements Kerberos encryption type aes128-cts-hmac-sha256-128 type Aes128CtsHmacSha256128 struct { } // GetETypeID returns the EType ID number. func (e Aes128CtsHmacSha256128) GetETypeID() int32 { return etypeID.AES128_CTS_HMAC_SHA256_128 } // GetHashID returns the checksum type ID number. func (e Aes128CtsHmacSha256128) GetHashID() int32 { return chksumtype.HMAC_SHA256_128_AES128 } // GetKeyByteSize returns the number of bytes for key of this etype. func (e Aes128CtsHmacSha256128) GetKeyByteSize() int { return 128 / 8 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e Aes128CtsHmacSha256128) GetKeySeedBitLength() int { return e.GetKeyByteSize() * 8 } // GetHashFunc returns the hash function for this etype. func (e Aes128CtsHmacSha256128) GetHashFunc() func() hash.Hash { return sha256.New } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e Aes128CtsHmacSha256128) GetMessageBlockByteSize() int { return 1 } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e Aes128CtsHmacSha256128) GetDefaultStringToKeyParams() string { return "00008000" } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e Aes128CtsHmacSha256128) GetConfounderByteSize() int { return aes.BlockSize } // GetHMACBitLength returns the bit count size of the integrity hash. func (e Aes128CtsHmacSha256128) GetHMACBitLength() int { return 128 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e Aes128CtsHmacSha256128) GetCypherBlockBitLength() int { return aes.BlockSize * 8 } // StringToKey returns a key derived from the string provided. func (e Aes128CtsHmacSha256128) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { saltp := rfc8009.GetSaltP(salt, "aes128-cts-hmac-sha256-128") return rfc8009.StringToKey(secret, saltp, s2kparams, e) } // RandomToKey returns a key from the bytes provided. func (e Aes128CtsHmacSha256128) RandomToKey(b []byte) []byte { return rfc8009.RandomToKey(b) } // EncryptData encrypts the data provided. func (e Aes128CtsHmacSha256128) EncryptData(key, data []byte) ([]byte, []byte, error) { return rfc8009.EncryptData(key, data, e) } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e Aes128CtsHmacSha256128) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { return rfc8009.EncryptMessage(key, message, usage, e) } // DecryptData decrypts the data provided. func (e Aes128CtsHmacSha256128) DecryptData(key, data []byte) ([]byte, error) { return rfc8009.DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e Aes128CtsHmacSha256128) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc8009.DecryptMessage(key, ciphertext, usage, e) } // DeriveKey derives a key from the protocol key based on the usage value. func (e Aes128CtsHmacSha256128) DeriveKey(protocolKey, usage []byte) ([]byte, error) { return rfc8009.DeriveKey(protocolKey, usage, e), nil } // DeriveRandom generates data needed for key generation. func (e Aes128CtsHmacSha256128) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { return rfc8009.DeriveRandom(protocolKey, usage, e) } // VerifyIntegrity checks the integrity of the ciphertext message. // Therefore the pt value to this interface method is not use. Pass any []byte. func (e Aes128CtsHmacSha256128) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { // We don't need ib just there for the interface return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e Aes128CtsHmacSha256128) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e Aes128CtsHmacSha256128) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { c, err := e.GetChecksumHash(protocolKey, data, usage) if err != nil { return false } return hmac.Equal(chksum, c) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes128-cts-hmac-sha256-128_test.go000066400000000000000000000146171362537225700275120ustar00rootroot00000000000000package crypto import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009" ) func TestAes128CtsHmacSha256128_StringToKey(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 Appendix A // Random 16bytes in test vector as string r, _ := hex.DecodeString("10DF9DD783E5BC8ACEA1730E74355F61") s := string(r) var tests = []struct { iterations uint32 phrase string salt string saltp string key string }{ {32768, "password", s + "ATHENA.MIT.EDUraeburn", "6165733132382d6374732d686d61632d7368613235362d3132380010df9dd783e5bc8acea1730e74355f61415448454e412e4d49542e4544557261656275726e", "089bca48b105ea6ea77ca5d2f39dc5e7"}, } var e Aes128CtsHmacSha256128 for _, test := range tests { saltp := rfc8009.GetSaltP(test.salt, "aes128-cts-hmac-sha256-128") assert.Equal(t, test.saltp, hex.EncodeToString([]byte(saltp)), "SaltP not as expected") k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(test.iterations)) assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected") } } func TestAes128CtsHmacSha256128_DeriveKey(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 Appendix A protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c") testUsage := uint32(2) var e Aes128CtsHmacSha256128 k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage)) if err != nil { t.Fatalf("Error deriving checksum key: %v", err) } assert.Equal(t, "b31a018a48f54776f403e9a396325dc3", hex.EncodeToString(k), "Checksum derived key not as epxected") k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage)) if err != nil { t.Fatalf("Error deriving encryption key: %v", err) } assert.Equal(t, "9b197dd1e8c5609d6e67c3e37c62c72e", hex.EncodeToString(k), "Encryption derived key not as epxected") k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage)) if err != nil { t.Fatalf("Error deriving integrity key: %v", err) } assert.Equal(t, "9fda0e56ab2d85e1569a688696c26a6c", hex.EncodeToString(k), "Integrity derived key not as epxected") } func TestAes128CtsHmacSha256128_VerifyIntegrity(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c") testUsage := uint32(2) var e Aes128CtsHmacSha256128 var tests = []struct { kc string pt string chksum string }{ {"b31a018a48f54776f403e9a396325dc3", "000102030405060708090a0b0c0d0e0f1011121314", "d78367186643d67b411cba9139fc1dee"}, } for _, test := range tests { p, _ := hex.DecodeString(test.pt) b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage) if err != nil { t.Errorf("error generating checksum: %v", err) } assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected") } } func TestAes128CtsHmacSha256128_Cypto(t *testing.T) { t.Parallel() protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c") testUsage := uint32(2) var tests = []struct { plain string confounder string ke string ki string encrypted string // AESOutput hash string // TruncatedHMACOutput cipher string // Ciphertext(AESOutput|HMACOutput) }{ // Test vectors from RFC 8009 Appendix A {"", "7e5895eaf2672435bad817f545a37148", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "ef85fb890bb8472f4dab20394dca781d", "ad877eda39d50c870c0d5a0a8e48c718", "ef85fb890bb8472f4dab20394dca781dad877eda39d50c870c0d5a0a8e48c718"}, {"000102030405", "7bca285e2fd4130fb55b1a5c83bc5b24", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6", "877ce99e247e52d16ed4421dfdf8976c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6877ce99e247e52d16ed4421dfdf8976c"}, {"000102030405060708090a0b0c0d0e0f", "56ab21713ff62c0a1457200f6fa9948f", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc2", "95fb4852e7d83e1e7c48c37eebe6b0d3", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc295fb4852e7d83e1e7c48c37eebe6b0d3"}, {"000102030405060708090a0b0c0d0e0f1011121314", "a7a4e29a4728ce10664fb64e49ad3fac", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c1", "86b39a413c2f92ca9b8334a287ffcbfc", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c186b39a413c2f92ca9b8334a287ffcbfc"}, } var e Aes128CtsHmacSha256128 for i, test := range tests { m, _ := hex.DecodeString(test.plain) b, _ := hex.DecodeString(test.encrypted) ke, _ := hex.DecodeString(test.ke) cf, _ := hex.DecodeString(test.confounder) ct, _ := hex.DecodeString(test.cipher) cfm := append(cf, m...) // Test encryption to raw encrypted bytes _, c, err := e.EncryptData(ke, cfm) if err != nil { t.Errorf("encryption failed for test %v: %v", i+1, err) } assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i) // Test decryption of raw encrypted bytes p, err := e.DecryptData(ke, b) //Remove the confounder bytes p = p[e.GetConfounderByteSize():] if err != nil { t.Errorf("decryption failed for test %v: %v", i+1, err) } assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected - test %v", i) // Test integrity check of complete ciphertext message assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed") // Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash) _, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage) if err != nil { t.Errorf("encryption to message failed for test %v: %v", i+1, err) } dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage) if err != nil { t.Errorf("decrypting complete encrypted message failed for test %v: %v", i+1, err) } assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err) // Test the integrity hash ivz := make([]byte, e.GetConfounderByteSize()) hm := append(ivz, b...) mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e) assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha1-96.go000066400000000000000000000105271362537225700262210ustar00rootroot00000000000000package crypto import ( "crypto/aes" "crypto/hmac" "crypto/sha1" "hash" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // RFC 3962 // Aes256CtsHmacSha96 implements Kerberos encryption type aes256-cts-hmac-sha1-96 type Aes256CtsHmacSha96 struct { } // GetETypeID returns the EType ID number. func (e Aes256CtsHmacSha96) GetETypeID() int32 { return etypeID.AES256_CTS_HMAC_SHA1_96 } // GetHashID returns the checksum type ID number. func (e Aes256CtsHmacSha96) GetHashID() int32 { return chksumtype.HMAC_SHA1_96_AES256 } // GetKeyByteSize returns the number of bytes for key of this etype. func (e Aes256CtsHmacSha96) GetKeyByteSize() int { return 256 / 8 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e Aes256CtsHmacSha96) GetKeySeedBitLength() int { return e.GetKeyByteSize() * 8 } // GetHashFunc returns the hash function for this etype. func (e Aes256CtsHmacSha96) GetHashFunc() func() hash.Hash { return sha1.New } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e Aes256CtsHmacSha96) GetMessageBlockByteSize() int { return 1 } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e Aes256CtsHmacSha96) GetDefaultStringToKeyParams() string { return "00001000" } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e Aes256CtsHmacSha96) GetConfounderByteSize() int { return aes.BlockSize } // GetHMACBitLength returns the bit count size of the integrity hash. func (e Aes256CtsHmacSha96) GetHMACBitLength() int { return 96 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e Aes256CtsHmacSha96) GetCypherBlockBitLength() int { return aes.BlockSize * 8 } // StringToKey returns a key derived from the string provided. func (e Aes256CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { return rfc3962.StringToKey(secret, salt, s2kparams, e) } // RandomToKey returns a key from the bytes provided. func (e Aes256CtsHmacSha96) RandomToKey(b []byte) []byte { return rfc3961.RandomToKey(b) } // EncryptData encrypts the data provided. func (e Aes256CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) { return rfc3962.EncryptData(key, data, e) } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e Aes256CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { return rfc3962.EncryptMessage(key, message, usage, e) } // DecryptData decrypts the data provided. func (e Aes256CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) { return rfc3962.DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e Aes256CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc3962.DecryptMessage(key, ciphertext, usage, e) } // DeriveKey derives a key from the protocol key based on the usage value. func (e Aes256CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) { return rfc3961.DeriveKey(protocolKey, usage, e) } // DeriveRandom generates data needed for key generation. func (e Aes256CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { return rfc3961.DeriveRandom(protocolKey, usage, e) } // VerifyIntegrity checks the integrity of the plaintext message. func (e Aes256CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e Aes256CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e Aes256CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { c, err := e.GetChecksumHash(protocolKey, data, usage) if err != nil { return false } return hmac.Equal(chksum, c) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha1-96_test.go000066400000000000000000000045451362537225700272630ustar00rootroot00000000000000package crypto import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3962" ) func TestAes256CtsHmacSha196_StringToKey(t *testing.T) { t.Parallel() // Test vectors from RFC 3962 Appendix B b, _ := hex.DecodeString("1234567878563412") s := string(b) b, _ = hex.DecodeString("f09d849e") s2 := string(b) var tests = []struct { iterations int64 phrase string salt string pbkdf2 string key string }{ {1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837", "fe697b52bc0d3ce14432ba036a92e65bbb52280990a2fa27883998d72af30161"}, {2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86", "a2e16d16b36069c135d5e9d2e25f896102685618b95914b467c67622225824ff"}, {1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13", "55a6ac740ad17b4846941051e1e8b0a7548d93b0ab30a8bc3ff16280382b8c2a"}, {5, "password", s, "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee", "97a4e786be20d81a382d5ebc96d5909cabcdadc87ca48f574504159f16c36e31"}, {1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1", "89adee3608db8bc71f1bfbfe459486b05618b70cbae22092534e56c553ba4b34"}, {1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"}, {50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"}, } var e Aes256CtsHmacSha96 for i, test := range tests { assert.Equal(t, test.pbkdf2, hex.EncodeToString(rfc3962.StringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected") k, err := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(uint32(test.iterations))) if err != nil { t.Errorf("error in processing string to key for test %d: %v", i, err) } assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha384-192.go000066400000000000000000000111701362537225700264470ustar00rootroot00000000000000package crypto import ( "crypto/aes" "crypto/hmac" "crypto/sha512" "hash" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // RFC https://tools.ietf.org/html/rfc8009 // Aes256CtsHmacSha384192 implements Kerberos encryption type aes256-cts-hmac-sha384-192 type Aes256CtsHmacSha384192 struct { } // GetETypeID returns the EType ID number. func (e Aes256CtsHmacSha384192) GetETypeID() int32 { return etypeID.AES256_CTS_HMAC_SHA384_192 } // GetHashID returns the checksum type ID number. func (e Aes256CtsHmacSha384192) GetHashID() int32 { return chksumtype.HMAC_SHA384_192_AES256 } // GetKeyByteSize returns the number of bytes for key of this etype. func (e Aes256CtsHmacSha384192) GetKeyByteSize() int { return 192 / 8 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e Aes256CtsHmacSha384192) GetKeySeedBitLength() int { return e.GetKeyByteSize() * 8 } // GetHashFunc returns the hash function for this etype. func (e Aes256CtsHmacSha384192) GetHashFunc() func() hash.Hash { return sha512.New384 } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e Aes256CtsHmacSha384192) GetMessageBlockByteSize() int { return 1 } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e Aes256CtsHmacSha384192) GetDefaultStringToKeyParams() string { return "00008000" } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e Aes256CtsHmacSha384192) GetConfounderByteSize() int { return aes.BlockSize } // GetHMACBitLength returns the bit count size of the integrity hash. func (e Aes256CtsHmacSha384192) GetHMACBitLength() int { return 192 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e Aes256CtsHmacSha384192) GetCypherBlockBitLength() int { return aes.BlockSize * 8 } // StringToKey returns a key derived from the string provided. func (e Aes256CtsHmacSha384192) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { saltp := rfc8009.GetSaltP(salt, "aes256-cts-hmac-sha384-192") return rfc8009.StringToKey(secret, saltp, s2kparams, e) } // RandomToKey returns a key from the bytes provided. func (e Aes256CtsHmacSha384192) RandomToKey(b []byte) []byte { return rfc8009.RandomToKey(b) } // EncryptData encrypts the data provided. func (e Aes256CtsHmacSha384192) EncryptData(key, data []byte) ([]byte, []byte, error) { return rfc8009.EncryptData(key, data, e) } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e Aes256CtsHmacSha384192) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { return rfc8009.EncryptMessage(key, message, usage, e) } // DecryptData decrypts the data provided. func (e Aes256CtsHmacSha384192) DecryptData(key, data []byte) ([]byte, error) { return rfc8009.DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e Aes256CtsHmacSha384192) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc8009.DecryptMessage(key, ciphertext, usage, e) } // DeriveKey derives a key from the protocol key based on the usage value. func (e Aes256CtsHmacSha384192) DeriveKey(protocolKey, usage []byte) ([]byte, error) { return rfc8009.DeriveKey(protocolKey, usage, e), nil } // DeriveRandom generates data needed for key generation. func (e Aes256CtsHmacSha384192) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { return rfc8009.DeriveRandom(protocolKey, usage, e) } // VerifyIntegrity checks the integrity of the ciphertext message. // Therefore the pt value to this interface method is not use. Pass any []byte. func (e Aes256CtsHmacSha384192) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { // We don't need ib just there for the interface return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e Aes256CtsHmacSha384192) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e Aes256CtsHmacSha384192) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { c, err := e.GetChecksumHash(protocolKey, data, usage) if err != nil { return false } return hmac.Equal(chksum, c) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/aes256-cts-hmac-sha384-192_test.go000066400000000000000000000156561362537225700275230ustar00rootroot00000000000000package crypto import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc8009" ) func TestAes256CtsHmacSha384192_StringToKey(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 Appendix A // Random 16bytes in test vector as string r, _ := hex.DecodeString("10DF9DD783E5BC8ACEA1730E74355F61") s := string(r) var tests = []struct { iterations uint32 phrase string salt string saltp string key string }{ {32768, "password", s + "ATHENA.MIT.EDUraeburn", "6165733235362d6374732d686d61632d7368613338342d3139320010df9dd783e5bc8acea1730e74355f61415448454e412e4d49542e4544557261656275726e", "45bd806dbf6a833a9cffc1c94589a222367a79bc21c413718906e9f578a78467"}, } var e Aes256CtsHmacSha384192 for _, test := range tests { saltp := rfc8009.GetSaltP(test.salt, "aes256-cts-hmac-sha384-192") assert.Equal(t, test.saltp, hex.EncodeToString([]byte(saltp)), "SaltP not as expected") k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2Kparams(test.iterations)) assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected") } } func TestAes256CtsHmacSha384192_DeriveKey(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 Appendix A protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52") testUsage := uint32(2) var e Aes256CtsHmacSha384192 k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage)) if err != nil { t.Fatalf("Error deriving checksum key: %v", err) } assert.Equal(t, "ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", hex.EncodeToString(k), "Checksum derived key not as epxected") k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage)) if err != nil { t.Fatalf("Error deriving encryption key: %v", err) } assert.Equal(t, "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", hex.EncodeToString(k), "Encryption derived key not as epxected") k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage)) if err != nil { t.Fatalf("Error deriving integrity key: %v", err) } assert.Equal(t, "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", hex.EncodeToString(k), "Integrity derived key not as epxected") } func TestAes256CtsHmacSha384192_Cypto(t *testing.T) { t.Parallel() protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52") testUsage := uint32(2) var tests = []struct { plain string confounder string ke string ki string encrypted string // AESOutput hash string // TruncatedHMACOutput cipher string // Ciphertext(AESOutput|HMACOutput) }{ // Test vectors from RFC 8009 Appendix A {"", "f764e9fa15c276478b2c7d0c4e5f58e4", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "41f53fa5bfe7026d91faf9be959195a0", "58707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c", "41f53fa5bfe7026d91faf9be959195a058707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c"}, {"000102030405", "b80d3251c1f6471494256ffe712d0b9a", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9", "f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2"}, {"000102030405060708090a0b0c0d0e0f", "53bf8a0d105265d4e276428624ce5e63", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f6", "8cf51f14d798c2273f35df574d1f932e40c4ff255b36a266", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f68cf51f14d798c2273f35df574d1f932e40c4ff255b36a266"}, {"000102030405060708090a0b0c0d0e0f1011121314", "763e65367e864f02f55153c7e3b58af1", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86", "fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62"}, } var e Aes256CtsHmacSha384192 for i, test := range tests { m, _ := hex.DecodeString(test.plain) b, _ := hex.DecodeString(test.encrypted) ke, _ := hex.DecodeString(test.ke) cf, _ := hex.DecodeString(test.confounder) ct, _ := hex.DecodeString(test.cipher) cfm := append(cf, m...) // Test encryption to raw encrypted bytes _, c, err := e.EncryptData(ke, cfm) if err != nil { t.Errorf("encryption failed for test %v: %v", i+1, err) } assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i) // Test decryption of raw encrypted bytes p, err := e.DecryptData(ke, b) //Remove the confounder bytes p = p[e.GetConfounderByteSize():] if err != nil { t.Errorf("decryption failed for test %v: %v", i+1, err) } assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected - test %v", i) // Test integrity check of complete ciphertext message assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed") // Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash) _, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage) if err != nil { t.Errorf("encryption to message failed for test %v: %v", i+1, err) } dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage) if err != nil { t.Errorf("decrypting complete encrypted message failed for test %v: %v", i+1, err) } assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err) // Test the integrity hash ivz := make([]byte, e.GetConfounderByteSize()) hm := append(ivz, b...) mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e) assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i) } } func TestAes256CtsHmacSha384192_VerifyIntegrity(t *testing.T) { t.Parallel() // Test vectors from RFC 8009 protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52") testUsage := uint32(2) var e Aes256CtsHmacSha384192 var tests = []struct { kc string pt string chksum string }{ {"ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", "000102030405060708090a0b0c0d0e0f1011121314", "45ee791567eefca37f4ac1e0222de80d43c3bfa06699672a"}, } for _, test := range tests { p, _ := hex.DecodeString(test.pt) b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage) if err != nil { t.Errorf("error generating checksum: %v", err) } assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/common/000077500000000000000000000000001362537225700233655ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/common/common.go000066400000000000000000000072321362537225700252100ustar00rootroot00000000000000// Package common provides encryption methods common across encryption types package common import ( "bytes" "crypto/hmac" "encoding/binary" "encoding/hex" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) // ZeroPad pads bytes with zeros to nearest multiple of message size m. func ZeroPad(b []byte, m int) ([]byte, error) { if m <= 0 { return nil, errors.New("Invalid message block size when padding") } if b == nil || len(b) == 0 { return nil, errors.New("Data not valid to pad: Zero size") } if l := len(b) % m; l != 0 { n := m - l z := make([]byte, n) b = append(b, z...) } return b, nil } // PKCS7Pad pads bytes according to RFC 2315 to nearest multiple of message size m. func PKCS7Pad(b []byte, m int) ([]byte, error) { if m <= 0 { return nil, errors.New("Invalid message block size when padding") } if b == nil || len(b) == 0 { return nil, errors.New("Data not valid to pad: Zero size") } n := m - (len(b) % m) pb := make([]byte, len(b)+n) copy(pb, b) copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n)) return pb, nil } // PKCS7Unpad removes RFC 2315 padding from byes where message size is m. func PKCS7Unpad(b []byte, m int) ([]byte, error) { if m <= 0 { return nil, errors.New("invalid message block size when unpadding") } if b == nil || len(b) == 0 { return nil, errors.New("padded data not valid: Zero size") } if len(b)%m != 0 { return nil, errors.New("padded data not valid: Not multiple of message block size") } c := b[len(b)-1] n := int(c) if n == 0 || n > len(b) { return nil, errors.New("padded data not valid: Data may not have been padded") } for i := 0; i < n; i++ { if b[len(b)-n+i] != c { return nil, errors.New("padded data not valid") } } return b[:len(b)-n], nil } // GetHash generates the keyed hash value according to the etype's hash function. func GetHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) { k, err := etype.DeriveKey(key, usage) if err != nil { return nil, fmt.Errorf("unable to derive key for checksum: %v", err) } mac := hmac.New(etype.GetHashFunc(), k) p := make([]byte, len(pt)) copy(p, pt) mac.Write(p) return mac.Sum(nil)[:etype.GetHMACBitLength()/8], nil } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func GetChecksumHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { return GetHash(b, key, GetUsageKc(usage), etype) } // GetIntegrityHash returns a keyed integrity hash of the bytes provided. func GetIntegrityHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { return GetHash(b, key, GetUsageKi(usage), etype) } // VerifyChecksum compares the checksum of the msg bytes is the same as the checksum provided. func VerifyChecksum(key, chksum, msg []byte, usage uint32, etype etype.EType) bool { expectedMAC, _ := GetChecksumHash(msg, key, usage, etype) return hmac.Equal(chksum, expectedMAC) } // GetUsageKc returns the checksum key usage value for the usage number un. func GetUsageKc(un uint32) []byte { return getUsage(un, 0x99) } // GetUsageKe returns the encryption key usage value for the usage number un func GetUsageKe(un uint32) []byte { return getUsage(un, 0xAA) } // GetUsageKi returns the integrity key usage value for the usage number un func GetUsageKi(un uint32) []byte { return getUsage(un, 0x55) } func getUsage(un uint32, o byte) []byte { var buf bytes.Buffer binary.Write(&buf, binary.BigEndian, un) return append(buf.Bytes(), o) } // IterationsToS2Kparams converts the number of iterations as an integer to a string representation. func IterationsToS2Kparams(i uint32) string { b := make([]byte, 4, 4) binary.BigEndian.PutUint32(b, i) return hex.EncodeToString(b) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/crypto.go000066400000000000000000000117111362537225700237450ustar00rootroot00000000000000// Package crypto implements cryptographic functions for Kerberos 5 implementation. package crypto import ( "encoding/hex" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/types" ) // GetEtype returns an instances of the required etype struct for the etype ID. func GetEtype(id int32) (etype.EType, error) { switch id { case etypeID.AES128_CTS_HMAC_SHA1_96: var et Aes128CtsHmacSha96 return et, nil case etypeID.AES256_CTS_HMAC_SHA1_96: var et Aes256CtsHmacSha96 return et, nil case etypeID.AES128_CTS_HMAC_SHA256_128: var et Aes128CtsHmacSha256128 return et, nil case etypeID.AES256_CTS_HMAC_SHA384_192: var et Aes256CtsHmacSha384192 return et, nil case etypeID.DES3_CBC_SHA1_KD: var et Des3CbcSha1Kd return et, nil case etypeID.RC4_HMAC: var et RC4HMAC return et, nil default: return nil, fmt.Errorf("unknown or unsupported EType: %d", id) } } // GetChksumEtype returns an instances of the required etype struct for the checksum ID. func GetChksumEtype(id int32) (etype.EType, error) { switch id { case chksumtype.HMAC_SHA1_96_AES128: var et Aes128CtsHmacSha96 return et, nil case chksumtype.HMAC_SHA1_96_AES256: var et Aes256CtsHmacSha96 return et, nil case chksumtype.HMAC_SHA256_128_AES128: var et Aes128CtsHmacSha256128 return et, nil case chksumtype.HMAC_SHA384_192_AES256: var et Aes256CtsHmacSha384192 return et, nil case chksumtype.HMAC_SHA1_DES3_KD: var et Des3CbcSha1Kd return et, nil case chksumtype.KERB_CHECKSUM_HMAC_MD5: var et RC4HMAC return et, nil //case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED: // var et RC4HMAC // return et, nil default: return nil, fmt.Errorf("unknown or unsupported checksum type: %d", id) } } // GetKeyFromPassword generates an encryption key from the principal's password. func GetKeyFromPassword(passwd string, cname types.PrincipalName, realm string, etypeID int32, pas types.PADataSequence) (types.EncryptionKey, etype.EType, error) { var key types.EncryptionKey et, err := GetEtype(etypeID) if err != nil { return key, et, fmt.Errorf("error getting encryption type: %v", err) } sk2p := et.GetDefaultStringToKeyParams() var salt string var paID int32 for _, pa := range pas { switch pa.PADataType { case patype.PA_PW_SALT: if paID > pa.PADataType { continue } salt = string(pa.PADataValue) case patype.PA_ETYPE_INFO: if paID > pa.PADataType { continue } var eti types.ETypeInfo err := eti.Unmarshal(pa.PADataValue) if err != nil { return key, et, fmt.Errorf("error unmashaling PA Data to PA-ETYPE-INFO2: %v", err) } if etypeID != eti[0].EType { et, err = GetEtype(eti[0].EType) if err != nil { return key, et, fmt.Errorf("error getting encryption type: %v", err) } } salt = string(eti[0].Salt) case patype.PA_ETYPE_INFO2: if paID > pa.PADataType { continue } var et2 types.ETypeInfo2 err := et2.Unmarshal(pa.PADataValue) if err != nil { return key, et, fmt.Errorf("error unmashalling PA Data to PA-ETYPE-INFO2: %v", err) } if etypeID != et2[0].EType { et, err = GetEtype(et2[0].EType) if err != nil { return key, et, fmt.Errorf("error getting encryption type: %v", err) } } if len(et2[0].S2KParams) == 4 { sk2p = hex.EncodeToString(et2[0].S2KParams) } salt = et2[0].Salt } } if salt == "" { salt = cname.GetSalt(realm) } k, err := et.StringToKey(passwd, salt, sk2p) if err != nil { return key, et, fmt.Errorf("error deriving key from string: %+v", err) } key = types.EncryptionKey{ KeyType: etypeID, KeyValue: k, } return key, et, nil } // GetEncryptedData encrypts the data provided and returns and EncryptedData type. // Pass a usage value of zero to use the key provided directly rather than deriving one. func GetEncryptedData(plainBytes []byte, key types.EncryptionKey, usage uint32, kvno int) (types.EncryptedData, error) { var ed types.EncryptedData et, err := GetEtype(key.KeyType) if err != nil { return ed, fmt.Errorf("error getting etype: %v", err) } _, b, err := et.EncryptMessage(key.KeyValue, plainBytes, usage) if err != nil { return ed, err } ed = types.EncryptedData{ EType: key.KeyType, Cipher: b, KVNO: kvno, } return ed, nil } // DecryptEncPart decrypts the EncryptedData. func DecryptEncPart(ed types.EncryptedData, key types.EncryptionKey, usage uint32) ([]byte, error) { return DecryptMessage(ed.Cipher, key, usage) } // DecryptMessage decrypts the ciphertext and verifies the integrity. func DecryptMessage(ciphertext []byte, key types.EncryptionKey, usage uint32) ([]byte, error) { et, err := GetEtype(key.KeyType) if err != nil { return []byte{}, fmt.Errorf("error decrypting: %v", err) } b, err := et.DecryptMessage(key.KeyValue, ciphertext, usage) if err != nil { return nil, fmt.Errorf("error decrypting: %v", err) } return b, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/des3-cbc-sha1-kd.go000066400000000000000000000106221362537225700252360ustar00rootroot00000000000000package crypto import ( "crypto/des" "crypto/hmac" "crypto/sha1" "errors" "hash" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) //RFC: 3961 Section 6.3 // Des3CbcSha1Kd implements Kerberos encryption type des3-cbc-hmac-sha1-kd type Des3CbcSha1Kd struct { } // GetETypeID returns the EType ID number. func (e Des3CbcSha1Kd) GetETypeID() int32 { return etypeID.DES3_CBC_SHA1_KD } // GetHashID returns the checksum type ID number. func (e Des3CbcSha1Kd) GetHashID() int32 { return chksumtype.HMAC_SHA1_DES3_KD } // GetKeyByteSize returns the number of bytes for key of this etype. func (e Des3CbcSha1Kd) GetKeyByteSize() int { return 24 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e Des3CbcSha1Kd) GetKeySeedBitLength() int { return 21 * 8 } // GetHashFunc returns the hash function for this etype. func (e Des3CbcSha1Kd) GetHashFunc() func() hash.Hash { return sha1.New } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e Des3CbcSha1Kd) GetMessageBlockByteSize() int { return des.BlockSize } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e Des3CbcSha1Kd) GetDefaultStringToKeyParams() string { var s string return s } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e Des3CbcSha1Kd) GetConfounderByteSize() int { return des.BlockSize } // GetHMACBitLength returns the bit count size of the integrity hash. func (e Des3CbcSha1Kd) GetHMACBitLength() int { return e.GetHashFunc()().Size() * 8 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e Des3CbcSha1Kd) GetCypherBlockBitLength() int { return des.BlockSize * 8 } // StringToKey returns a key derived from the string provided. func (e Des3CbcSha1Kd) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { if s2kparams != "" { return []byte{}, errors.New("s2kparams must be an empty string") } return rfc3961.DES3StringToKey(secret, salt, e) } // RandomToKey returns a key from the bytes provided. func (e Des3CbcSha1Kd) RandomToKey(b []byte) []byte { return rfc3961.DES3RandomToKey(b) } // DeriveRandom generates data needed for key generation. func (e Des3CbcSha1Kd) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { r, err := rfc3961.DeriveRandom(protocolKey, usage, e) return r, err } // DeriveKey derives a key from the protocol key based on the usage value. func (e Des3CbcSha1Kd) DeriveKey(protocolKey, usage []byte) ([]byte, error) { r, err := e.DeriveRandom(protocolKey, usage) if err != nil { return nil, err } return e.RandomToKey(r), nil } // EncryptData encrypts the data provided. func (e Des3CbcSha1Kd) EncryptData(key, data []byte) ([]byte, []byte, error) { return rfc3961.DES3EncryptData(key, data, e) } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e Des3CbcSha1Kd) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { return rfc3961.DES3EncryptMessage(key, message, usage, e) } // DecryptData decrypts the data provided. func (e Des3CbcSha1Kd) DecryptData(key, data []byte) ([]byte, error) { return rfc3961.DES3DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e Des3CbcSha1Kd) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc3961.DES3DecryptMessage(key, ciphertext, usage, e) } // VerifyIntegrity checks the integrity of the plaintext message. func (e Des3CbcSha1Kd) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e Des3CbcSha1Kd) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e Des3CbcSha1Kd) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { c, err := e.GetChecksumHash(protocolKey, data, usage) if err != nil { return false } return hmac.Equal(chksum, c) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/des3-cbc-sha1-kd_test.go000066400000000000000000000062621362537225700263020ustar00rootroot00000000000000package crypto import ( "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestDes3CbcSha1Kd_DR_DK(t *testing.T) { t.Parallel() // Test vectors from RFC 3961 Appendix A3 var tests = []struct { key string usage string dr string dk string }{ {"dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92", "0000000155", "935079d14490a75c3093c4a6e8c3b049c71e6ee705", "925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd"}, {"5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2", "00000001aa", "9f58e5a047d894101c469845d67ae3c5249ed812f2", "9e58e5a146d9942a101c469845d67a20e3c4259ed913f207"}, {"98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc", "0000000155", "12fff90c773f956d13fc2ca0d0840349dbd39908eb", "13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf"}, {"622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5", "00000001aa", "f8debf05b097e7dc0603686aca35d91fd9a5516a70", "f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e"}, {"d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb", "6b65726265726f73", "2270db565d2a3d64cfbfdc5305d4f778a6de42d9da", "2370da575d2a3da864cebfdc5204d56df779a7df43d9da43"}, {"c1081649ada74362e6a1459d01dfd30d67c2234c940704da", "0000000155", "348056ec98fcc517171d2b4d7a9493af482d999175", "348057ec98fdc48016161c2a4c7a943e92ae492c989175f7"}, {"5d154af238f46713155719d55e2f1f790dd661f279a7917c", "00000001aa", "a8818bc367dadacbe9a6c84627fb60c294b01215e5", "a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1"}, {"798562e049852f57dc8c343ba17f2ca1d97394efc8adc443", "0000000155", "c813f88b3be2b2f75424ce9175fbc8483b88c8713a", "c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49"}, {"26dce334b545292f2feab9a8701a89a4b99eb9942cecd016", "00000001aa", "f58efc6f83f93e55e695fd252cf8fe59f7d5ba37ec", "f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d"}, } for _, test := range tests { var e Des3CbcSha1Kd key, _ := hex.DecodeString(test.key) usage, _ := hex.DecodeString(test.usage) derivedRandom, err := e.DeriveRandom(key, usage) if err != nil { t.Fatal(fmt.Sprintf("Error in deriveRandom: %v", err)) } assert.Equal(t, test.dr, hex.EncodeToString(derivedRandom), "DR not as expected") derivedKey, err := e.DeriveKey(key, usage) if err != nil { t.Fatal(fmt.Sprintf("Error in deriveKey: %v", err)) } assert.Equal(t, test.dk, hex.EncodeToString(derivedKey), "DK not as expected") } } func TestDes3CbcSha1Kd_StringToKey(t *testing.T) { t.Parallel() var tests = []struct { salt string secret string key string }{ {"ATHENA.MIT.EDUraeburn", "password", "850bb51358548cd05e86768c313e3bfef7511937dcf72c3e"}, {"WHITEHOUSE.GOVdanny", "potatoe", "dfcd233dd0a43204ea6dc437fb15e061b02979c1f74f377a"}, {"EXAMPLE.COMbuckaroo", "penny", "6d2fcdf2d6fbbc3ddcadb5da5710a23489b0d3b69d5d9d4a"}, {"ATHENA.MIT.EDUJuri" + "\u0161" + "i" + "\u0107", "\u00DF", "16d5a40e1ce3bacb61b9dce00470324c831973a7b952feb0"}, {"EXAMPLE.COMpianist", "𝄞", "85763726585dbc1cce6ec43e1f751f07f1c4cbb098f40b19"}, } var e Des3CbcSha1Kd for _, test := range tests { key, err := e.StringToKey(test.secret, test.salt, "") if err != nil { t.Errorf("error in StringToKey: %v", err) } assert.Equal(t, test.key, hex.EncodeToString(key), "StringToKey not as expected") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/etype/000077500000000000000000000000001362537225700232235ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/etype/etype.go000066400000000000000000000021201362537225700246730ustar00rootroot00000000000000// Package etype provides the Kerberos Encryption Type interface package etype import "hash" // EType is the interface defining the Encryption Type. type EType interface { GetETypeID() int32 GetHashID() int32 GetKeyByteSize() int GetKeySeedBitLength() int GetDefaultStringToKeyParams() string StringToKey(string, salt, s2kparams string) ([]byte, error) RandomToKey(b []byte) []byte GetHMACBitLength() int GetMessageBlockByteSize() int EncryptData(key, data []byte) ([]byte, []byte, error) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) DecryptData(key, data []byte) ([]byte, error) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) GetCypherBlockBitLength() int GetConfounderByteSize() int DeriveKey(protocolKey, usage []byte) ([]byte, error) DeriveRandom(protocolKey, usage []byte) ([]byte, error) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool GetHashFunc() func() hash.Hash } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rc4-hmac.go000066400000000000000000000105551362537225700240300ustar00rootroot00000000000000package crypto import ( "bytes" "crypto/md5" "hash" "io" "golang.org/x/crypto/md4" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc3961" "gopkg.in/jcmturner/gokrb5.v5/crypto/rfc4757" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) //http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/security/krb5/internal/crypto/dk/ArcFourCrypto.java#ArcFourCrypto.encrypt%28byte%5B%5D%2Cint%2Cbyte%5B%5D%2Cbyte%5B%5D%2Cbyte%5B%5D%2Cint%2Cint%29 // RC4HMAC implements Kerberos encryption type aes256-cts-hmac-sha1-96 type RC4HMAC struct { } // GetETypeID returns the EType ID number. func (e RC4HMAC) GetETypeID() int32 { return etypeID.RC4_HMAC } // GetHashID returns the checksum type ID number. func (e RC4HMAC) GetHashID() int32 { return chksumtype.KERB_CHECKSUM_HMAC_MD5 } // GetKeyByteSize returns the number of bytes for key of this etype. func (e RC4HMAC) GetKeyByteSize() int { return 16 } // GetKeySeedBitLength returns the number of bits for the seed for key generation. func (e RC4HMAC) GetKeySeedBitLength() int { return e.GetKeyByteSize() * 8 } // GetHashFunc returns the hash function for this etype. func (e RC4HMAC) GetHashFunc() func() hash.Hash { return md5.New } // GetMessageBlockByteSize returns the block size for the etype's messages. func (e RC4HMAC) GetMessageBlockByteSize() int { return 1 } // GetDefaultStringToKeyParams returns the default key derivation parameters in string form. func (e RC4HMAC) GetDefaultStringToKeyParams() string { return "" } // GetConfounderByteSize returns the byte count for confounder to be used during cryptographic operations. func (e RC4HMAC) GetConfounderByteSize() int { return 8 } // GetHMACBitLength returns the bit count size of the integrity hash. func (e RC4HMAC) GetHMACBitLength() int { return md5.Size * 8 } // GetCypherBlockBitLength returns the bit count size of the cypher block. func (e RC4HMAC) GetCypherBlockBitLength() int { return 8 // doesn't really apply } // StringToKey returns a key derived from the string provided. func (e RC4HMAC) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) { return rfc4757.StringToKey(secret) } // RandomToKey returns a key from the bytes provided. func (e RC4HMAC) RandomToKey(b []byte) []byte { r := bytes.NewReader(b) h := md4.New() io.Copy(h, r) return h.Sum(nil) } // EncryptData encrypts the data provided. func (e RC4HMAC) EncryptData(key, data []byte) ([]byte, []byte, error) { b, err := rfc4757.EncryptData(key, data, e) return []byte{}, b, err } // EncryptMessage encrypts the message provided and concatenates it with the integrity hash to create an encrypted message. func (e RC4HMAC) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) { b, err := rfc4757.EncryptMessage(key, message, usage, false, e) return []byte{}, b, err } // DecryptData decrypts the data provided. func (e RC4HMAC) DecryptData(key, data []byte) ([]byte, error) { return rfc4757.DecryptData(key, data, e) } // DecryptMessage decrypts the message provided and verifies the integrity of the message. func (e RC4HMAC) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) { return rfc4757.DecryptMessage(key, ciphertext, usage, false, e) } // DeriveKey derives a key from the protocol key based on the usage value. func (e RC4HMAC) DeriveKey(protocolKey, usage []byte) ([]byte, error) { return rfc4757.HMAC(protocolKey, usage), nil } // DeriveRandom generates data needed for key generation. func (e RC4HMAC) DeriveRandom(protocolKey, usage []byte) ([]byte, error) { return rfc3961.DeriveRandom(protocolKey, usage, e) } // VerifyIntegrity checks the integrity of the plaintext message. func (e RC4HMAC) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool { return rfc4757.VerifyIntegrity(protocolKey, pt, ct, e) } // GetChecksumHash returns a keyed checksum hash of the bytes provided. func (e RC4HMAC) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) { return rfc4757.Checksum(protocolKey, usage, data) } // VerifyChecksum compares the checksum of the message bytes is the same as the checksum provided. func (e RC4HMAC) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool { checksum, err := rfc4757.Checksum(protocolKey, usage, data) if err != nil { return false } if !bytes.Equal(checksum, chksum) { return false } return true } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/000077500000000000000000000000001362537225700231725ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/encryption.go000066400000000000000000000100271362537225700257130ustar00rootroot00000000000000// Package rfc3961 provides encryption and checksum methods as specified in RFC 3961 package rfc3961 import ( "crypto/cipher" "crypto/des" "crypto/hmac" "crypto/rand" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) // DES3EncryptData encrypts the data provided using DES3 and methods specific to the etype provided. func DES3EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { if len(key) != e.GetKeyByteSize() { return nil, nil, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize()) block, err := des.NewTripleDESCipher(key) if err != nil { return nil, nil, fmt.Errorf("error creating cipher: %v", err) } ivz := make([]byte, des.BlockSize) ct := make([]byte, len(data)) mode := cipher.NewCBCEncrypter(block, ivz) mode.CryptBlocks(ct, data) return ct[len(ct)-e.GetMessageBlockByteSize():], ct, nil } // DES3EncryptMessage encrypts the message provided using DES3 and methods specific to the etype provided. // The encrypted data is concatenated with its integrity hash to create an encrypted message. func DES3EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { //confounder c := make([]byte, e.GetConfounderByteSize()) _, err := rand.Read(c) if err != nil { return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) } plainBytes := append(c, message...) plainBytes, _ = common.ZeroPad(plainBytes, e.GetMessageBlockByteSize()) // Derive key for encryption from usage var k []byte if usage != 0 { k, err = e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) } } iv, b, err := e.EncryptData(k, plainBytes) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } // Generate and append integrity hash ih, err := common.GetIntegrityHash(plainBytes, key, usage, e) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } b = append(b, ih...) return iv, b, nil } // DES3DecryptData decrypts the data provided using DES3 and methods specific to the etype provided. func DES3DecryptData(key, data []byte, e etype.EType) ([]byte, error) { if len(key) != e.GetKeyByteSize() { return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 { return []byte{}, errors.New("ciphertext is not a multiple of the block size") } block, err := des.NewTripleDESCipher(key) if err != nil { return []byte{}, fmt.Errorf("error creating cipher: %v", err) } pt := make([]byte, len(data)) ivz := make([]byte, des.BlockSize) mode := cipher.NewCBCDecrypter(block, ivz) mode.CryptBlocks(pt, data) return pt, nil } // DES3DecryptMessage decrypts the message provided using DES3 and methods specific to the etype provided. // The integrity of the message is also verified. func DES3DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { //Derive the key k, err := e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return nil, fmt.Errorf("error deriving key: %v", err) } // Strip off the checksum from the end b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) if err != nil { return nil, fmt.Errorf("error decrypting: %v", err) } //Verify checksum if !e.VerifyIntegrity(key, ciphertext, b, usage) { return nil, errors.New("error decrypting: integrity verification failed") } //Remove the confounder bytes return b[e.GetConfounderByteSize():], nil } // VerifyIntegrity verifies the integrity of cipertext bytes ct. func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool { h := make([]byte, etype.GetHMACBitLength()/8) copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:]) expectedMAC, _ := common.GetIntegrityHash(pt, key, usage, etype) return hmac.Equal(h, expectedMAC) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/keyDerivation.go000066400000000000000000000064441362537225700263460ustar00rootroot00000000000000package rfc3961 import ( "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) const ( prfconstant = "prf" ) // DeriveRandom implements the RFC 3961 defined function // // key: base key or protocol key. Likely to be a key from a keytab file. // // usage: a constant. // // n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes. // // k: key length / key seed length in bits. Eg. for AES256 this value is 256. // // e: the encryption etype function to use. func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) { n := e.GetCypherBlockBitLength() k := e.GetKeySeedBitLength() //Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be. nFoldUsage := Nfold(usage, n) //k-truncate implemented by creating a byte array the size of k (k is in bits hence /8) out := make([]byte, k/8) _, K, err := e.EncryptData(key, nFoldUsage) if err != nil { return out, err } for i := copy(out, K); i < len(out); { _, K, _ = e.EncryptData(key, K) i = i + copy(out[i:], K) } return out, nil } // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods. func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) { r, err := e.DeriveRandom(protocolKey, usage) if err != nil { return nil, err } return e.RandomToKey(r), nil } // RandomToKey returns a key from the bytes provided according to the definition in RFC 3961. func RandomToKey(b []byte) []byte { return b } // DES3RandomToKey returns a key from the bytes provided according to the definition in RFC 3961 for DES3 etypes. func DES3RandomToKey(b []byte) []byte { r := stretch56Bits(b[:7]) r2 := stretch56Bits(b[7:14]) r = append(r, r2...) r3 := stretch56Bits(b[14:21]) r = append(r, r3...) return r } // DES3StringToKey returns a key derived from the string provided according to the definition in RFC 3961 for DES3 etypes. func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) { s := secret + salt tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength())) return e.DeriveKey(tkey, []byte("kerberos")) } // PseudoRandom function as defined in RFC 3961 func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) { h := e.GetHashFunc()() h.Write(b) tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()] k, err := e.DeriveKey(key, []byte(prfconstant)) if err != nil { return []byte{}, err } _, prf, err := e.EncryptData(k, tmp) if err != nil { return []byte{}, err } return prf, nil } func stretch56Bits(b []byte) []byte { d := make([]byte, len(b), len(b)) copy(d, b) var lb byte for i, v := range d { bv, nb := calcEvenParity(v) d[i] = nb if bv != 0 { lb = lb | (1 << uint(i+1)) } else { lb = lb &^ (1 << uint(i+1)) } } _, lb = calcEvenParity(lb) d = append(d, lb) return d } func calcEvenParity(b byte) (uint8, uint8) { lowestbit := b & 0x01 // c counter of 1s in the first 7 bits of the byte var c int // Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s. for p := 1; p < 8; p++ { v := b & (1 << uint(p)) if v != 0 { c++ } } if c%2 == 0 { //Even number of 1s so set parity to 1 b = b | 1 } else { //Odd number of 1s so set parity to 0 b = b &^ 1 } return lowestbit, b } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/nfold.go000066400000000000000000000050771362537225700246340ustar00rootroot00000000000000package rfc3961 /* Credits This golang implementation of nfold used the following project for help with implementation detail. Although their source is in java it was helpful as a reference implementation of the RFC. You can find the source code of their open source project along with license information below. We acknowledge and are grateful to these developers for their contributions to open source Project: Apache Directory (http://http://directory.apache.org/) https://svn.apache.org/repos/asf/directory/apacheds/tags/1.5.1/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java License: http://www.apache.org/licenses/LICENSE-2.0 */ // Nfold expands the key to ensure it is not smaller than one cipher block. // Defined in RFC 3961. // // m input bytes that will be "stretched" to the least common multiple of n bits and the bit length of m. func Nfold(m []byte, n int) []byte { k := len(m) * 8 //Get the lowest common multiple of the two bit sizes lcm := lcm(n, k) relicate := lcm / k var sumBytes []byte for i := 0; i < relicate; i++ { rotation := 13 * i sumBytes = append(sumBytes, rotateRight(m, rotation)...) } nfold := make([]byte, n/8) sum := make([]byte, n/8) for i := 0; i < lcm/n; i++ { for j := 0; j < n/8; j++ { sum[j] = sumBytes[j+(i*len(sum))] } nfold = onesComplementAddition(nfold, sum) } return nfold } func onesComplementAddition(n1, n2 []byte) []byte { numBits := len(n1) * 8 out := make([]byte, numBits/8) carry := 0 for i := numBits - 1; i > -1; i-- { n1b := getBit(&n1, i) n2b := getBit(&n2, i) s := n1b + n2b + carry if s == 0 || s == 1 { setBit(&out, i, s) carry = 0 } else if s == 2 { carry = 1 } else if s == 3 { setBit(&out, i, 1) carry = 1 } } if carry == 1 { carryArray := make([]byte, len(n1)) carryArray[len(carryArray)-1] = 1 out = onesComplementAddition(out, carryArray) } return out } func rotateRight(b []byte, step int) []byte { out := make([]byte, len(b)) bitLen := len(b) * 8 for i := 0; i < bitLen; i++ { v := getBit(&b, i) setBit(&out, (i+step)%bitLen, v) } return out } func lcm(x, y int) int { return (x * y) / gcd(x, y) } func gcd(x, y int) int { for y != 0 { x, y = y, x%y } return x } func getBit(b *[]byte, p int) int { pByte := p / 8 pBit := uint(p % 8) vByte := (*b)[pByte] vInt := int(vByte >> (8 - (pBit + 1)) & 0x0001) return vInt } func setBit(b *[]byte, p, v int) { pByte := p / 8 pBit := uint(p % 8) oldByte := (*b)[pByte] var newByte byte newByte = byte(v<<(8-(pBit+1))) | oldByte (*b)[pByte] = newByte } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3961/nfold_test.go000066400000000000000000000015071362537225700256650ustar00rootroot00000000000000package rfc3961 import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func Test_nfold(t *testing.T) { t.Parallel() var tests = []struct { n int b []byte folded string }{ {64, []byte("012345"), "be072631276b1955"}, {56, []byte("password"), "78a07b6caf85fa"}, {64, []byte("Rough Consensus, and Running Code"), "bb6ed30870b7f0e0"}, {168, []byte("password"), "59e4a8ca7c0385c3c37b3f6d2000247cb6e6bd5b3e"}, {192, []byte("MASSACHVSETTS INSTITVTE OF TECHNOLOGY"), "db3b0d8f0b061e603282b308a50841229ad798fab9540c1b"}, {168, []byte("Q"), "518a54a215a8452a518a54a215a8452a518a54a215"}, {168, []byte("ba"), "fb25d531ae8974499f52fd92ea9857c4ba24cf297e"}, } for _, test := range tests { assert.Equal(t, test.folded, hex.EncodeToString(Nfold(test.b, test.n)), "Folded not as expected") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/000077500000000000000000000000001362537225700231735ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/encryption.go000066400000000000000000000062221362537225700257160ustar00rootroot00000000000000// Package rfc3962 provides encryption and checksum methods as specified in RFC 3962 package rfc3962 import ( "crypto/rand" "errors" "fmt" "gopkg.in/jcmturner/aescts.v1" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 3962. func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { if len(key) != e.GetKeyByteSize() { return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } ivz := make([]byte, e.GetCypherBlockBitLength()/8) return aescts.Encrypt(key, ivz, data) } // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 3962. // The encrypted data is concatenated with its integrity hash to create an encrypted message. func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { if len(key) != e.GetKeyByteSize() { return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } //confounder c := make([]byte, e.GetConfounderByteSize()) _, err := rand.Read(c) if err != nil { return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) } plainBytes := append(c, message...) // Derive key for encryption from usage var k []byte if usage != 0 { k, err = e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) } } // Encrypt the data iv, b, err := e.EncryptData(k, plainBytes) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } // Generate and append integrity hash ih, err := common.GetIntegrityHash(plainBytes, key, usage, e) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } b = append(b, ih...) return iv, b, nil } // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 3962. func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { if len(key) != e.GetKeyByteSize() { return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } ivz := make([]byte, e.GetCypherBlockBitLength()/8) return aescts.Decrypt(key, ivz, data) } // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 3962. // The integrity of the message is also verified. func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { //Derive the key k, err := e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return nil, fmt.Errorf("error deriving key: %v", err) } // Strip off the checksum from the end b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) if err != nil { return nil, err } //Verify checksum if !e.VerifyIntegrity(key, ciphertext, b, usage) { return nil, errors.New("integrity verification failed") } //Remove the confounder bytes return b[e.GetConfounderByteSize():], nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc3962/keyDerivation.go000066400000000000000000000034701362537225700263430ustar00rootroot00000000000000package rfc3962 import ( "encoding/binary" "encoding/hex" "errors" "github.com/jcmturner/gofork/x/crypto/pbkdf2" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) const ( s2kParamsZero = 4294967296 ) // StringToKey returns a key derived from the string provided according to the definition in RFC 3961. func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) { i, err := S2KparamsToItertions(s2kparams) if err != nil { return nil, err } return StringToKeyIter(secret, salt, i, e) } // StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0 func StringToPBKDF2(secret, salt string, iterations int64, e etype.EType) []byte { return pbkdf2.Key64([]byte(secret), []byte(salt), iterations, int64(e.GetKeyByteSize()), e.GetHashFunc()) } // StringToKeyIter returns a key derived from the string provided according to the definition in RFC 3961. func StringToKeyIter(secret, salt string, iterations int64, e etype.EType) ([]byte, error) { tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e)) return e.DeriveKey(tkey, []byte("kerberos")) } // S2KparamsToItertions converts the string representation of iterations to an integer func S2KparamsToItertions(s2kparams string) (int64, error) { //process s2kparams string var i uint32 if len(s2kparams) != 8 { return int64(s2kParamsZero), errors.New("invalid s2kparams length") } b, err := hex.DecodeString(s2kparams) if err != nil { return int64(s2kParamsZero), errors.New("invalid s2kparams, cannot decode string to bytes") } i = binary.BigEndian.Uint32(b) //buf := bytes.NewBuffer(b) //err = binary.Read(buf, binary.BigEndian, &i) if err != nil { return int64(s2kParamsZero), errors.New("invalid s2kparams, cannot convert to big endian int32") } return int64(i), nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/000077500000000000000000000000001362537225700231765ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/checksum.go000066400000000000000000000015141362537225700253300ustar00rootroot00000000000000package rfc4757 import ( "bytes" "crypto/hmac" "crypto/md5" "io" ) // Checksum returns a hash of the data in accordance with RFC 4757 func Checksum(key []byte, usage uint32, data []byte) ([]byte, error) { // Create hashing key s := append([]byte(`signaturekey`), byte(0x00)) //includes zero octet at end mac := hmac.New(md5.New, key) mac.Write(s) Ksign := mac.Sum(nil) // Format data tb := UsageToMSMsgType(usage) p := append(tb, data...) h := md5.New() rb := bytes.NewReader(p) _, err := io.Copy(h, rb) if err != nil { return []byte{}, err } tmp := h.Sum(nil) // Generate HMAC mac = hmac.New(md5.New, Ksign) mac.Write(tmp) return mac.Sum(nil), nil } // HMAC returns a keyed MD5 checksum of the data func HMAC(key []byte, data []byte) []byte { mac := hmac.New(md5.New, key) mac.Write(data) return mac.Sum(nil) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/encryption.go000066400000000000000000000054151362537225700257240ustar00rootroot00000000000000// Package rfc4757 provides encryption and checksum methods as specified in RFC 4757 package rfc4757 import ( "bytes" "crypto/rand" "crypto/rc4" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" ) // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 4757. func EncryptData(key, data []byte, e etype.EType) ([]byte, error) { if len(key) != e.GetKeyByteSize() { return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } rc4Cipher, err := rc4.NewCipher(key) if err != nil { return []byte{}, fmt.Errorf("error creating RC4 cipher: %v", err) } ed := make([]byte, len(data)) copy(ed, data) rc4Cipher.XORKeyStream(ed, ed) rc4Cipher.Reset() return ed, nil } // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 4757. func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { return EncryptData(key, data, e) } // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 4757. // The encrypted data is concatenated with its RC4 header containing integrity checksum and confounder to create an encrypted message. func EncryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { confounder := make([]byte, e.GetConfounderByteSize()) // size = 8 _, err := rand.Read(confounder) if err != nil { return []byte{}, fmt.Errorf("error generating confounder: %v", err) } k1 := key k2 := HMAC(k1, UsageToMSMsgType(usage)) toenc := append(confounder, data...) chksum := HMAC(k2, toenc) k3 := HMAC(k2, chksum) ed, err := EncryptData(k3, toenc, e) if err != nil { return []byte{}, fmt.Errorf("error encrypting data: %v", err) } msg := append(chksum, ed...) return msg, nil } // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 4757. // The integrity of the message is also verified. func DecryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { checksum := data[:e.GetHMACBitLength()/8] ct := data[e.GetHMACBitLength()/8:] _, k2, k3 := deriveKeys(key, checksum, usage, export) pt, err := DecryptData(k3, ct, e) if err != nil { return []byte{}, fmt.Errorf("error decrypting data: %v", err) } if !VerifyIntegrity(k2, pt, data, e) { return []byte{}, errors.New("integrity checksum incorrect") } return pt[e.GetConfounderByteSize():], nil } // VerifyIntegrity checks the integrity checksum of the data matches that calculated from the decrypted data. func VerifyIntegrity(key, pt, data []byte, e etype.EType) bool { chksum := HMAC(key, pt) if bytes.Equal(chksum, data[:e.GetHMACBitLength()/8]) { return true } return false } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/keyDerivation.go000066400000000000000000000023421362537225700263430ustar00rootroot00000000000000package rfc4757 import ( "bytes" "encoding/hex" "errors" "fmt" "io" "golang.org/x/crypto/md4" ) // StringToKey returns a key derived from the string provided according to the definition in RFC 4757. func StringToKey(secret string) ([]byte, error) { b := make([]byte, len(secret)*2, len(secret)*2) for i, r := range secret { u := fmt.Sprintf("%04x", r) c, err := hex.DecodeString(u) if err != nil { return []byte{}, errors.New("character could not be encoded") } // Swap round the two bytes to make little endian as we put into byte slice b[2*i] = c[1] b[2*i+1] = c[0] } r := bytes.NewReader(b) h := md4.New() _, err := io.Copy(h, r) if err != nil { return []byte{}, err } return h.Sum(nil), nil } func deriveKeys(key, checksum []byte, usage uint32, export bool) (k1, k2, k3 []byte) { //if export { // L40 := make([]byte, 14, 14) // copy(L40, []byte(`fortybits`)) // k1 = HMAC(key, L40) //} else { // tb := MessageTypeBytes(usage) // k1 = HMAC(key, tb) //} //k2 = k1[:16] //if export { // mask := []byte{0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB,0xAB} // copy(k1[7:16], mask) //} //k3 = HMAC(k1, checksum) //return k1 = key k2 = HMAC(k1, UsageToMSMsgType(usage)) k3 = HMAC(k2, checksum) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/keyDerivation_test.go000066400000000000000000000006511362537225700274030ustar00rootroot00000000000000package rfc4757 import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const ( testPassword = "foo" testKey = "ac8e657f83df82beea5d43bdaf7800cc" ) func TestStringToKey(t *testing.T) { t.Parallel() kb, err := StringToKey(testPassword) if err != nil { t.Fatalf("Error deriving key from string: %v", err) } k := hex.EncodeToString(kb) assert.Equal(t, testKey, k, "Key not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc4757/msgtype.go000066400000000000000000000007731362537225700252240ustar00rootroot00000000000000package rfc4757 import "encoding/binary" // UsageToMSMsgType converts Kerberos key usage numbers to Microsoft message type encoded as a little-endian four byte slice. func UsageToMSMsgType(usage uint32) []byte { // Translate usage numbers to the Microsoft T numbers switch usage { case 3: usage = 8 case 9: usage = 8 case 23: usage = 13 } // Now convert to bytes tb := make([]byte, 4) // We force an int32 input so we can't go over 4 bytes binary.PutUvarint(tb, uint64(usage)) return tb } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/000077500000000000000000000000001362537225700231705ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/encryption.go000066400000000000000000000102561362537225700257150ustar00rootroot00000000000000// Package rfc8009 provides encryption and checksum methods as specified in RFC 8009 package rfc8009 import ( "crypto/aes" "crypto/hmac" "crypto/rand" "errors" "fmt" "gopkg.in/jcmturner/aescts.v1" "gopkg.in/jcmturner/gokrb5.v5/crypto/common" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 8009. func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { kl := e.GetKeyByteSize() if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { kl = 32 } if len(key) != kl { return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) } ivz := make([]byte, aes.BlockSize) return aescts.Encrypt(key, ivz, data) } // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 8009. // The encrypted data is concatenated with its integrity hash to create an encrypted message. func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { kl := e.GetKeyByteSize() if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { kl = 32 } if len(key) != kl { return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key)) } if len(key) != e.GetKeyByteSize() { } //confounder c := make([]byte, e.GetConfounderByteSize()) _, err := rand.Read(c) if err != nil { return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) } plainBytes := append(c, message...) // Derive key for encryption from usage var k []byte if usage != 0 { k, err = e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) } } // Encrypt the data iv, b, err := e.EncryptData(k, plainBytes) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } ivz := make([]byte, e.GetConfounderByteSize()) ih, err := GetIntegityHash(ivz, b, key, usage, e) if err != nil { return iv, b, fmt.Errorf("error encrypting data: %v", err) } b = append(b, ih...) return iv, b, nil } // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 8009. func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { kl := e.GetKeyByteSize() if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { kl = 32 } if len(key) != kl { return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key)) } ivz := make([]byte, aes.BlockSize) return aescts.Decrypt(key, ivz, data) } // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 8009. // The integrity of the message is also verified. func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { //Derive the key k, err := e.DeriveKey(key, common.GetUsageKe(usage)) if err != nil { return nil, fmt.Errorf("error deriving key: %v", err) } // Strip off the checksum from the end b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) if err != nil { return nil, err } //Verify checksum if !e.VerifyIntegrity(key, ciphertext, b, usage) { return nil, errors.New("integrity verification failed") } //Remove the confounder bytes return b[e.GetConfounderByteSize():], nil } // GetIntegityHash returns a keyed integrity hash of the bytes provided as defined in RFC 8009 func GetIntegityHash(iv, c, key []byte, usage uint32, e etype.EType) ([]byte, error) { // Generate and append integrity hash ib := append(iv, c...) return common.GetIntegrityHash(ib, key, usage, e) } // VerifyIntegrity verifies the integrity of cipertext bytes ct. func VerifyIntegrity(key, ct []byte, usage uint32, etype etype.EType) bool { h := make([]byte, etype.GetHMACBitLength()/8) copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:]) ivz := make([]byte, etype.GetConfounderByteSize()) ib := append(ivz, ct[:len(ct)-(etype.GetHMACBitLength()/8)]...) expectedMAC, _ := common.GetIntegrityHash(ib, key, usage, etype) return hmac.Equal(h, expectedMAC) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/crypto/rfc8009/keyDerivation.go000066400000000000000000000101551362537225700263360ustar00rootroot00000000000000package rfc8009 import ( "crypto/hmac" "encoding/binary" "encoding/hex" "errors" "golang.org/x/crypto/pbkdf2" "gopkg.in/jcmturner/gokrb5.v5/crypto/etype" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" ) const ( s2kParamsZero = 32768 ) // DeriveRandom for key derivation as defined in RFC 8009 func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) { h := e.GetHashFunc()() return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil } // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods. // // https://tools.ietf.org/html/rfc8009#section-5 func DeriveKey(protocolKey, label []byte, e etype.EType) []byte { var context []byte var kl int // Key length is longer for aes256-cts-hmac-sha384-192 is it is a Ke or from StringToKey (where label is "kerberos") if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { switch label[len(label)-1] { case 0x73: // 0x73 is "s" so label could be kerberos meaning StringToKey so now check if the label is "kerberos" kerblabel := []byte("kerberos") if len(label) != len(kerblabel) { break } for i, b := range label { if b != kerblabel[i] { kl = e.GetKeySeedBitLength() break } } if kl == 0 { // This is StringToKey kl = 256 } case 0xAA: // This is a Ke kl = 256 } } if kl == 0 { kl = e.GetKeySeedBitLength() } return e.RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e)) } // RandomToKey returns a key from the bytes provided according to the definition in RFC 8009. func RandomToKey(b []byte) []byte { return b } // StringToKey returns a key derived from the string provided according to the definition in RFC 8009. func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) { i, err := S2KparamsToItertions(s2kparams) if err != nil { return nil, err } return StringToKeyIter(secret, salt, int(i), e) } // StringToKeyIter returns a key derived from the string provided according to the definition in RFC 8009. func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) { tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e)) return e.DeriveKey(tkey, []byte("kerberos")) } // StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0 func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte { kl := e.GetKeyByteSize() if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { kl = 32 } return pbkdf2.Key([]byte(secret), []byte(salt), iterations, kl, e.GetHashFunc()) } // KDF_HMAC_SHA2 key derivation: https://tools.ietf.org/html/rfc8009#section-3 func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte { //k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes. k := make([]byte, 4, 4) binary.BigEndian.PutUint32(k, uint32(kl)) c := make([]byte, 4, 4) binary.BigEndian.PutUint32(c, uint32(1)) c = append(c, label...) c = append(c, byte(uint8(0))) if len(context) > 0 { c = append(c, context...) } c = append(c, k...) mac := hmac.New(e.GetHashFunc(), protocolKey) mac.Write(c) return mac.Sum(nil)[:(kl / 8)] } // GetSaltP returns the salt value based on the etype name: https://tools.ietf.org/html/rfc8009#section-4 func GetSaltP(salt, ename string) string { b := []byte(ename) b = append(b, byte(uint8(0))) b = append(b, []byte(salt)...) return string(b) } // S2KparamsToItertions converts the string representation of iterations to an integer for RFC 8009. func S2KparamsToItertions(s2kparams string) (int, error) { var i uint32 if len(s2kparams) != 8 { return s2kParamsZero, errors.New("Invalid s2kparams length") } b, err := hex.DecodeString(s2kparams) if err != nil { return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes") } i = binary.BigEndian.Uint32(b) //buf := bytes.NewBuffer(b) //err = binary.Read(buf, binary.BigEndian, &i) if err != nil { return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32") } return int(i), nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/000077500000000000000000000000001362537225700223735ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/example-AD.go000066400000000000000000000072421362537225700246440ustar00rootroot00000000000000// +build examples package main import ( "encoding/hex" "fmt" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/service" "gopkg.in/jcmturner/gokrb5.v5/testdata" "io/ioutil" "log" "net/http" "net/http/httptest" "os" ) func main() { s := httpServer() defer s.Close() b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) cl := client.NewClientWithKeytab("testuser1", "USER.GOKRB5", kt) cl.WithConfig(c) httpRequest(s.URL, cl) b, _ = hex.DecodeString(testdata.TESTUSER2_KEYTAB) kt, _ = keytab.Parse(b) c, _ = config.NewConfigFromString(testdata.TEST_KRB5CONF) cl = client.NewClientWithKeytab("testuser2", "USER.GOKRB5", kt) cl.WithConfig(c) httpRequest(s.URL, cl) //httpRequest("http://host.test.gokrb5/index.html") } func httpRequest(url string, cl client.Client) { l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile) err := cl.Login() if err != nil { l.Printf("Error on AS_REQ: %v\n", err) } r, _ := http.NewRequest("GET", url, nil) err = cl.SetSPNEGOHeader(r, "HTTP/host.res.gokrb5") if err != nil { l.Printf("Error setting client SPNEGO header: %v", err) } httpResp, err := http.DefaultClient.Do(r) if err != nil { l.Printf("Request error: %v\n", err) } fmt.Fprintf(os.Stdout, "Response Code: %v\n", httpResp.StatusCode) content, _ := ioutil.ReadAll(httpResp.Body) fmt.Fprintf(os.Stdout, "Response Body:\n%s\n", content) } func httpServer() *httptest.Server { l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile) b, _ := hex.DecodeString(testdata.SYSHTTP_KEYTAB) kt, _ := keytab.Parse(b) th := http.HandlerFunc(testAppHandler) s := httptest.NewServer(service.SPNEGOKRB5Authenticate(th, kt, "sysHTTP", false, l)) return s } func testAppHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() fmt.Fprint(w, "\n

TEST.GOKRB5 Handler

\n") if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser { if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok { fmt.Fprintf(w, "
  • Authenticed user: %s
  • \n", creds.UserName()) fmt.Fprintf(w, "
  • User's realm: %s
  • \n", creds.Domain()) fmt.Fprint(w, "
  • Authz Attributes (Group Memberships):
    • \n") for _, s := range creds.AuthzAttributes() { fmt.Fprintf(w, "
    • %v
    • \n", s) } fmt.Fprint(w, "
    \n") if ADCreds, ok := creds.Attributes[credentials.AttributeKeyADCredentials].(credentials.ADCredentials); ok { // Now access the fields of the ADCredentials struct. For example: fmt.Fprintf(w, "
  • EffectiveName: %v
  • \n", ADCreds.EffectiveName) fmt.Fprintf(w, "
  • FullName: %v
  • \n", ADCreds.FullName) fmt.Fprintf(w, "
  • UserID: %v
  • \n", ADCreds.UserID) fmt.Fprintf(w, "
  • PrimaryGroupID: %v
  • \n", ADCreds.PrimaryGroupID) fmt.Fprintf(w, "
  • Group SIDs: %v
  • \n", ADCreds.GroupMembershipSIDs) fmt.Fprintf(w, "
  • LogOnTime: %v
  • \n", ADCreds.LogOnTime) fmt.Fprintf(w, "
  • LogOffTime: %v
  • \n", ADCreds.LogOffTime) fmt.Fprintf(w, "
  • PasswordLastSet: %v
  • \n", ADCreds.PasswordLastSet) fmt.Fprintf(w, "
  • LogonServer: %v
  • \n", ADCreds.LogonServer) fmt.Fprintf(w, "
  • LogonDomainName: %v
  • \n", ADCreds.LogonDomainName) fmt.Fprintf(w, "
  • LogonDomainID: %v
  • \n", ADCreds.LogonDomainID) } fmt.Fprint(w, "
") } } else { w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, "Authentication failed") } fmt.Fprint(w, "") return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/example.go000066400000000000000000000050011362537225700243510ustar00rootroot00000000000000// +build examples package main import ( "encoding/hex" "fmt" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/service" "gopkg.in/jcmturner/gokrb5.v5/testdata" "io/ioutil" "log" "net/http" "net/http/httptest" "os" ) func main() { s := httpServer() defer s.Close() b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) c.LibDefaults.NoAddresses = true cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) httpRequest(s.URL, cl) b, _ = hex.DecodeString(testdata.TESTUSER2_KEYTAB) kt, _ = keytab.Parse(b) c, _ = config.NewConfigFromString(testdata.TEST_KRB5CONF) c.LibDefaults.NoAddresses = true cl = client.NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt) cl.WithConfig(c) httpRequest(s.URL, cl) } func httpRequest(url string, cl client.Client) { l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile) err := cl.Login() if err != nil { l.Printf("Error on AS_REQ: %v\n", err) } r, _ := http.NewRequest("GET", url, nil) err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { l.Printf("Error setting client SPNEGO header: %v", err) } httpResp, err := http.DefaultClient.Do(r) if err != nil { l.Printf("Request error: %v\n", err) } fmt.Fprintf(os.Stdout, "Response Code: %v\n", httpResp.StatusCode) content, _ := ioutil.ReadAll(httpResp.Body) fmt.Fprintf(os.Stdout, "Response Body:\n%s\n", content) } func httpServer() *httptest.Server { l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile) b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) th := http.HandlerFunc(testAppHandler) s := httptest.NewServer(service.SPNEGOKRB5Authenticate(th, kt, "", false, l)) return s } func testAppHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() fmt.Fprint(w, "\n

TEST.GOKRB5 Handler

\n") if validuser, ok := ctx.Value(service.CTXKeyAuthenticated).(bool); ok && validuser { if creds, ok := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials); ok { fmt.Fprintf(w, "
  • Authenticed user: %s
  • \n", creds.Username) fmt.Fprintf(w, "
  • User's realm: %s
\n", creds.Realm) } } else { w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, "Authentication failed") } fmt.Fprint(w, "") return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/httpClient.go000066400000000000000000000035211362537225700250410ustar00rootroot00000000000000// +build examples package main import ( "encoding/hex" "fmt" "io/ioutil" "net/http" "os" //"github.com/pkg/profile" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) const ( port = ":9080" kRB5CONF = `[libdefaults] default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false ticket_lifetime = 24h forwardable = yes default_tkt_enctypes = aes256-cts-hmac-sha1-96 default_tgs_enctypes = aes256-cts-hmac-sha1-96 [realms] TEST.GOKRB5 = { kdc = 127.0.0.1:88 admin_server = 127.0.0.1:749 default_domain = test.gokrb5 } [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 ` ) func main() { //defer profile.Start(profile.TraceProfile).Stop() // Load the keytab kb, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, err := keytab.Parse(kb) if err != nil { panic(err) } // Create the client with the keytab cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) // Load the client krb5 config conf, err := config.NewConfigFromString(kRB5CONF) if err != nil { panic(err) } addr := os.Getenv("TEST_KDC_ADDR") if addr != "" { conf.Realms[0].KDC = []string{addr + ":88"} } // Apply the config to the client cl.WithConfig(conf) // Log in the client err = cl.Login() if err != nil { panic(err) } // Form the request url := "http://localhost" + port r, err := http.NewRequest("GET", url, nil) if err != nil { panic(err) } // Apply the client's auth headers to the request err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5") if err != nil { panic(err) } // Make the request resp, err := http.DefaultClient.Do(r) if err != nil { panic(err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(b)) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/examples/httpServer.go000066400000000000000000000026121362537225700250710ustar00rootroot00000000000000// +build examples package main import ( "encoding/hex" "fmt" "log" "net/http" "os" //"github.com/pkg/profile" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/service" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) const ( port = ":9080" ) func main() { //defer profile.Start(profile.TraceProfile).Stop() // Create logger l := log.New(os.Stderr, "GOKRB5 Service: ", log.Ldate|log.Ltime|log.Lshortfile) // Load the service's keytab b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) // Create the application's specific handler th := http.HandlerFunc(testAppHandler) // Set up handler mappings wrapping in the SPNEGOKRB5Authenticate handler wrapper mux := http.NewServeMux() mux.Handle("/", service.SPNEGOKRB5Authenticate(th, kt, "", false, l)) // Start up the web server log.Fatal(http.ListenAndServe(port, mux)) } // Simple application specific handler func testAppHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) ctx := r.Context() creds := ctx.Value(service.CTXKeyCredentials).(credentials.Credentials) fmt.Fprintf(w, `

GOKRB5 Handler

  • Authenticed user: %s
  • User's realm: %s
  • Authn time: %v
  • Session ID: %s
    • `, creds.UserName(), creds.Domain(), creds.AuthTime(), creds.SessionID(), ) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gokrb5.go000066400000000000000000000001151362537225700222720ustar00rootroot00000000000000// Package gokrb5 provides a Kerberos 5 implementation for Go package gokrb5 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/000077500000000000000000000000001362537225700220435ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/ContextFlags.go000066400000000000000000000012161362537225700247730ustar00rootroot00000000000000package gssapi import "github.com/jcmturner/gofork/encoding/asn1" /* ContextFlags ::= BIT STRING { delegFlag (0), mutualFlag (1), replayFlag (2), sequenceFlag (3), anonFlag (4), confFlag (5), integFlag (6) } (SIZE (32)) */ const ( delegFlag = 0 mutualFlag = 1 replayFlag = 2 sequenceFlag = 3 anonFlag = 4 confFlag = 5 integFlag = 6 ) // ContextFlags flags for GSSAPI type ContextFlags asn1.BitString // NewContextFlags creates a new ContextFlags instance. func NewContextFlags() ContextFlags { var c ContextFlags c.BitLength = 32 c.Bytes = make([]byte, 4) return c } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/MechType.go000066400000000000000000000005321362537225700241100ustar00rootroot00000000000000package gssapi import "github.com/jcmturner/gofork/encoding/asn1" // MechTypeOIDKRB5 is the MechType OID for Kerberos 5 var MechTypeOIDKRB5 = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2} // MechTypeOIDMSLegacyKRB5 is the MechType OID for MS legacy Kerberos 5 var MechTypeOIDMSLegacyKRB5 = asn1.ObjectIdentifier{1, 2, 840, 48018, 1, 2, 2} golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/NegotiationToken.go000066400000000000000000000065531362537225700256640ustar00rootroot00000000000000package gssapi import ( "errors" "fmt" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) /* https://msdn.microsoft.com/en-us/library/ms995330.aspx */ // NegTokenInit implements Negotiation Token of type Init type NegTokenInit struct { MechTypes []asn1.ObjectIdentifier `asn1:"explicit,tag:0"` ReqFlags ContextFlags `asn1:"explicit,optional,tag:1"` MechToken []byte `asn1:"explicit,optional,tag:2"` MechTokenMIC []byte `asn1:"explicit,optional,tag:3"` } // NegTokenResp implements Negotiation Token of type Resp/Targ type NegTokenResp struct { NegState asn1.Enumerated `asn1:"explicit,tag:0"` SupportedMech asn1.ObjectIdentifier `asn1:"explicit,optional,tag:1"` ResponseToken []byte `asn1:"explicit,optional,tag:2"` MechListMIC []byte `asn1:"explicit,optional,tag:3"` } // NegTokenTarg implements Negotiation Token of type Resp/Targ type NegTokenTarg NegTokenResp // UnmarshalNegToken umarshals and returns either a NegTokenInit or a NegTokenResp. // // The boolean indicates if the response is a NegTokenInit. // If error is nil and the boolean is false the response is a NegTokenResp. func UnmarshalNegToken(b []byte) (bool, interface{}, error) { var a asn1.RawValue _, err := asn1.Unmarshal(b, &a) if err != nil { return false, nil, fmt.Errorf("error unmarshalling NegotiationToken: %v", err) } switch a.Tag { case 0: var negToken NegTokenInit _, err = asn1.Unmarshal(a.Bytes, &negToken) if err != nil { return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err) } return true, negToken, nil case 1: var negToken NegTokenResp _, err = asn1.Unmarshal(a.Bytes, &negToken) if err != nil { return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err) } return false, negToken, nil default: return false, nil, errors.New("unknown choice type for NegotiationToken") } } // Marshal an Init negotiation token func (n *NegTokenInit) Marshal() ([]byte, error) { b, err := asn1.Marshal(*n) if err != nil { return nil, err } nt := asn1.RawValue{ Tag: 0, Class: 2, IsCompound: true, Bytes: b, } nb, err := asn1.Marshal(nt) if err != nil { return nil, err } return nb, nil } // Marshal a Resp/Targ negotiation token func (n *NegTokenResp) Marshal() ([]byte, error) { b, err := asn1.Marshal(*n) if err != nil { return nil, err } nt := asn1.RawValue{ Tag: 1, Class: 2, IsCompound: true, Bytes: b, } nb, err := asn1.Marshal(nt) if err != nil { return nil, err } return nb, nil } // NewNegTokenInitKrb5 creates new Init negotiation token for Kerberos 5 func NewNegTokenInitKrb5(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) { mt, err := NewAPREQMechToken(creds, tkt, sessionKey, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{}) if err != nil { return NegTokenInit{}, fmt.Errorf("error getting MechToken; %v", err) } mtb, err := mt.Marshal() if err != nil { return NegTokenInit{}, fmt.Errorf("error marshalling MechToken; %v", err) } return NegTokenInit{ MechTypes: []asn1.ObjectIdentifier{MechTypeOIDKRB5}, MechToken: mtb, }, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/NegotiationToken_test.go000066400000000000000000000100771362537225700267170ustar00rootroot00000000000000package gssapi import ( "encoding/hex" "testing" "github.com/jcmturner/gofork/encoding/asn1" "github.com/stretchr/testify/assert" ) const ( testNegTokenInit = "a08202aa308202a6a027302506092a864886f71201020206052b0501050206092a864882f71201020206062b0601050205a2820279048202756082027106092a864886f71201020201006e8202603082025ca003020105a10302010ea20703050000000000a38201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020103a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a282011904820115d4bd890abc456f44e2e7a2e8111bd6767abf03266dfcda97c629af2ece450a5ae1f145e4a4d1bc2c848e66a6c6b31d9740b26b03cdbd2570bfcf126e90adf5f5ebce9e283ff5086da47b129b14fc0aabd4d1df9c1f3c72b80cc614dfc28783450b2c7b7749651f432b47aaa2ff158c0066b757f3fb00dd7b4f63d68276c76373ecdd3f19c66ebc43a81e577f3c263b878356f57e8d6c4eccd587b81538e70392cf7e73fc12a6f7c537a894a7bb5566c83ac4d69757aa320a51d8d690017aebf952add1889adfc3307b0e6cd8c9b57cf8589fbe52800acb6461c25473d49faa1bdceb8bce3f61db23f9cd6a09d5adceb411e1c4546b30b33331e570fd6bc50aa403557e75f488e759750ea038aab6454667d9b64f41a481d23081cfa003020112a281c70481c4d67ba2ae4cf5d917caab1d863605249320e90482563662ed92408a543b6ad5edeb8f9375e9060a205491df082fd2a5fec93dfb76f41012bb60cae20f07adbb77a1aa56f0521f36e1ea10dc9fb762902b254dd7664d0bcc6f751f2003e41990af1b4330d10477bfad638b9f0b704ac80cc47731f8ec8d801762bad8884b8de90adb1dbe7fc7b0ffafd38fb5eb8b6547cee30d89873281ce63ad70042a13478b1a7c2bdde0f223ace62dbb84e2d06f1070f4265f66e0544449335e2fcc4d0aee5bf81c5999" testNegTokenResp = "a1143012a0030a0100a10b06092a864886f712010202" ) func TestUnmarshal_negTokenInit(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testNegTokenInit) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } isInit, nt, err := UnmarshalNegToken(b) if err != nil { t.Fatalf("Error unmarshalling negotiation token: %v", err) } assert.IsType(t, NegTokenInit{}, nt, "Not the expected type NegTokenInit") assert.True(t, isInit, "Boolean indicating type is negTokenInit is not true") nInit := nt.(NegTokenInit) assert.Equal(t, 4, len(nInit.MechTypes)) expectMechTypes := []asn1.ObjectIdentifier{ []int{1, 2, 840, 113554, 1, 2, 2}, []int{1, 3, 5, 1, 5, 2}, []int{1, 2, 840, 48018, 1, 2, 2}, []int{1, 3, 6, 1, 5, 2, 5}, } assert.Equal(t, expectMechTypes, nInit.MechTypes, "MechTypes list in NegTokenInit not as expected") } func TestMarshal_negTokenInit(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testNegTokenInit) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } _, nt, err := UnmarshalNegToken(b) if err != nil { t.Fatalf("Error unmarshalling negotiation token: %v", err) } nInit := nt.(NegTokenInit) mb, err := nInit.Marshal() if err != nil { t.Fatalf("Error marshalling negotiation init token: %v", err) } assert.Equal(t, b, mb, "Marshalled bytes not as expected for NegTokenInit") } func TestUnmarshal_negTokenResp(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testNegTokenResp) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } isInit, nt, err := UnmarshalNegToken(b) if err != nil { t.Fatalf("Error unmarshalling negotiation token: %v", err) } assert.IsType(t, NegTokenResp{}, nt, "Not the expected type NegTokenResp") assert.False(t, isInit, "Boolean indicating type is negTokenInit is not false") nResp := nt.(NegTokenResp) assert.Equal(t, asn1.Enumerated(0), nResp.NegState) assert.Equal(t, MechTypeOIDKRB5, nResp.SupportedMech, "SupportedMech type not as expected.") } func TestMarshal_negTokenResp(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testNegTokenResp) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } _, nt, err := UnmarshalNegToken(b) if err != nil { t.Fatalf("Error unmarshalling negotiation token: %v", err) } nResp := nt.(NegTokenResp) mb, err := nResp.Marshal() if err != nil { t.Fatalf("Error marshalling negotiation init token: %v", err) } assert.Equal(t, b, mb, "Marshalled bytes not as expected for NegTokenResp") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/WrapToken.go000066400000000000000000000167641362537225700243220ustar00rootroot00000000000000package gssapi import ( "bytes" "encoding/binary" "encoding/hex" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/types" ) /* From RFC 4121, section 4.2.6.2: Quick notes: - "EC" or "Extra Count" refers to the length of the cheksum. - "Flags" (complete details in section 4.2.2) is a set of bits: - if bit 0 is set, it means the token was sent by the acceptor (generally the kerberized service). - bit 1 indicates that the token's payload is encrypted - bit 2 indicates if the message is protected using a subkey defined by the acceptor. - When computing checksums, EC and RRC MUST be set to 0. - Wrap Tokens are not ASN.1 encoded. */ const ( HdrLen = 16 // Length of the Wrap Token's header FillerByte byte = 0xFF ) // WrapToken represents a GSS API Wrap token, as defined in RFC 4121. // It contains the header fields, the payload and the checksum, and provides // the logic for converting to/from bytes plus computing and verifying checksums type WrapToken struct { // const GSS Token ID: 0x0504 Flags byte // contains three flags: acceptor, sealed, acceptor subkey // const Filler: 0xFF EC uint16 // checksum length. big-endian RRC uint16 // right rotation count. big-endian SndSeqNum uint64 // sender's sequence number. big-endian Payload []byte // your data! :) CheckSum []byte // authenticated checksum of { payload | header } } // Return the 2 bytes identifying a GSS API Wrap token func getGssWrapTokenId() *[2]byte { return &[2]byte{0x05, 0x04} } // Marshal the WrapToken into a byte slice. // The payload should have been set and the checksum computed, otherwise an error is returned. func (wt *WrapToken) Marshal() ([]byte, error) { if wt.CheckSum == nil { return nil, errors.New("checksum has not been set") } if wt.Payload == nil { return nil, errors.New("payload has not been set") } pldOffset := HdrLen // Offset of the payload in the token chkSOffset := HdrLen + len(wt.Payload) // Offset of the checksum in the token bytes := make([]byte, chkSOffset+int(wt.EC)) copy(bytes[0:], getGssWrapTokenId()[:]) bytes[2] = wt.Flags bytes[3] = FillerByte binary.BigEndian.PutUint16(bytes[4:6], wt.EC) binary.BigEndian.PutUint16(bytes[6:8], wt.RRC) binary.BigEndian.PutUint64(bytes[8:16], wt.SndSeqNum) copy(bytes[pldOffset:], wt.Payload) copy(bytes[chkSOffset:], wt.CheckSum) return bytes, nil } // ComputeAndSetCheckSum uses the passed encryption key and key usage to compute the checksum over the payload and // the header, and sets the CheckSum field of this WrapToken. // If the payload has not been set or the checksum has already been set, an error is returned. func (wt *WrapToken) ComputeAndSetCheckSum(key types.EncryptionKey, keyUsage uint32) error { if wt.Payload == nil { return errors.New("payload has not been set") } if wt.CheckSum != nil { return errors.New("checksum has already been computed") } chkSum, cErr := wt.ComputeCheckSum(key, keyUsage) if cErr != nil { return cErr } wt.CheckSum = chkSum return nil } // ComputeCheckSum computes and returns the checksum of this token, computed using the passed key and key usage. // Conforms to RFC 4121 in that the checksum will be computed over { body | header }, // with the EC and RRC flags zeroed out. // In the context of Kerberos Wrap tokens, mostly keyusage GSSAPI_ACCEPTOR_SEAL (=22) // and GSSAPI_INITIATOR_SEAL (=24) will be used. // Note: This will NOT update the struct's Checksum field. func (wt *WrapToken) ComputeCheckSum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) { if wt.Payload == nil { return nil, errors.New("cannot compute checksum with uninitialized payload") } // Build a slice containing { payload | header } checksumMe := make([]byte, HdrLen+len(wt.Payload)) copy(checksumMe[0:], wt.Payload) copy(checksumMe[len(wt.Payload):], getChecksumHeader(wt.Flags, wt.SndSeqNum)) encType, err := crypto.GetEtype(key.KeyType) if err != nil { return nil, err } return encType.GetChecksumHash(key.KeyValue, checksumMe, keyUsage) } // Build a header suitable for a checksum computation func getChecksumHeader(flags byte, senderSeqNum uint64) []byte { header := make([]byte, 16) copy(header[0:], []byte{0x05, 0x04, flags, 0xFF, 0x00, 0x00, 0x00, 0x00}) binary.BigEndian.PutUint64(header[8:], senderSeqNum) return header } // VerifyCheckSum computes the token's checksum with the provided key and usage, // and compares it to the checksum present in the token. // In case of any failure, (false, Err) is returned, with Err an explanatory error. func (wt *WrapToken) VerifyCheckSum(key types.EncryptionKey, keyUsage uint32) (bool, error) { computed, cErr := wt.ComputeCheckSum(key, keyUsage) if cErr != nil { return false, cErr } if !bytes.Equal(computed, wt.CheckSum) { return false, fmt.Errorf( "checksum mismatch. Computed: %s, Contained in token: %s", hex.EncodeToString(computed), hex.EncodeToString(wt.CheckSum)) } return true, nil } // Unmarshal bytes into the corresponding WrapToken. // If expectFromAcceptor is true, we expect the token to have been emitted by the gss acceptor, // and will check the according flag, returning an error if the token does not match the expectation. func (wt *WrapToken) Unmarshal(b []byte, expectFromAcceptor bool) error { // Check if we can read a whole header if len(b) < 16 { return errors.New("bytes shorter than header length") } // Is the Token ID correct? if !bytes.Equal(getGssWrapTokenId()[:], b[0:2]) { return fmt.Errorf("wrong Token ID. Expected %s, was %s", hex.EncodeToString(getGssWrapTokenId()[:]), hex.EncodeToString(b[0:2])) } // Check the acceptor flag flags := b[2] isFromAcceptor := flags&0x01 == 1 if isFromAcceptor && !expectFromAcceptor { return errors.New("unexpected acceptor flag is set: not expecting a token from the acceptor") } if !isFromAcceptor && expectFromAcceptor { return errors.New("expected acceptor flag is not set: expecting a token from the acceptor, not the initiator") } // Check the filler byte if b[3] != FillerByte { return fmt.Errorf("unexpected filler byte: expecting 0xFF, was %s ", hex.EncodeToString(b[3:4])) } checksumL := binary.BigEndian.Uint16(b[4:6]) // Sanity check on the checksum length if int(checksumL) > len(b)-HdrLen { return fmt.Errorf("inconsistent checksum length: %d bytes to parse, checksum length is %d", len(b), checksumL) } wt.Flags = flags wt.EC = checksumL wt.RRC = binary.BigEndian.Uint16(b[6:8]) wt.SndSeqNum = binary.BigEndian.Uint64(b[8:16]) wt.Payload = b[16 : len(b)-int(checksumL)] wt.CheckSum = b[len(b)-int(checksumL):] return nil } // NewInitiatorToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum. // Other flags are set to 0, and the RRC and sequence number are initialized to 0. // Note that in certain circumstances you may need to provide a sequence number that has been defined earlier. // This is currently not supported. func NewInitiatorToken(payload []byte, key types.EncryptionKey) (*WrapToken, error) { encType, err := crypto.GetEtype(key.KeyType) if err != nil { return nil, err } token := WrapToken{ Flags: 0x00, // all zeroed out (this is a token sent by the initiator) // Checksum size: lenth of output of the HMAC function, in bytes. EC: uint16(encType.GetHMACBitLength() / 8), RRC: 0, SndSeqNum: 0, Payload: payload, } if err := token.ComputeAndSetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil { return nil, err } return &token, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/WrapToken_test.go000066400000000000000000000153551362537225700253540ustar00rootroot00000000000000package gssapi import ( "encoding/binary" "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( // What a kerberized server might send testChallengeFromAcceptor = "050401ff000c000000000000575e85d601010000853b728d5268525a1386c19f" // What an initiator client could reply testChallengeReplyFromInitiator = "050400ff000c000000000000000000000101000079a033510b6f127212242b97" // session key used to sign the tokens above sessionKey = "14f9bde6b50ec508201a97f74c4e5bd3" sessionKeyType = 17 acceptorSeal = keyusage.GSSAPI_ACCEPTOR_SEAL initiatorSeal = keyusage.GSSAPI_INITIATOR_SEAL ) func getSessionKey() types.EncryptionKey { key, _ := hex.DecodeString(sessionKey) return types.EncryptionKey{ KeyType: sessionKeyType, KeyValue: key, } } func getChallengeReference() *WrapToken { challenge, _ := hex.DecodeString(testChallengeFromAcceptor) return &WrapToken{ Flags: 0x01, EC: 12, RRC: 0, SndSeqNum: binary.BigEndian.Uint64(challenge[8:16]), Payload: []byte{0x01, 0x01, 0x00, 0x00}, CheckSum: challenge[20:32], } } func getChallengeReferenceNoChksum() *WrapToken { c := getChallengeReference() c.CheckSum = nil return c } func getResponseReference() *WrapToken { response, _ := hex.DecodeString(testChallengeReplyFromInitiator) return &WrapToken{ Flags: 0x00, EC: 12, RRC: 0, SndSeqNum: 0, Payload: []byte{0x01, 0x01, 0x00, 0x00}, CheckSum: response[20:32], } } func getResponseReferenceNoChkSum() *WrapToken { r := getResponseReference() r.CheckSum = nil return r } func TestUnmarshal_Challenge(t *testing.T) { t.Parallel() challenge, _ := hex.DecodeString(testChallengeFromAcceptor) var wt WrapToken err := wt.Unmarshal(challenge, true) assert.Nil(t, err, "Unexpected error occurred.") assert.Equal(t, getChallengeReference(), &wt, "Token not decoded as expected.") } func TestUnmarshalFailure_Challenge(t *testing.T) { t.Parallel() challenge, _ := hex.DecodeString(testChallengeFromAcceptor) var wt WrapToken err := wt.Unmarshal(challenge, false) assert.NotNil(t, err, "Expected error did not occur: a message from the acceptor cannot be expected to be sent from the initiator.") assert.Nil(t, wt.Payload, "Token fields should not have been initialised") assert.Nil(t, wt.CheckSum, "Token fields should not have been initialised") assert.Equal(t, byte(0x00), wt.Flags, "Token fields should not have been initialised") assert.Equal(t, uint16(0), wt.EC, "Token fields should not have been initialised") assert.Equal(t, uint16(0), wt.RRC, "Token fields should not have been initialised") assert.Equal(t, uint64(0), wt.SndSeqNum, "Token fields should not have been initialised") } func TestUnmarshal_ChallengeReply(t *testing.T) { t.Parallel() response, _ := hex.DecodeString(testChallengeReplyFromInitiator) var wt WrapToken err := wt.Unmarshal(response, false) assert.Nil(t, err, "Unexpected error occurred.") assert.Equal(t, getResponseReference(), &wt, "Token not decoded as expected.") } func TestUnmarshalFailure_ChallengeReply(t *testing.T) { t.Parallel() response, _ := hex.DecodeString(testChallengeReplyFromInitiator) var wt WrapToken err := wt.Unmarshal(response, true) assert.NotNil(t, err, "Expected error did not occur: a message from the initiator cannot be expected to be sent from the acceptor.") assert.Nil(t, wt.Payload, "Token fields should not have been initialised") assert.Nil(t, wt.CheckSum, "Token fields should not have been initialised") assert.Equal(t, byte(0x00), wt.Flags, "Token fields should not have been initialised") assert.Equal(t, uint16(0), wt.EC, "Token fields should not have been initialised") assert.Equal(t, uint16(0), wt.RRC, "Token fields should not have been initialised") assert.Equal(t, uint64(0), wt.SndSeqNum, "Token fields should not have been initialised") } func TestChallengeChecksumVerification(t *testing.T) { t.Parallel() challenge, _ := hex.DecodeString(testChallengeFromAcceptor) var wt WrapToken wt.Unmarshal(challenge, true) challengeOk, cErr := wt.VerifyCheckSum(getSessionKey(), acceptorSeal) assert.Nil(t, cErr, "Error occurred during checksum verification.") assert.True(t, challengeOk, "Checksum verification failed.") } func TestResponseChecksumVerification(t *testing.T) { t.Parallel() reply, _ := hex.DecodeString(testChallengeReplyFromInitiator) var wt WrapToken wt.Unmarshal(reply, false) replyOk, rErr := wt.VerifyCheckSum(getSessionKey(), initiatorSeal) assert.Nil(t, rErr, "Error occurred during checksum verification.") assert.True(t, replyOk, "Checksum verification failed.") } func TestChecksumVerificationFailure(t *testing.T) { t.Parallel() challenge, _ := hex.DecodeString(testChallengeFromAcceptor) var wt WrapToken wt.Unmarshal(challenge, true) // Test a failure with the correct key but wrong keyusage: challengeOk, cErr := wt.VerifyCheckSum(getSessionKey(), initiatorSeal) assert.NotNil(t, cErr, "Expected error did not occur.") assert.False(t, challengeOk, "Checksum verification succeeded when it should have failed.") wrongKeyVal, _ := hex.DecodeString("14f9bde6b50ec508201a97f74c4effff") badKey := types.EncryptionKey{ KeyType: sessionKeyType, KeyValue: wrongKeyVal, } // Test a failure with the wrong key but correct keyusage: wrongKeyOk, wkErr := wt.VerifyCheckSum(badKey, acceptorSeal) assert.NotNil(t, wkErr, "Expected error did not occur.") assert.False(t, wrongKeyOk, "Checksum verification succeeded when it should have failed.") } func TestMarshal_Challenge(t *testing.T) { t.Parallel() bytes, _ := getChallengeReference().Marshal() assert.Equal(t, testChallengeFromAcceptor, hex.EncodeToString(bytes), "Marshalling did not yield the expected result.") } func TestMarshal_ChallengeReply(t *testing.T) { t.Parallel() bytes, _ := getResponseReference().Marshal() assert.Equal(t, testChallengeReplyFromInitiator, hex.EncodeToString(bytes), "Marshalling did not yield the expected result.") } func TestMarshal_Failures(t *testing.T) { t.Parallel() noChkSum := getResponseReferenceNoChkSum() chkBytes, chkErr := noChkSum.Marshal() assert.Nil(t, chkBytes, "No bytes should be returned.") assert.NotNil(t, chkErr, "Expected an error as no checksum was set") noPayload := getResponseReference() noPayload.Payload = nil pldBytes, pldErr := noPayload.Marshal() assert.Nil(t, pldBytes, "No bytes should be returned.") assert.NotNil(t, pldErr, "Expected an error as no checksum was set") } func TestNewInitiatorTokenSignatureAndMarshalling(t *testing.T) { t.Parallel() token, tErr := NewInitiatorToken([]byte{0x01, 0x01, 0x00, 0x00}, getSessionKey()) assert.Nil(t, tErr, "Unexepected error.") assert.Equal(t, getResponseReference(), token, "Token failed to be marshalled to the expected bytes.") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/gssapi.go000066400000000000000000000055731362537225700236720ustar00rootroot00000000000000// Package gssapi implements Generic Security Services Application Program Interface required for SPNEGO kerberos authentication. package gssapi import ( "errors" "fmt" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // SPNEGO_OID is the OID for SPNEGO header type. var SPNEGO_OID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 2} // SPNEGO header struct type SPNEGO struct { Init bool Resp bool NegTokenInit NegTokenInit NegTokenResp NegTokenResp } // Unmarshal SPNEGO negotiation token func (s *SPNEGO) Unmarshal(b []byte) error { var r []byte var err error if b[0] != byte(161) { // Not a NegTokenResp/Targ could be a NegTokenInit var oid asn1.ObjectIdentifier r, err = asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0)) if err != nil { return fmt.Errorf("not a valid SPNEGO token: %v", err) } // Check the OID is the SPNEGO OID value if !oid.Equal(SPNEGO_OID) { return fmt.Errorf("OID %s does not match SPNEGO OID %s", oid.String(), SPNEGO_OID.String()) } } else { // Could be a NegTokenResp/Targ r = b } var a asn1.RawValue _, err = asn1.Unmarshal(r, &a) if err != nil { return fmt.Errorf("error unmarshalling SPNEGO: %v", err) } switch a.Tag { case 0: _, err = asn1.Unmarshal(a.Bytes, &s.NegTokenInit) if err != nil { return fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err) } s.Init = true case 1: _, err = asn1.Unmarshal(a.Bytes, &s.NegTokenResp) if err != nil { return fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err) } s.Resp = true default: return errors.New("unknown choice type for NegotiationToken") } return nil } // Marshal SPNEGO negotiation token func (s *SPNEGO) Marshal() ([]byte, error) { var b []byte if s.Init { hb, _ := asn1.Marshal(SPNEGO_OID) tb, err := s.NegTokenInit.Marshal() if err != nil { return b, fmt.Errorf("could not marshal NegTokenInit: %v", err) } b = append(hb, tb...) return asn1tools.AddASNAppTag(b, 0), nil } if s.Resp { b, err := s.NegTokenResp.Marshal() if err != nil { return b, fmt.Errorf("could not marshal NegTokenResp: %v", err) } return b, nil } return b, errors.New("SPNEGO cannot be marshalled. It contains neither a NegTokenInit or NegTokenResp") } // GetSPNEGOKrbNegTokenInit returns an SPNEGO struct containing a NegTokenInit. func GetSPNEGOKrbNegTokenInit(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey) (SPNEGO, error) { negTokenInit, err := NewNegTokenInitKrb5(creds, tkt, sessionKey) if err != nil { return SPNEGO{}, fmt.Errorf("could not create NegTokenInit: %v", err) } return SPNEGO{ Init: true, NegTokenInit: negTokenInit, }, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/gssapi_test.go000066400000000000000000000102231362537225700247150ustar00rootroot00000000000000package gssapi import ( "encoding/hex" "testing" "github.com/jcmturner/gofork/encoding/asn1" "github.com/stretchr/testify/assert" ) const ( testGSSAPIInit = "608202b606062b0601050502a08202aa308202a6a027302506092a864886f71201020206052b0501050206092a864882f71201020206062b0601050205a2820279048202756082027106092a864886f71201020201006e8202603082025ca003020105a10302010ea20703050000000000a38201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020103a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a282011904820115d4bd890abc456f44e2e7a2e8111bd6767abf03266dfcda97c629af2ece450a5ae1f145e4a4d1bc2c848e66a6c6b31d9740b26b03cdbd2570bfcf126e90adf5f5ebce9e283ff5086da47b129b14fc0aabd4d1df9c1f3c72b80cc614dfc28783450b2c7b7749651f432b47aaa2ff158c0066b757f3fb00dd7b4f63d68276c76373ecdd3f19c66ebc43a81e577f3c263b878356f57e8d6c4eccd587b81538e70392cf7e73fc12a6f7c537a894a7bb5566c83ac4d69757aa320a51d8d690017aebf952add1889adfc3307b0e6cd8c9b57cf8589fbe52800acb6461c25473d49faa1bdceb8bce3f61db23f9cd6a09d5adceb411e1c4546b30b33331e570fd6bc50aa403557e75f488e759750ea038aab6454667d9b64f41a481d23081cfa003020112a281c70481c4eb593beb5afcb1a2a669d54cb85a3772231559f2d40c9f8f053f218ba6eb084ed7efc467d94b88bcd189dda920d6e675ec001a6a2bca11f0a1de37f2f7ae9929f94a86d625b2ec1b213a88cbae6099dda7b172cd3bd1802cb177ae4554d59277004bfd3435248f55044fe7af7b2c9c5a3c43763278c585395aebe2856cdff9f2569d8b823564ce6be2d19748b910ec06bd3c0a9bc5de51ddcf7d875f1108ca6ad935f52d90cb62a18197d9b8e796bef0fbe1463f61df61cfbce6008ae9e1a2d2314a986d" testGSSAPIResp = "a1143012a0030a0100a10b06092a864886f712010202" ) func TestUnmarshal_SPNEGO_Init(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testGSSAPIInit) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } var s SPNEGO err = s.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshalling SPNEGO with NegTokenInit: %v", err) } assert.True(t, s.Init, "SPNEGO does not indicate it contains NegTokenInit as expected") assert.False(t, s.Resp, "SPNEGO indicates is contains a NegTokenResp but it shouldn't") assert.Equal(t, 4, len(s.NegTokenInit.MechTypes)) expectMechTypes := []asn1.ObjectIdentifier{ MechTypeOIDKRB5, []int{1, 3, 5, 1, 5, 2}, MechTypeOIDMSLegacyKRB5, []int{1, 3, 6, 1, 5, 2, 5}, } assert.Equal(t, expectMechTypes, s.NegTokenInit.MechTypes, "MechTypes list in NegTokenInit not as expected") assert.NotZero(t, len(s.NegTokenInit.MechToken), "MechToken is zero in length") } func TestUnmarshal_SPNEGO_RespTarg(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testGSSAPIResp) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } var s SPNEGO err = s.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshalling SPNEGO with NegTokenResp/NegTokenTarg: %v", err) } assert.True(t, s.Resp, "SPNEGO does not indicate it contains NegTokenResp/Targ as expected") assert.False(t, s.Init, "SPNEGO indicates is contains a NegTokenInit but it shouldn't") assert.Equal(t, asn1.Enumerated(0), s.NegTokenResp.NegState, "Negotiation state not as expected.") assert.Equal(t, MechTypeOIDKRB5, s.NegTokenResp.SupportedMech, "SupportedMech type not as expected.") } func TestMarshal_SPNEGO_Init(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testGSSAPIInit) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } var s SPNEGO err = s.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshalling SPNEGO with NegTokenInit: %v", err) } mb, err := s.Marshal() if err != nil { t.Fatalf("Error marshalling SPNEGO containing NegTokenInit: %v", err) } assert.Equal(t, b, mb, "Marshaled bytes not as expected") } func TestMarshal_SPNEGO_RespTarg(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testGSSAPIResp) if err != nil { t.Fatalf("Error converting hex string test data to bytes: %v", err) } var s SPNEGO err = s.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshalling SPNEGO with NegTokenResp: %v", err) } mb, err := s.Marshal() if err != nil { t.Fatalf("Error marshalling SPNEGO containing NegTokenResp: %v", err) } assert.Equal(t, b, mb, "Marshaled bytes not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/krb5Token.go000066400000000000000000000111211362537225700242320ustar00rootroot00000000000000package gssapi import ( "encoding/binary" "encoding/hex" "errors" "fmt" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // GSSAPI MechToken IDs and flags. const ( TOK_ID_KRB_AP_REQ = "0100" TOK_ID_KRB_AP_REP = "0200" TOK_ID_KRB_ERROR = "0300" GSS_C_DELEG_FLAG = 1 GSS_C_MUTUAL_FLAG = 2 GSS_C_REPLAY_FLAG = 4 GSS_C_SEQUENCE_FLAG = 8 GSS_C_CONF_FLAG = 16 GSS_C_INTEG_FLAG = 32 ) // MechToken implementation for GSSAPI. type MechToken struct { OID asn1.ObjectIdentifier TokID []byte APReq messages.APReq APRep messages.APRep KRBError messages.KRBError } // Marshal a MechToken into a slice of bytes. func (m *MechToken) Marshal() ([]byte, error) { // Create the header b, _ := asn1.Marshal(m.OID) b = append(b, m.TokID...) var tb []byte var err error switch hex.EncodeToString(m.TokID) { case TOK_ID_KRB_AP_REQ: tb, err = m.APReq.Marshal() if err != nil { return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err) } case TOK_ID_KRB_AP_REP: return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5") case TOK_ID_KRB_ERROR: return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5") } if err != nil { return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err) } b = append(b, tb...) return asn1tools.AddASNAppTag(b, 0), nil } // Unmarshal a MechToken. func (m *MechToken) Unmarshal(b []byte) error { var oid asn1.ObjectIdentifier r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0)) if err != nil { return fmt.Errorf("error unmarshalling MechToken OID: %v", err) } m.OID = oid m.TokID = r[0:2] switch hex.EncodeToString(m.TokID) { case TOK_ID_KRB_AP_REQ: var a messages.APReq err = a.Unmarshal(r[2:]) if err != nil { return fmt.Errorf("error unmarshalling MechToken AP_REQ: %v", err) } m.APReq = a case TOK_ID_KRB_AP_REP: var a messages.APRep err = a.Unmarshal(r[2:]) if err != nil { return fmt.Errorf("error unmarshalling MechToken AP_REP: %v", err) } m.APRep = a case TOK_ID_KRB_ERROR: var a messages.KRBError err = a.Unmarshal(r[2:]) if err != nil { return fmt.Errorf("error unmarshalling MechToken KRBError: %v", err) } m.KRBError = a } return nil } // IsAPReq tests if the MechToken contains an AP_REQ. func (m *MechToken) IsAPReq() bool { if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REQ { return true } return false } // IsAPRep tests if the MechToken contains an AP_REP. func (m *MechToken) IsAPRep() bool { if hex.EncodeToString(m.TokID) == TOK_ID_KRB_AP_REP { return true } return false } // IsKRBError tests if the MechToken contains an KRB_ERROR. func (m *MechToken) IsKRBError() bool { if hex.EncodeToString(m.TokID) == TOK_ID_KRB_ERROR { return true } return false } // NewAPREQMechToken creates new Kerberos AP_REQ MechToken. func NewAPREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (MechToken, error) { var m MechToken m.OID = MechTypeOIDKRB5 tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ) m.TokID = tb auth, err := NewAuthenticator(creds, GSSAPIFlags) if err != nil { return m, err } APReq, err := messages.NewAPReq( tkt, sessionKey, auth, ) if err != nil { return m, err } for _, o := range APOptions { types.SetFlag(&APReq.APOptions, o) } m.APReq = APReq return m, nil } // NewAuthenticator creates a new kerberos authenticator for kerberos MechToken func NewAuthenticator(creds credentials.Credentials, flags []int) (types.Authenticator, error) { //RFC 4121 Section 4.1.1 auth, err := types.NewAuthenticator(creds.Realm, creds.CName) if err != nil { return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") } auth.Cksum = types.Checksum{ CksumType: chksumtype.GSSAPI, Checksum: newAuthenticatorChksum(flags), } return auth, nil } // Create new authenticator checksum for kerberos MechToken func newAuthenticatorChksum(flags []int) []byte { a := make([]byte, 24) binary.LittleEndian.PutUint32(a[:4], 16) for _, i := range flags { if i == GSS_C_DELEG_FLAG { x := make([]byte, 28-len(a)) a = append(a, x...) } f := binary.LittleEndian.Uint32(a[20:24]) f |= uint32(i) binary.LittleEndian.PutUint32(a[20:24], f) } return a } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/gssapi/krb5Token_test.go000066400000000000000000000145601362537225700253030ustar00rootroot00000000000000package gssapi import ( "encoding/hex" "math" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( MechTokenHex = "6082026306092a864886f71201020201006e8202523082024ea003020105a10302010ea20703050000000000a382015d6182015930820155a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382011830820114a003020112a103020103a28201060482010230621d868c97f30bf401e03bbffcd724bd9d067dce2afc31f71a356449b070cdafcc1ff372d0eb1e7a708b50c0152f3996c45b1ea312a803907fb97192d39f20cdcaea29876190f51de6e2b4a4df0460122ed97f363434e1e120b0e76c172b4424a536987152ac0b73013ab88af4b13a3fcdc63f739039dd46d839709cf5b51bb0ce6cb3af05fab3844caac280929955495235e9d0424f8a1fb9b4bd4f6bba971f40b97e9da60b9dabfcf0b1feebfca02c9a19b327a0004aa8e19192726cf347561fa8ac74afad5d6a264e50cf495b93aac86c77b2bc2d184234f6c2767dbea431485a25687b9044a20b601e968efaefffa1fc5283ff32aa6a53cb6c5cdd2eddcb26a481d73081d4a003020112a103020103a281c70481c4a1b29e420324f7edf9efae39df7bcaaf196a3160cf07e72f52a4ef8a965721b2f3343719c50699046e4fcc18ca26c2bfc7e4a9eddfc9d9cfc57ff2f6bdbbd1fc40ac442195bc669b9a0dbba12563b3e4cac9f4022fc01b8aa2d1ab84815bb078399ff7f4d5f9815eef896a0c7e3c049e6fd9932b97096cdb5861425b9d81753d0743212ded1a0fb55a00bf71a46be5ce5e1c8a5cc327b914347d9efcb6cb31ca363b1850d95c7b6c4c3cc6301615ad907318a0c5379d343610fab17eca9c7dc0a5a60658" AuthChksum = "100000000000000000000000000000000000000030000000" ) func TestMechToken_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(MechTokenHex) if err != nil { t.Fatalf("Error decoding MechToken hex: %v", err) } var mt MechToken err = mt.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshalling MechToken: %v", err) } assert.Equal(t, MechTypeOIDKRB5, mt.OID, "MechToken OID not as expected.") assert.Equal(t, []byte{1, 0}, mt.TokID, "TokID not as expected") assert.Equal(t, msgtype.KRB_AP_REQ, mt.APReq.MsgType, "MechToken AP_REQ does not have the right message type.") assert.Equal(t, int32(0), mt.KRBError.ErrorCode, "KRBError in MechToken does not indicate no error.") assert.Equal(t, int32(18), mt.APReq.Authenticator.EType, "Authenticator within AP_REQ does not have the etype expected.") } func TestMechToken_newAuthenticatorChksum(t *testing.T) { t.Parallel() b, err := hex.DecodeString(AuthChksum) if err != nil { t.Fatalf("Error decoding MechToken hex: %v", err) } cb := newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}) assert.Equal(t, b, cb, "SPNEGO Authenticator checksum not as expected") } // Test with explicit subkey generation. func TestMechToken_newAuthenticatorWithSubkeyGeneration(t *testing.T) { t.Parallel() creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM) creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING var etypeID int32 = 18 keyLen := 32 // etypeID 18 refers to AES256 -> 32 bytes key a, err := NewAuthenticator(creds, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}) if err != nil { t.Fatalf("Error creating authenticator: %v", err) } a.GenerateSeqNumberAndSubKey(etypeID, keyLen) assert.Equal(t, int32(32771), a.Cksum.CksumType, "Checksum type in authenticator for SPNEGO mechtoken not as expected.") assert.Equal(t, etypeID, a.SubKey.KeyType, "Subkey not of the expected type.") assert.Equal(t, keyLen, len(a.SubKey.KeyValue), "Subkey value not of the right length") var nz bool for _, b := range a.SubKey.KeyValue { if b != byte(0) { nz = true } } assert.True(t, nz, "subkey not initialised") assert.Condition(t, assert.Comparison(func() bool { return a.SeqNumber > 0 }), "Sequence number is not greater than zero") assert.Condition(t, assert.Comparison(func() bool { return a.SeqNumber <= math.MaxUint32 })) } // Test without subkey generation. func TestMechToken_newAuthenticator(t *testing.T) { t.Parallel() creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM) creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING a, err := NewAuthenticator(creds, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}) if err != nil { t.Fatalf("Error creating authenticator: %v", err) } assert.Equal(t, int32(32771), a.Cksum.CksumType, "Checksum type in authenticator for SPNEGO mechtoken not as expected.") assert.Equal(t, int32(0), a.SubKey.KeyType, "Subkey not of the expected type.") assert.Nil(t, a.SubKey.KeyValue, "Subkey should not be set.") assert.Condition(t, assert.Comparison(func() bool { return a.SeqNumber > 0 }), "Sequence number is not greater than zero") assert.Condition(t, assert.Comparison(func() bool { return a.SeqNumber <= math.MaxUint32 })) } func TestNewAPREQMechToken_and_Marshal(t *testing.T) { t.Parallel() creds := credentials.NewCredentials("hftsai", testdata.TEST_REALM) creds.CName.NameString = testdata.TEST_PRINCIPALNAME_NAMESTRING var tkt messages.Ticket v := "encode_krb5_ticket" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = tkt.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } key := types.EncryptionKey{ KeyType: 18, KeyValue: make([]byte, 32), } mt, err := NewAPREQMechToken(creds, tkt, key, []int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}, []int{}) if err != nil { t.Fatalf("Error creating MechToken: %v", err) } mb, err := mt.Marshal() if err != nil { t.Fatalf("Error unmarshalling MechToken: %v", err) } err = mt.Unmarshal(mb) if err != nil { t.Fatalf("Error unmarshalling MechToken: %v", err) } assert.Equal(t, MechTypeOIDKRB5, mt.OID, "MechToken OID not as expected.") assert.Equal(t, []byte{1, 0}, mt.TokID, "TokID not as expected") assert.Equal(t, msgtype.KRB_AP_REQ, mt.APReq.MsgType, "MechToken AP_REQ does not have the right message type.") assert.Equal(t, int32(0), mt.KRBError.ErrorCode, "KRBError in MechToken does not indicate no error.") assert.Equal(t, testdata.TEST_REALM, mt.APReq.Ticket.Realm, "Realm in ticket within the AP_REQ of the MechToken not as expected.") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, mt.APReq.Ticket.SName.NameString, "SName in ticket within the AP_REQ of the MechToken not as expected.") assert.Equal(t, int32(18), mt.APReq.Authenticator.EType, "Authenticator within AP_REQ does not have the etype expected.") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/000077500000000000000000000000001362537225700214655ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/addrtype/000077500000000000000000000000001362537225700233015ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/addrtype/constants.go000066400000000000000000000005221362537225700256430ustar00rootroot00000000000000// Package addrtype provides Address type assigned numbers. package addrtype // Address type IDs. const ( IPv4 int32 = 2 Directional int32 = 3 ChaosNet int32 = 5 XNS int32 = 6 ISO int32 = 7 DECNETPhaseIV int32 = 12 AppleTalkDDP int32 = 16 NetBios int32 = 20 IPv6 int32 = 24 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/adtype/000077500000000000000000000000001362537225700227535ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/adtype/constants.go000066400000000000000000000014711362537225700253210ustar00rootroot00000000000000// Package adtype provides Authenticator type assigned numbers. package adtype // Authenticator type IDs. const ( ADIfRelevant int32 = 1 ADIntendedForServer int32 = 2 ADIntendedForApplicationClass int32 = 3 ADKDCIssued int32 = 4 ADAndOr int32 = 5 ADMandatoryTicketExtensions int32 = 6 ADInTicketExtensions int32 = 7 ADMandatoryForKDC int32 = 8 OSFDCE int32 = 64 SESAME int32 = 65 ADOSFDCEPKICertID int32 = 66 ADAuthenticationStrength int32 = 70 ADFXFastArmor int32 = 71 ADFXFastUsed int32 = 72 ADWin2KPAC int32 = 128 ADEtypeNegotiation int32 = 129 //Reserved values 9-63 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/asnAppTag/000077500000000000000000000000001362537225700233435ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/asnAppTag/constants.go000066400000000000000000000007611362537225700257120ustar00rootroot00000000000000// Package asnAppTag provides ASN1 application tag numbers. package asnAppTag // ASN1 application tag numbers. const ( Ticket = 1 Authenticator = 2 EncTicketPart = 3 ASREQ = 10 TGSREQ = 12 ASREP = 11 TGSREP = 13 APREQ = 14 APREP = 15 KRBSafe = 20 KRBPriv = 21 KRBCred = 22 EncASRepPart = 25 EncTGSRepPart = 26 EncAPRepPart = 27 EncKrbPrivPart = 28 EncKrbCredPart = 29 KRBError = 30 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/chksumtype/000077500000000000000000000000001362537225700236615ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/chksumtype/constants.go000066400000000000000000000017431362537225700262310ustar00rootroot00000000000000// Package chksumtype provides Kerberos 5 checksum type assigned numbers. package chksumtype // Checksum type IDs. const ( //RESERVED : 0 CRC32 int32 = 1 RSA_MD4 int32 = 2 RSA_MD4_DES int32 = 3 DES_MAC int32 = 4 DES_MAC_K int32 = 5 RSA_MD4_DES_K int32 = 6 RSA_MD5 int32 = 7 RSA_MD5_DES int32 = 8 RSA_MD5_DES3 int32 = 9 SHA1_ID10 int32 = 10 //UNASSIGNED : 11 HMAC_SHA1_DES3_KD int32 = 12 HMAC_SHA1_DES3 int32 = 13 SHA1_ID14 int32 = 14 HMAC_SHA1_96_AES128 int32 = 15 HMAC_SHA1_96_AES256 int32 = 16 CMAC_CAMELLIA128 int32 = 17 CMAC_CAMELLIA256 int32 = 18 HMAC_SHA256_128_AES128 int32 = 19 HMAC_SHA384_192_AES256 int32 = 20 //UNASSIGNED : 21-32770 GSSAPI int32 = 32771 //UNASSIGNED : 32772-2147483647 KERB_CHECKSUM_HMAC_MD5_UNSIGNED uint32 = 4294967158 // 0xFFFFFF76 documentation says this is -138 but in an unsigned int this is 4294967158 KERB_CHECKSUM_HMAC_MD5 int32 = -138 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/constants.go000066400000000000000000000001731362537225700240310ustar00rootroot00000000000000// Package iana provides Kerberos 5 assigned numbers. package iana // PVNO is the Protocol Version Number. const PVNO = 5 golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/errorcode/000077500000000000000000000000001362537225700234515ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/errorcode/constants.go000066400000000000000000000305641362537225700260240ustar00rootroot00000000000000// Package errorcode provides Kerberos 5 assigned error codes. package errorcode import "fmt" // Kerberos error codes. const ( KDC_ERR_NONE int32 = 0 //No error KDC_ERR_NAME_EXP int32 = 1 //Client's entry in database has expired KDC_ERR_SERVICE_EXP int32 = 2 //Server's entry in database has expired KDC_ERR_BAD_PVNO int32 = 3 //Requested protocol version number not supported KDC_ERR_C_OLD_MAST_KVNO int32 = 4 //Client's key encrypted in old master key KDC_ERR_S_OLD_MAST_KVNO int32 = 5 //Server's key encrypted in old master key KDC_ERR_C_PRINCIPAL_UNKNOWN int32 = 6 //Client not found in Kerberos database KDC_ERR_S_PRINCIPAL_UNKNOWN int32 = 7 //Server not found in Kerberos database KDC_ERR_PRINCIPAL_NOT_UNIQUE int32 = 8 //Multiple principal entries in database KDC_ERR_NULL_KEY int32 = 9 //The client or server has a null key KDC_ERR_CANNOT_POSTDATE int32 = 10 //Ticket not eligible for postdating KDC_ERR_NEVER_VALID int32 = 11 //Requested starttime is later than end time KDC_ERR_POLICY int32 = 12 //KDC policy rejects request KDC_ERR_BADOPTION int32 = 13 //KDC cannot accommodate requested option KDC_ERR_ETYPE_NOSUPP int32 = 14 //KDC has no support for encryption type KDC_ERR_SUMTYPE_NOSUPP int32 = 15 //KDC has no support for checksum type KDC_ERR_PADATA_TYPE_NOSUPP int32 = 16 //KDC has no support for padata type KDC_ERR_TRTYPE_NOSUPP int32 = 17 //KDC has no support for transited type KDC_ERR_CLIENT_REVOKED int32 = 18 //Clients credentials have been revoked KDC_ERR_SERVICE_REVOKED int32 = 19 //Credentials for server have been revoked KDC_ERR_TGT_REVOKED int32 = 20 //TGT has been revoked KDC_ERR_CLIENT_NOTYET int32 = 21 //Client not yet valid; try again later KDC_ERR_SERVICE_NOTYET int32 = 22 //Server not yet valid; try again later KDC_ERR_KEY_EXPIRED int32 = 23 //Password has expired; change password to reset KDC_ERR_PREAUTH_FAILED int32 = 24 //Pre-authentication information was invalid KDC_ERR_PREAUTH_REQUIRED int32 = 25 //Additional pre-authentication required KDC_ERR_SERVER_NOMATCH int32 = 26 //Requested server and ticket don't match KDC_ERR_MUST_USE_USER2USER int32 = 27 //Server principal valid for user2user only KDC_ERR_PATH_NOT_ACCEPTED int32 = 28 //KDC Policy rejects transited path KDC_ERR_SVC_UNAVAILABLE int32 = 29 //A service is not available KRB_AP_ERR_BAD_INTEGRITY int32 = 31 //Integrity check on decrypted field failed KRB_AP_ERR_TKT_EXPIRED int32 = 32 //Ticket expired KRB_AP_ERR_TKT_NYV int32 = 33 //Ticket not yet valid KRB_AP_ERR_REPEAT int32 = 34 //Request is a replay KRB_AP_ERR_NOT_US int32 = 35 //The ticket isn't for us KRB_AP_ERR_BADMATCH int32 = 36 //Ticket and authenticator don't match KRB_AP_ERR_SKEW int32 = 37 //Clock skew too great KRB_AP_ERR_BADADDR int32 = 38 //Incorrect net address KRB_AP_ERR_BADVERSION int32 = 39 //Protocol version mismatch KRB_AP_ERR_MSG_TYPE int32 = 40 //Invalid msg type KRB_AP_ERR_MODIFIED int32 = 41 //Message stream modified KRB_AP_ERR_BADORDER int32 = 42 //Message out of order KRB_AP_ERR_BADKEYVER int32 = 44 //Specified version of key is not available KRB_AP_ERR_NOKEY int32 = 45 //Service key not available KRB_AP_ERR_MUT_FAIL int32 = 46 //Mutual authentication failed KRB_AP_ERR_BADDIRECTION int32 = 47 //Incorrect message direction KRB_AP_ERR_METHOD int32 = 48 //Alternative authentication method required KRB_AP_ERR_BADSEQ int32 = 49 //Incorrect sequence number in message KRB_AP_ERR_INAPP_CKSUM int32 = 50 //Inappropriate type of checksum in message KRB_AP_PATH_NOT_ACCEPTED int32 = 51 //Policy rejects transited path KRB_ERR_RESPONSE_TOO_BIG int32 = 52 //Response too big for UDP; retry with TCP KRB_ERR_GENERIC int32 = 60 //Generic error (description in e-text) KRB_ERR_FIELD_TOOLONG int32 = 61 //Field is too long for this implementation KDC_ERROR_CLIENT_NOT_TRUSTED int32 = 62 //Reserved for PKINIT KDC_ERROR_KDC_NOT_TRUSTED int32 = 63 //Reserved for PKINIT KDC_ERROR_INVALID_SIG int32 = 64 //Reserved for PKINIT KDC_ERR_KEY_TOO_WEAK int32 = 65 //Reserved for PKINIT KDC_ERR_CERTIFICATE_MISMATCH int32 = 66 //Reserved for PKINIT KRB_AP_ERR_NO_TGT int32 = 67 //No TGT available to validate USER-TO-USER KDC_ERR_WRONG_REALM int32 = 68 //Reserved for future use KRB_AP_ERR_USER_TO_USER_REQUIRED int32 = 69 //Ticket must be for USER-TO-USER KDC_ERR_CANT_VERIFY_CERTIFICATE int32 = 70 //Reserved for PKINIT KDC_ERR_INVALID_CERTIFICATE int32 = 71 //Reserved for PKINIT KDC_ERR_REVOKED_CERTIFICATE int32 = 72 //Reserved for PKINIT KDC_ERR_REVOCATION_STATUS_UNKNOWN int32 = 73 //Reserved for PKINIT KDC_ERR_REVOCATION_STATUS_UNAVAILABLE int32 = 74 //Reserved for PKINIT KDC_ERR_CLIENT_NAME_MISMATCH int32 = 75 //Reserved for PKINIT KDC_ERR_KDC_NAME_MISMATCH int32 = 76 //Reserved for PKINIT ) // Lookup an error code description. func Lookup(i int32) string { if s, ok := errorcodeLookup[i]; ok { return fmt.Sprintf("(%d) %s", i, s) } return fmt.Sprintf("Unknown ErrorCode %d", i) } var errorcodeLookup = map[int32]string{ KDC_ERR_NONE: "KDC_ERR_NONE No error", KDC_ERR_NAME_EXP: "KDC_ERR_NAME_EXP Client's entry in database has expired", KDC_ERR_SERVICE_EXP: "KDC_ERR_SERVICE_EXP Server's entry in database has expired", KDC_ERR_BAD_PVNO: "KDC_ERR_BAD_PVNO Requested protocol version number not supported", KDC_ERR_C_OLD_MAST_KVNO: "KDC_ERR_C_OLD_MAST_KVNO Client's key encrypted in old master key", KDC_ERR_S_OLD_MAST_KVNO: "KDC_ERR_S_OLD_MAST_KVNO Server's key encrypted in old master key", KDC_ERR_C_PRINCIPAL_UNKNOWN: "KDC_ERR_C_PRINCIPAL_UNKNOWN Client not found in Kerberos database", KDC_ERR_S_PRINCIPAL_UNKNOWN: "KDC_ERR_S_PRINCIPAL_UNKNOWN Server not found in Kerberos database", KDC_ERR_PRINCIPAL_NOT_UNIQUE: "KDC_ERR_PRINCIPAL_NOT_UNIQUE Multiple principal entries in database", KDC_ERR_NULL_KEY: "KDC_ERR_NULL_KEY The client or server has a null key", KDC_ERR_CANNOT_POSTDATE: "KDC_ERR_CANNOT_POSTDATE Ticket not eligible for postdating", KDC_ERR_NEVER_VALID: "KDC_ERR_NEVER_VALID Requested starttime is later than end time", KDC_ERR_POLICY: "KDC_ERR_POLICY KDC policy rejects request", KDC_ERR_BADOPTION: "KDC_ERR_BADOPTION KDC cannot accommodate requested option", KDC_ERR_ETYPE_NOSUPP: "KDC_ERR_ETYPE_NOSUPP KDC has no support for encryption type", KDC_ERR_SUMTYPE_NOSUPP: "KDC_ERR_SUMTYPE_NOSUPP KDC has no support for checksum type", KDC_ERR_PADATA_TYPE_NOSUPP: "KDC_ERR_PADATA_TYPE_NOSUPP KDC has no support for padata type", KDC_ERR_TRTYPE_NOSUPP: "KDC_ERR_TRTYPE_NOSUPP KDC has no support for transited type", KDC_ERR_CLIENT_REVOKED: "KDC_ERR_CLIENT_REVOKED Clients credentials have been revoked", KDC_ERR_SERVICE_REVOKED: "KDC_ERR_SERVICE_REVOKED Credentials for server have been revoked", KDC_ERR_TGT_REVOKED: "KDC_ERR_TGT_REVOKED TGT has been revoked", KDC_ERR_CLIENT_NOTYET: "KDC_ERR_CLIENT_NOTYET Client not yet valid; try again later", KDC_ERR_SERVICE_NOTYET: "KDC_ERR_SERVICE_NOTYET Server not yet valid; try again later", KDC_ERR_KEY_EXPIRED: "KDC_ERR_KEY_EXPIRED Password has expired; change password to reset", KDC_ERR_PREAUTH_FAILED: "KDC_ERR_PREAUTH_FAILED Pre-authentication information was invalid", KDC_ERR_PREAUTH_REQUIRED: "KDC_ERR_PREAUTH_REQUIRED Additional pre-authentication required", KDC_ERR_SERVER_NOMATCH: "KDC_ERR_SERVER_NOMATCH Requested server and ticket don't match", KDC_ERR_MUST_USE_USER2USER: "KDC_ERR_MUST_USE_USER2USER Server principal valid for user2user only", KDC_ERR_PATH_NOT_ACCEPTED: "KDC_ERR_PATH_NOT_ACCEPTED KDC Policy rejects transited path", KDC_ERR_SVC_UNAVAILABLE: "KDC_ERR_SVC_UNAVAILABLE A service is not available", KRB_AP_ERR_BAD_INTEGRITY: "KRB_AP_ERR_BAD_INTEGRITY Integrity check on decrypted field failed", KRB_AP_ERR_TKT_EXPIRED: "KRB_AP_ERR_TKT_EXPIRED Ticket expired", KRB_AP_ERR_TKT_NYV: "KRB_AP_ERR_TKT_NYV Ticket not yet valid", KRB_AP_ERR_REPEAT: "KRB_AP_ERR_REPEAT Request is a replay", KRB_AP_ERR_NOT_US: "KRB_AP_ERR_NOT_US The ticket isn't for us", KRB_AP_ERR_BADMATCH: "KRB_AP_ERR_BADMATCH Ticket and authenticator don't match", KRB_AP_ERR_SKEW: "KRB_AP_ERR_SKEW Clock skew too great", KRB_AP_ERR_BADADDR: "KRB_AP_ERR_BADADDR Incorrect net address", KRB_AP_ERR_BADVERSION: "KRB_AP_ERR_BADVERSION Protocol version mismatch", KRB_AP_ERR_MSG_TYPE: "KRB_AP_ERR_MSG_TYPE Invalid msg type", KRB_AP_ERR_MODIFIED: "KRB_AP_ERR_MODIFIED Message stream modified", KRB_AP_ERR_BADORDER: "KRB_AP_ERR_BADORDER Message out of order", KRB_AP_ERR_BADKEYVER: "KRB_AP_ERR_BADKEYVER Specified version of key is not available", KRB_AP_ERR_NOKEY: "KRB_AP_ERR_NOKEY Service key not available", KRB_AP_ERR_MUT_FAIL: "KRB_AP_ERR_MUT_FAIL Mutual authentication failed", KRB_AP_ERR_BADDIRECTION: "KRB_AP_ERR_BADDIRECTION Incorrect message direction", KRB_AP_ERR_METHOD: "KRB_AP_ERR_METHOD Alternative authentication method required", KRB_AP_ERR_BADSEQ: "KRB_AP_ERR_BADSEQ Incorrect sequence number in message", KRB_AP_ERR_INAPP_CKSUM: "KRB_AP_ERR_INAPP_CKSUM Inappropriate type of checksum in message", KRB_AP_PATH_NOT_ACCEPTED: "KRB_AP_PATH_NOT_ACCEPTED Policy rejects transited path", KRB_ERR_RESPONSE_TOO_BIG: "KRB_ERR_RESPONSE_TOO_BIG Response too big for UDP; retry with TCP", KRB_ERR_GENERIC: "KRB_ERR_GENERIC Generic error (description in e-text)", KRB_ERR_FIELD_TOOLONG: "KRB_ERR_FIELD_TOOLONG Field is too long for this implementation", KDC_ERROR_CLIENT_NOT_TRUSTED: "KDC_ERROR_CLIENT_NOT_TRUSTED Reserved for PKINIT", KDC_ERROR_KDC_NOT_TRUSTED: "KDC_ERROR_KDC_NOT_TRUSTED Reserved for PKINIT", KDC_ERROR_INVALID_SIG: "KDC_ERROR_INVALID_SIG Reserved for PKINIT", KDC_ERR_KEY_TOO_WEAK: "KDC_ERR_KEY_TOO_WEAK Reserved for PKINIT", KDC_ERR_CERTIFICATE_MISMATCH: "KDC_ERR_CERTIFICATE_MISMATCH Reserved for PKINIT", KRB_AP_ERR_NO_TGT: "KRB_AP_ERR_NO_TGT No TGT available to validate USER-TO-USER", KDC_ERR_WRONG_REALM: "KDC_ERR_WRONG_REALM Reserved for future use", KRB_AP_ERR_USER_TO_USER_REQUIRED: "KRB_AP_ERR_USER_TO_USER_REQUIRED Ticket must be for USER-TO-USER", KDC_ERR_CANT_VERIFY_CERTIFICATE: "KDC_ERR_CANT_VERIFY_CERTIFICATE Reserved for PKINIT", KDC_ERR_INVALID_CERTIFICATE: "KDC_ERR_INVALID_CERTIFICATE Reserved for PKINIT", KDC_ERR_REVOKED_CERTIFICATE: "KDC_ERR_REVOKED_CERTIFICATE Reserved for PKINIT", KDC_ERR_REVOCATION_STATUS_UNKNOWN: "KDC_ERR_REVOCATION_STATUS_UNKNOWN Reserved for PKINIT", KDC_ERR_REVOCATION_STATUS_UNAVAILABLE: "KDC_ERR_REVOCATION_STATUS_UNAVAILABLE Reserved for PKINIT", KDC_ERR_CLIENT_NAME_MISMATCH: "KDC_ERR_CLIENT_NAME_MISMATCH Reserved for PKINIT", KDC_ERR_KDC_NAME_MISMATCH: "KDC_ERR_KDC_NAME_MISMATCH Reserved for PKINIT", } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/etypeID/000077500000000000000000000000001362537225700230305ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/etypeID/constants.go000066400000000000000000000073561362537225700254060ustar00rootroot00000000000000// Package etypeID provides Kerberos 5 encryption type assigned numbers. package etypeID // Kerberos encryption type assigned numbers. const ( //RESERVED : 0 DES_CBC_CRC int32 = 1 DES_CBC_MD4 int32 = 2 DES_CBC_MD5 int32 = 3 DES_CBC_RAW int32 = 4 DES3_CBC_MD5 int32 = 5 DES3_CBC_RAW int32 = 6 DES3_CBC_SHA1 int32 = 7 DES_HMAC_SHA1 int32 = 8 DSAWITHSHA1_CMSOID int32 = 9 MD5WITHRSAENCRYPTION_CMSOID int32 = 10 SHA1WITHRSAENCRYPTION_CMSOID int32 = 11 RC2CBC_ENVOID int32 = 12 RSAENCRYPTION_ENVOID int32 = 13 RSAES_OAEP_ENV_OID int32 = 14 DES_EDE3_CBC_ENV_OID int32 = 15 DES3_CBC_SHA1_KD int32 = 16 AES128_CTS_HMAC_SHA1_96 int32 = 17 AES256_CTS_HMAC_SHA1_96 int32 = 18 AES128_CTS_HMAC_SHA256_128 int32 = 19 AES256_CTS_HMAC_SHA384_192 int32 = 20 //UNASSIGNED : 21-22 RC4_HMAC int32 = 23 RC4_HMAC_EXP int32 = 24 CAMELLIA128_CTS_CMAC int32 = 25 CAMELLIA256_CTS_CMAC int32 = 26 //UNASSIGNED : 27-64 SUBKEY_KEYMATERIAL int32 = 65 //UNASSIGNED : 66-2147483647 ) // ETypesByName is a map of EncType names to their assigned EncType number. var ETypesByName = map[string]int32{ "des-cbc-crc": DES_CBC_CRC, "des-cbc-md4": DES_CBC_MD4, "des-cbc-md5": DES_CBC_MD5, "des-cbc-raw": DES_CBC_RAW, "des3-cbc-md5": DES3_CBC_MD5, "des3-cbc-raw": DES3_CBC_RAW, "des3-cbc-sha1": DES3_CBC_SHA1, "des3-hmac-sha1": DES_HMAC_SHA1, "des3-cbc-sha1-kd": DES3_CBC_SHA1_KD, "des-hmac-sha1": DES_HMAC_SHA1, "dsaWithSHA1-CmsOID": DSAWITHSHA1_CMSOID, "md5WithRSAEncryption-CmsOID": MD5WITHRSAENCRYPTION_CMSOID, "sha1WithRSAEncryption-CmsOID": SHA1WITHRSAENCRYPTION_CMSOID, "rc2CBC-EnvOID": RC2CBC_ENVOID, "rsaEncryption-EnvOID": RSAENCRYPTION_ENVOID, "rsaES-OAEP-ENV-OID": RSAES_OAEP_ENV_OID, "des-ede3-cbc-Env-OID": DES_EDE3_CBC_ENV_OID, "aes128-cts-hmac-sha1-96": AES128_CTS_HMAC_SHA1_96, "aes128-cts": AES128_CTS_HMAC_SHA1_96, "aes128-sha1": AES128_CTS_HMAC_SHA1_96, "aes256-cts-hmac-sha1-96": AES256_CTS_HMAC_SHA1_96, "aes256-cts": AES128_CTS_HMAC_SHA1_96, "aes256-sha1": AES128_CTS_HMAC_SHA1_96, "aes128-cts-hmac-sha256-128": AES128_CTS_HMAC_SHA256_128, "aes128-sha2": AES128_CTS_HMAC_SHA256_128, "aes256-cts-hmac-sha384-192": AES256_CTS_HMAC_SHA384_192, "aes256-sha2": AES256_CTS_HMAC_SHA384_192, "arcfour-hmac": RC4_HMAC, "rc4-hmac": RC4_HMAC, "arcfour-hmac-md5": RC4_HMAC, "arcfour-hmac-exp": RC4_HMAC_EXP, "rc4-hmac-exp": RC4_HMAC_EXP, "arcfour-hmac-md5-exp": RC4_HMAC_EXP, "camellia128-cts-cmac": CAMELLIA128_CTS_CMAC, "camellia128-cts": CAMELLIA128_CTS_CMAC, "camellia256-cts-cmac": CAMELLIA256_CTS_CMAC, "camellia256-cts": CAMELLIA256_CTS_CMAC, "subkey-keymaterial": SUBKEY_KEYMATERIAL, } // EtypeSupported resolves the etype name string to the etype ID. // If zero is returned the etype is not supported by gokrb5. func EtypeSupported(etype string) int32 { // Slice of supported enctype IDs s := []int32{ AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96, AES128_CTS_HMAC_SHA256_128, AES256_CTS_HMAC_SHA384_192, DES3_CBC_SHA1_KD, RC4_HMAC, } id := ETypesByName[etype] if id == 0 { return id } for _, sid := range s { if id == sid { return id } } return 0 } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/flags/000077500000000000000000000000001362537225700225615ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/flags/constants.go000066400000000000000000000014601362537225700251250ustar00rootroot00000000000000// Package flags provides Kerberos 5 flag assigned numbers. package flags // Flag values for KRB5 messages and tickets. const ( Reserved = 0 Forwardable = 1 Forwarded = 2 Proxiable = 3 Proxy = 4 AllowPostDate = 5 MayPostDate = 5 PostDated = 6 Invalid = 7 Renewable = 8 Initial = 9 PreAuthent = 10 HWAuthent = 11 OptHardwareAuth = 11 RequestAnonymous = 12 TransitedPolicyChecked = 12 OKAsDelegate = 13 EncPARep = 15 Canonicalize = 15 DisableTransitedCheck = 26 RenewableOK = 27 EncTktInSkey = 28 Renew = 30 Validate = 31 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/keyusage/000077500000000000000000000000001362537225700233025ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/keyusage/constants.go000066400000000000000000000036351362537225700256540ustar00rootroot00000000000000// Package keyusage provides Kerberos 5 key usage assigned numbers. package keyusage // Key usage numbers. const ( AS_REQ_PA_ENC_TIMESTAMP = 1 KDC_REP_TICKET = 2 AS_REP_ENCPART = 3 TGS_REQ_KDC_REQ_BODY_AUTHDATA_SESSION_KEY = 4 TGS_REQ_KDC_REQ_BODY_AUTHDATA_SUB_KEY = 5 TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM = 6 TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR = 7 TGS_REP_ENCPART_SESSION_KEY = 8 TGS_REP_ENCPART_AUTHENTICATOR_SUB_KEY = 9 AP_REQ_AUTHENTICATOR_CHKSUM = 10 AP_REQ_AUTHENTICATOR = 11 AP_REP_ENCPART = 12 KRB_PRIV_ENCPART = 13 KRB_CRED_ENCPART = 14 KRB_SAFE_CHKSUM = 15 KERB_NON_KERB_SALT = 16 KERB_NON_KERB_CKSUM_SALT = 17 //18. Reserved for future use in Kerberos and related protocols. AD_KDC_ISSUED_CHKSUM = 19 //20-21. Reserved for future use in Kerberos and related protocols. GSSAPI_ACCEPTOR_SEAL = 22 GSSAPI_ACCEPTOR_SIGN = 23 GSSAPI_INITIATOR_SEAL = 24 GSSAPI_INITIATOR_SIGN = 25 KEY_USAGE_FAST_REQ_CHKSUM = 50 KEY_USAGE_FAST_ENC = 51 KEY_USAGE_FAST_REP = 52 KEY_USAGE_FAST_FINISHED = 53 KEY_USAGE_ENC_CHALLENGE_CLIENT = 54 KEY_USAGE_ENC_CHALLENGE_KDC = 55 KEY_USAGE_AS_REQ = 56 //26-511. Reserved for future use in Kerberos and related protocols. //512-1023. Reserved for uses internal to a Kerberos implementation. //1024. Encryption for application use in protocols that do not specify key usage values //1025. Checksums for application use in protocols that do not specify key usage values //1026-2047. Reserved for application use. ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/msgtype/000077500000000000000000000000001362537225700231555ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/msgtype/constants.go000066400000000000000000000014711362537225700255230ustar00rootroot00000000000000// Package msgtype provides Kerberos 5 message type assigned numbers. package msgtype // KRB message type IDs. const ( KRB_AS_REQ = 10 //Request for initial authentication KRB_AS_REP = 11 //Response to KRB_AS_REQ request KRB_TGS_REQ = 12 //Request for authentication based on TGT KRB_TGS_REP = 13 //Response to KRB_TGS_REQ request KRB_AP_REQ = 14 //Application request to server KRB_AP_REP = 15 //Response to KRB_AP_REQ_MUTUAL KRB_RESERVED16 = 16 //Reserved for user-to-user krb_tgt_request KRB_RESERVED17 = 17 //Reserved for user-to-user krb_tgt_reply KRB_SAFE = 20 // Safe (checksummed) application message KRB_PRIV = 21 // Private (encrypted) application message KRB_CRED = 22 //Private (encrypted) message to forward credentials KRB_ERROR = 30 //Error response ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/nametype/000077500000000000000000000000001362537225700233075ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/nametype/constants.go000066400000000000000000000014671362537225700256620ustar00rootroot00000000000000// Package nametype provides Kerberos 5 principal name type numbers. package nametype // Kerberos name type IDs. const ( KRB_NT_UNKNOWN int32 = 0 //Name type not known KRB_NT_PRINCIPAL int32 = 1 //Just the name of the principal as in DCE, or for users KRB_NT_SRV_INST int32 = 2 //Service and other unique instance (krbtgt) KRB_NT_SRV_HST int32 = 3 //Service with host name as instance (telnet, rcommands) KRB_NT_SRV_XHST int32 = 4 //Service with host as remaining components KRB_NT_UID int32 = 5 //Unique ID KRB_NT_X500_PRINCIPAL int32 = 6 //Encoded X.509 Distinguished name [RFC2253] KRB_NT_SMTP_NAME int32 = 7 //Name in form of SMTP email name (e.g., user@example.com) KRB_NT_ENTERPRISE int32 = 10 //Enterprise name; may be mapped to principal name ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/patype/000077500000000000000000000000001362537225700227675ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/patype/constants.go000066400000000000000000000047221362537225700253370ustar00rootroot00000000000000// Package patype provides Kerberos 5 pre-authentication type assigned numbers. package patype // Kerberos pre-authentication type assigned numbers. const ( PA_TGS_REQ int32 = 1 PA_ENC_TIMESTAMP int32 = 2 PA_PW_SALT int32 = 3 //RESERVED : 4 PA_ENC_UNIX_TIME int32 = 5 PA_SANDIA_SECUREID int32 = 6 PA_SESAME int32 = 7 PA_OSF_DCE int32 = 8 PA_CYBERSAFE_SECUREID int32 = 9 PA_AFS3_SALT int32 = 10 PA_ETYPE_INFO int32 = 11 PA_SAM_CHALLENGE int32 = 12 PA_SAM_RESPONSE int32 = 13 PA_PK_AS_REQ_OLD int32 = 14 PA_PK_AS_REP_OLD int32 = 15 PA_PK_AS_REQ int32 = 16 PA_PK_AS_REP int32 = 17 PA_PK_OCSP_RESPONSE int32 = 18 PA_ETYPE_INFO2 int32 = 19 PA_USE_SPECIFIED_KVNO int32 = 20 PA_SVR_REFERRAL_INFO int32 = 20 PA_SAM_REDIRECT int32 = 21 PA_GET_FROM_TYPED_DATA int32 = 22 TD_PADATA int32 = 22 PA_SAM_ETYPE_INFO int32 = 23 PA_ALT_PRINC int32 = 24 PA_SERVER_REFERRAL int32 = 25 //UNASSIGNED : 26-29 PA_SAM_CHALLENGE2 int32 = 30 PA_SAM_RESPONSE2 int32 = 31 //UNASSIGNED : 32-40 PA_EXTRA_TGT int32 = 41 //UNASSIGNED : 42-100 TD_PKINIT_CMS_CERTIFICATES int32 = 101 TD_KRB_PRINCIPAL int32 = 102 TD_KRB_REALM int32 = 103 TD_TRUSTED_CERTIFIERS int32 = 104 TD_CERTIFICATE_INDEX int32 = 105 TD_APP_DEFINED_ERROR int32 = 106 TD_REQ_NONCE int32 = 107 TD_REQ_SEQ int32 = 108 TD_DH_PARAMETERS int32 = 109 //UNASSIGNED : 110 TD_CMS_DIGEST_ALGORITHMS int32 = 111 TD_CERT_DIGEST_ALGORITHMS int32 = 112 //UNASSIGNED : 113-127 PA_PAC_REQUEST int32 = 128 PA_FOR_USER int32 = 129 PA_FOR_X509_USER int32 = 130 PA_FOR_CHECK_DUPS int32 = 131 PA_AS_CHECKSUM int32 = 132 PA_FX_COOKIE int32 = 133 PA_AUTHENTICATION_SET int32 = 134 PA_AUTH_SET_SELECTED int32 = 135 PA_FX_FAST int32 = 136 PA_FX_ERROR int32 = 137 PA_ENCRYPTED_CHALLENGE int32 = 138 //UNASSIGNED : 139-140 PA_OTP_CHALLENGE int32 = 141 PA_OTP_REQUEST int32 = 142 PA_OTP_CONFIRM int32 = 143 PA_OTP_PIN_CHANGE int32 = 144 PA_EPAK_AS_REQ int32 = 145 PA_EPAK_AS_REP int32 = 146 PA_PKINIT_KX int32 = 147 PA_PKU2U_NAME int32 = 148 PA_REQ_ENC_PA_REP int32 = 149 PA_AS_FRESHNESS int32 = 150 //UNASSIGNED : 151-164 PA_SUPPORTED_ETYPES int32 = 165 PA_EXTENDED_ERROR int32 = 166 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/trtype/000077500000000000000000000000001362537225700230145ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/iana/trtype/constants.go000066400000000000000000000003151362537225700253560ustar00rootroot00000000000000// Package trtype provides Transited Encoding Type assigned numbers. package trtype // Transited Encoding Type IDs. const ( DOMAIN_X500_COMPRESS int32 = 1 //Reserved values All others ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/000077500000000000000000000000001362537225700220205ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/changepasswddata.go000066400000000000000000000012021362537225700256430ustar00rootroot00000000000000package kadmin import ( "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/types" ) // ChangePasswdData is the payload to a password change message. type ChangePasswdData struct { NewPasswd []byte `asn1:"explicit,tag:0"` TargName types.PrincipalName `asn1:"explicit,optional,tag:1"` TargRealm string `asn1:"generalstring,optional,explicit,tag:2"` } // Marshal ChangePasswdData into a byte slice. func (c *ChangePasswdData) Marshal() ([]byte, error) { b, err := asn1.Marshal(*c) if err != nil { return []byte{}, err } //b = asn1tools.AddASNAppTag(b, asnAppTag.) return b, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/changepasswddata_test.go000066400000000000000000000014621362537225700267120ustar00rootroot00000000000000package kadmin import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestChangePasswdData_Marshal(t *testing.T) { t.Parallel() chgpasswd := ChangePasswdData{ NewPasswd: []byte("newpassword"), TargName: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, "testuser1"), TargRealm: "TEST.GOKRB5", } chpwdb, err := chgpasswd.Marshal() if err != nil { t.Fatalf("error marshaling change passwd data: %v\n", err) } v := "ChangePasswdData" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } assert.Equal(t, b, chpwdb, "marshaled bytes of change passwd data not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/message.go000066400000000000000000000053031362537225700237740ustar00rootroot00000000000000package kadmin import ( "bytes" "encoding/binary" "errors" "fmt" "math" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( verisonHex = "ff80" ) // Request message for changing password. type Request struct { APREQ messages.APReq KRBPriv messages.KRBPriv } // Reply message for a password change. type Reply struct { MessageLength int Version int APREPLength int APREP messages.APRep KRBPriv messages.KRBPriv KRBError messages.KRBError IsKRBError bool ResultCode uint16 Result string } // Marshal a Request into a byte slice. func (m *Request) Marshal() (b []byte, err error) { b = []byte{255, 128} // protocol version number: contains the hex constant 0xff80 (big-endian integer). ab, e := m.APREQ.Marshal() if e != nil { err = fmt.Errorf("error marshaling AP_REQ: %v", e) return } if len(ab) > math.MaxUint16 { err = errors.New("length of AP_REQ greater then max Uint16 size") return } al := make([]byte, 2) binary.BigEndian.PutUint16(al, uint16(len(ab))) b = append(b, al...) b = append(b, ab...) pb, e := m.KRBPriv.Marshal() if e != nil { err = fmt.Errorf("error marshaling KRB_Priv: %v", e) return } b = append(b, pb...) if len(b)+2 > math.MaxUint16 { err = errors.New("length of message greater then max Uint16 size") return } ml := make([]byte, 2) binary.BigEndian.PutUint16(ml, uint16(len(b)+2)) b = append(ml, b...) return } // Unmarshal a byte slice into a Reply. func (m *Reply) Unmarshal(b []byte) error { m.MessageLength = int(binary.BigEndian.Uint16(b[0:2])) m.Version = int(binary.BigEndian.Uint16(b[2:4])) if m.Version != 1 { return fmt.Errorf("kadmin reply has incorrect protocol version number: %d", m.Version) } m.APREPLength = int(binary.BigEndian.Uint16(b[4:6])) if m.APREPLength != 0 { err := m.APREP.Unmarshal(b[6 : 6+m.APREPLength]) if err != nil { return err } err = m.KRBPriv.Unmarshal(b[6+m.APREPLength : m.MessageLength]) if err != nil { return err } } else { m.IsKRBError = true m.KRBError.Unmarshal(b[6:m.MessageLength]) m.ResultCode, m.Result = parseResponse(m.KRBError.EData) } return nil } func parseResponse(b []byte) (c uint16, s string) { c = binary.BigEndian.Uint16(b[0:2]) buf := bytes.NewBuffer(b[2:]) m := make([]byte, len(b)-2) binary.Read(buf, binary.BigEndian, &m) s = string(m) return } // Decrypt the encrypted part of the KRBError within the change password Reply. func (m *Reply) Decrypt(key types.EncryptionKey) error { if m.IsKRBError { return m.KRBError } err := m.KRBPriv.DecryptEncPart(key) if err != nil { return err } m.ResultCode, m.Result = parseResponse(m.KRBPriv.DecryptedEncPart.UserData) return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/message_test.go000066400000000000000000000025551362537225700250410ustar00rootroot00000000000000package kadmin import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalReply(t *testing.T) { t.Parallel() var a Reply v := "Kpasswd_Rep" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, 236, a.MessageLength, "message length not as expected") assert.Equal(t, 1, a.Version, "message version not as expected") assert.Equal(t, 140, a.APREPLength, "AP_REP length not as expected") assert.Equal(t, iana.PVNO, a.APREP.PVNO, "AP_REP within reply not as expected") assert.Equal(t, msgtype.KRB_AP_REP, a.APREP.MsgType, "AP_REP message type within reply not as expected") assert.Equal(t, int32(18), a.APREP.EncPart.EType, "AP_REQ etype not as expected") assert.Equal(t, iana.PVNO, a.KRBPriv.PVNO, "KRBPriv within reply not as expected") assert.Equal(t, msgtype.KRB_PRIV, a.KRBPriv.MsgType, "KRBPriv type within reply not as expected") assert.Equal(t, int32(18), a.KRBPriv.EncPart.EType, "KRBPriv etype not as expected") } // Request marshal is tested via integration test in the client package due to the dynamic keys and encryption. golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/kadmin/passwd.go000066400000000000000000000036611362537225700236560ustar00rootroot00000000000000// Package kadmin provides Kerberos administration capabilities. package kadmin import ( "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // ChangePasswdMsg generate a change password request and also return the key needed to decrypt the reply. func ChangePasswdMsg(cname types.PrincipalName, realm, password string, tkt messages.Ticket, sessionKey types.EncryptionKey) (r Request, k types.EncryptionKey, err error) { // Create change password data struct and marshal to bytes chgpasswd := ChangePasswdData{ NewPasswd: []byte(password), TargName: cname, TargRealm: realm, } chpwdb, err := chgpasswd.Marshal() if err != nil { err = krberror.Errorf(err, krberror.KRBMsgError, "error marshaling change passwd data") return } // Generate authenticator auth, err := types.NewAuthenticator(realm, cname) if err != nil { err = krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") return } etype, err := crypto.GetEtype(sessionKey.KeyType) if err != nil { err = krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey etype") return } err = auth.GenerateSeqNumberAndSubKey(etype.GetETypeID(), etype.GetKeyByteSize()) if err != nil { err = krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey") return } k = auth.SubKey // Generate AP_REQ APreq, err := messages.NewAPReq(tkt, sessionKey, auth) if err != nil { return } // Form the KRBPriv encpart data kp := messages.EncKrbPrivPart{ UserData: chpwdb, Timestamp: auth.CTime, Usec: auth.Cusec, SequenceNumber: auth.SeqNumber, } kpriv := messages.NewKRBPriv(kp) err = kpriv.EncryptEncPart(k) if err != nil { err = krberror.Errorf(err, krberror.EncryptingError, "error encrypting change passwd data") return } r = Request{ APREQ: APreq, KRBPriv: kpriv, } return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/000077500000000000000000000000001362537225700220345ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/keytab.go000066400000000000000000000210611362537225700236420ustar00rootroot00000000000000// Package keytab implements Kerberos keytabs: https://web.mit.edu/kerberos/krb5-devel/doc/formats/keytab_file_format.html. package keytab import ( "bytes" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "time" "unsafe" "gopkg.in/jcmturner/gokrb5.v5/types" ) const ( keytabFirstByte byte = 05 ) // Keytab struct. type Keytab struct { Version uint8 Entries []entry } // Keytab entry struct. type entry struct { Principal principal Timestamp time.Time KVNO8 uint8 Key types.EncryptionKey KVNO uint32 } // Keytab entry principal struct. type principal struct { NumComponents int16 Realm string Components []string NameType int32 } // NewKeytab creates new, empty Keytab type. func NewKeytab() Keytab { var e []entry return Keytab{ Version: 0, Entries: e, } } // GetEncryptionKey returns the EncryptionKey from the Keytab for the newest entry with the required kvno, etype and matching principal. func (kt *Keytab) GetEncryptionKey(nameString []string, realm string, kvno int, etype int32) (types.EncryptionKey, error) { var key types.EncryptionKey var t time.Time for _, k := range kt.Entries { if k.Principal.Realm == realm && len(k.Principal.Components) == len(nameString) && k.Key.KeyType == etype && (k.KVNO == uint32(kvno) || kvno == 0) && k.Timestamp.After(t) { p := true for i, n := range k.Principal.Components { if nameString[i] != n { p = false break } } if p { key = k.Key t = k.Timestamp } } } if len(key.KeyValue) < 1 { return key, fmt.Errorf("matching key not found in keytab. Looking for %v realm: %v kvno: %v etype: %v", nameString, realm, kvno, etype) } return key, nil } // Create a new Keytab entry. func newKeytabEntry() entry { var b []byte return entry{ Principal: newPrincipal(), Timestamp: time.Time{}, KVNO8: 0, Key: types.EncryptionKey{ KeyType: 0, KeyValue: b, }, KVNO: 0, } } // Create a new principal. func newPrincipal() principal { var c []string return principal{ NumComponents: 0, Realm: "", Components: c, NameType: 0, } } // Load a Keytab file into a Keytab type. func Load(ktPath string) (kt Keytab, err error) { k, err := ioutil.ReadFile(ktPath) if err != nil { return } return Parse(k) } // Marshal keytab into byte slice func (kt Keytab) Marshal() ([]byte, error) { b := []byte{keytabFirstByte, kt.Version} for _, e := range kt.Entries { eb, err := e.marshal(int(kt.Version)) if err != nil { return b, err } b = append(b, eb...) } return b, nil } // Write the keytab bytes to io.Writer. // Returns the number of bytes written func (kt Keytab) Write(w io.Writer) (int, error) { b, err := kt.Marshal() if err != nil { return 0, fmt.Errorf("error marshaling keytab: %v", err) } return w.Write(b) } // Parse byte slice of Keytab data into Keytab type. func Parse(b []byte) (kt Keytab, err error) { //The first byte of the file always has the value 5 if b[0] != keytabFirstByte { err = errors.New("invalid keytab data. First byte does not equal 5") return } //Get keytab version //The 2nd byte contains the version number (1 or 2) kt.Version = uint8(b[1]) if kt.Version != 1 && kt.Version != 2 { err = errors.New("invalid keytab data. Keytab version is neither 1 nor 2") return } //Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order var endian binary.ByteOrder endian = binary.BigEndian if kt.Version == 1 && isNativeEndianLittle() { endian = binary.LittleEndian } // n tracks position in the byte array n := 2 l := readInt32(b, &n, &endian) for l != 0 { if l < 0 { //Zero padded so skip over l = l * -1 n = n + int(l) } else { //fmt.Printf("Bytes for entry: %v\n", b[n:n+int(l)]) eb := b[n : n+int(l)] n = n + int(l) ke := newKeytabEntry() // p keeps track as to where we are in the byte stream var p int parsePrincipal(eb, &p, &kt, &ke, &endian) ke.Timestamp = readTimestamp(eb, &p, &endian) ke.KVNO8 = uint8(readInt8(eb, &p, &endian)) ke.Key.KeyType = int32(readInt16(eb, &p, &endian)) kl := int(readInt16(eb, &p, &endian)) ke.Key.KeyValue = readBytes(eb, &p, kl, &endian) if len(eb)-p >= 4 { // The 32-bit key may be present ke.KVNO = uint32(readInt32(eb, &p, &endian)) } if ke.KVNO == 0 { // Handles if the value from the last 4 bytes was zero and also if there are not the 4 bytes present. Makes sense to put the same value here as KVNO8 ke.KVNO = uint32(ke.KVNO8) } // Add the entry to the keytab kt.Entries = append(kt.Entries, ke) } // Check if there are still 4 bytes left to read if n > len(b) || len(b[n:]) < 4 { break } // Read the size of the next entry l = readInt32(b, &n, &endian) } return } func (e entry) marshal(v int) ([]byte, error) { var b []byte pb, err := e.Principal.marshal(v) if err != nil { return b, err } b = append(b, pb...) var endian binary.ByteOrder endian = binary.BigEndian if v == 1 && isNativeEndianLittle() { endian = binary.LittleEndian } t := make([]byte, 9) endian.PutUint32(t[0:4], uint32(e.Timestamp.Unix())) t[4] = byte(e.KVNO8) endian.PutUint16(t[5:7], uint16(e.Key.KeyType)) endian.PutUint16(t[7:9], uint16(len(e.Key.KeyValue))) b = append(b, t...) buf := new(bytes.Buffer) err = binary.Write(buf, endian, e.Key.KeyValue) if err != nil { return b, err } b = append(b, buf.Bytes()...) t = make([]byte, 4) endian.PutUint32(t, e.KVNO) b = append(b, t...) // Add the length header t = make([]byte, 4) endian.PutUint32(t, uint32(len(b))) b = append(t, b...) return b, nil } // Parse the Keytab bytes of a principal into a Keytab entry's principal. func parsePrincipal(b []byte, p *int, kt *Keytab, ke *entry, e *binary.ByteOrder) error { ke.Principal.NumComponents = readInt16(b, p, e) if kt.Version == 1 { //In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2 ke.Principal.NumComponents-- } lenRealm := readInt16(b, p, e) ke.Principal.Realm = string(readBytes(b, p, int(lenRealm), e)) for i := 0; i < int(ke.Principal.NumComponents); i++ { l := readInt16(b, p, e) ke.Principal.Components = append(ke.Principal.Components, string(readBytes(b, p, int(l), e))) } if kt.Version != 1 { //Name Type is omitted in version 1 ke.Principal.NameType = readInt32(b, p, e) } return nil } func (p principal) marshal(v int) ([]byte, error) { //var b []byte b := make([]byte, 2) var endian binary.ByteOrder endian = binary.BigEndian if v == 1 && isNativeEndianLittle() { endian = binary.LittleEndian } endian.PutUint16(b[0:], uint16(p.NumComponents)) realm, err := marshalString(p.Realm, v) if err != nil { return b, err } b = append(b, realm...) for _, c := range p.Components { cb, err := marshalString(c, v) if err != nil { return b, err } b = append(b, cb...) } if v != 1 { t := make([]byte, 4) endian.PutUint32(t, uint32(p.NameType)) b = append(b, t...) } return b, nil } func marshalString(s string, v int) ([]byte, error) { sb := []byte(s) b := make([]byte, 2) var endian binary.ByteOrder endian = binary.BigEndian if v == 1 && isNativeEndianLittle() { endian = binary.LittleEndian } endian.PutUint16(b[0:], uint16(len(sb))) buf := new(bytes.Buffer) err := binary.Write(buf, endian, sb) if err != nil { return b, err } b = append(b, buf.Bytes()...) return b, err } // Read bytes representing a timestamp. func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time { return time.Unix(int64(readInt32(b, p, e)), 0) } // Read bytes representing an eight bit integer. func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) { buf := bytes.NewBuffer(b[*p : *p+1]) binary.Read(buf, *e, &i) *p++ return } // Read bytes representing a sixteen bit integer. func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) { buf := bytes.NewBuffer(b[*p : *p+2]) binary.Read(buf, *e, &i) *p += 2 return } // Read bytes representing a thirty two bit integer. func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) { buf := bytes.NewBuffer(b[*p : *p+4]) binary.Read(buf, *e, &i) *p += 4 return } func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte { buf := bytes.NewBuffer(b[*p : *p+s]) r := make([]byte, s) binary.Read(buf, *e, &r) *p += s return r } func isNativeEndianLittle() bool { var x = 0x012345678 var p = unsafe.Pointer(&x) var bp = (*[4]byte)(p) var endian bool if 0x01 == bp[0] { endian = false } else if (0x78 & 0xff) == (bp[0] & 0xff) { endian = true } else { // Default to big endian endian = false } return endian } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/keytab/keytab_test.go000066400000000000000000000033321362537225700247020ustar00rootroot00000000000000package keytab import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestParse(t *testing.T) { t.Parallel() dat, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, err := Parse(dat) if err != nil { t.Fatalf("Error parsing keytab data: %v\n", err) } assert.Equal(t, uint8(2), kt.Version, "Keytab version not as expected") assert.Equal(t, uint32(1), kt.Entries[0].KVNO, "KVNO not as expected") assert.Equal(t, uint8(1), kt.Entries[0].KVNO8, "KVNO8 not as expected") assert.Equal(t, time.Unix(1505669592, 0), kt.Entries[0].Timestamp, "Timestamp not as expected") assert.Equal(t, int32(17), kt.Entries[0].Key.KeyType, "Key's EType not as expected") assert.Equal(t, "698c4df8e9f60e7eea5a21bf4526ad25", hex.EncodeToString(kt.Entries[0].Key.KeyValue), "Key material not as expected") assert.Equal(t, int16(1), kt.Entries[0].Principal.NumComponents, "Number of components in principal not as expected") assert.Equal(t, int32(1), kt.Entries[0].Principal.NameType, "Name type of principal not as expected") assert.Equal(t, "TEST.GOKRB5", kt.Entries[0].Principal.Realm, "Realm of principal not as expected") assert.Equal(t, "testuser1", kt.Entries[0].Principal.Components[0], "Component in principal not as expected") } func TestMarshal(t *testing.T) { t.Parallel() dat, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, err := Parse(dat) if err != nil { t.Fatalf("Error parsing keytab data: %v\n", err) } b, err := kt.Marshal() if err != nil { t.Fatalf("Error marshaling: %v", err) } assert.Equal(t, dat, b, "Marshaled bytes not the same as input bytes") _, err = Parse(b) if err != nil { t.Fatalf("Error parsing marshaled bytes: %v", err) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/000077500000000000000000000000001362537225700224055ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/error.go000066400000000000000000000032301362537225700240630ustar00rootroot00000000000000// Package krberror provides error type and functions for gokrb5. package krberror import ( "fmt" "strings" ) // Error type descriptions. const ( separator = " < " EncodingError = "Encoding_Error" NetworkingError = "Networking_Error" DecryptingError = "Decrypting_Error" EncryptingError = "Encrypting_Error" ChksumError = "Checksum_Error" KRBMsgError = "KRBMessage_Handling_Error" ConfigError = "Configuration_Error" KDCError = "KDC_Error" ) // Krberror is an error type for gokrb5 type Krberror struct { RootCause string EText []string } // Error function to implement the error interface. func (e Krberror) Error() string { return fmt.Sprintf("[Root cause: %s] ", e.RootCause) + strings.Join(e.EText, separator) } // Add another error statement to the error. func (e *Krberror) Add(et string, s string) { e.EText = append([]string{fmt.Sprintf("%s: %s", et, s)}, e.EText...) } // NewKrberror creates a new instance of Krberror. func NewKrberror(et, s string) Krberror { return Krberror{ RootCause: et, EText: []string{s}, } } // Errorf appends to or creates a new Krberror. func Errorf(err error, et, format string, a ...interface{}) Krberror { if e, ok := err.(Krberror); ok { e.Add(et, fmt.Sprintf(format, a...)) return e } return NewErrorf(et, format+": %s", append(a, err)...) } // NewErrorf creates a new Krberror from a formatted string. func NewErrorf(et, format string, a ...interface{}) Krberror { var s string if len(a) > 0 { s = fmt.Sprintf("%s: %s", et, fmt.Sprintf(format, a...)) } else { s = fmt.Sprintf("%s: %s", et, format) } return Krberror{ RootCause: et, EText: []string{s}, } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/krberror/error_test.go000066400000000000000000000014221362537225700251230ustar00rootroot00000000000000package krberror import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestErrorf(t *testing.T) { err := fmt.Errorf("an error") var a Krberror a = Errorf(err, "cause", "some text") assert.Equal(t, "[Root cause: cause] cause: some text: an error", a.Error()) a = Errorf(err, "cause", "arg1=%d arg2=%s", 123, "arg") assert.Equal(t, "[Root cause: cause] cause: arg1=123 arg2=arg: an error", a.Error()) err = NewErrorf("another error", "some text") a = Errorf(err, "cause", "some text") assert.Equal(t, "[Root cause: another error] cause: some text < another error: some text", a.Error()) a = Errorf(err, "cause", "arg1=%d arg2=%s", 123, "arg") assert.Equal(t, "[Root cause: another error] cause: arg1=123 arg2=arg < another error: some text", a.Error()) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/000077500000000000000000000000001362537225700223645ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APRep.go000066400000000000000000000032671362537225700236720ustar00rootroot00000000000000package messages import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) // APRep implements RFC 4120 KRB_AP_REP: https://tools.ietf.org/html/rfc4120#section-5.5.2. type APRep struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` EncPart types.EncryptedData `asn1:"explicit,tag:2"` } // EncAPRepPart is the encrypted part of KRB_AP_REP. type EncAPRepPart struct { CTime time.Time `asn1:"generalized,explicit,tag:0"` Cusec int `asn1:"explicit,tag:1"` Subkey types.EncryptionKey `asn1:"optional,explicit,tag:2"` SequenceNumber int64 `asn1:"optional,explicit,tag:3"` } // Unmarshal bytes b into the APRep struct. func (a *APRep) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREP)) if err != nil { return processUnmarshalReplyError(b, err) } expectedMsgType := msgtype.KRB_AP_REP if a.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_AP_REP. Expected: %v; Actual: %v", expectedMsgType, a.MsgType) } return nil } // Unmarshal bytes b into the APRep encrypted part struct. func (a *EncAPRepPart) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncAPRepPart)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "AP_REP unmarshal error") } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APRep_test.go000066400000000000000000000045711362537225700247300ustar00rootroot00000000000000package messages import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalAPRep(t *testing.T) { t.Parallel() var a APRep v := "encode_krb5_ap_rep" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AP_REP, a.MsgType, "MsgType is not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Ticket encPart etype not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Ticket encPart KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Ticket encPart cipher not as expected") } func TestUnmarshalEncAPRepPart(t *testing.T) { t.Parallel() var a EncAPRepPart v := "encode_krb5_ap_rep_enc_part" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, tt, a.CTime, "CTime not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, int32(1), a.Subkey.KeyType, "Subkey type not as expected") assert.Equal(t, []byte("12345678"), a.Subkey.KeyValue, "Subkey value not as expected") assert.Equal(t, int64(17), a.SequenceNumber, "Sequence number not as expected") } func TestUnmarshalEncAPRepPart_optionalsNULL(t *testing.T) { t.Parallel() var a EncAPRepPart v := "encode_krb5_ap_rep_enc_part(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, tt, a.CTime, "CTime not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APReq.go000066400000000000000000000106461362537225700236720ustar00rootroot00000000000000package messages import ( "fmt" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) type marshalAPReq struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` APOptions asn1.BitString `asn1:"explicit,tag:2"` // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag Ticket asn1.RawValue `asn1:"explicit,tag:3"` Authenticator types.EncryptedData `asn1:"explicit,tag:4"` } // APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1. type APReq struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` APOptions asn1.BitString `asn1:"explicit,tag:2"` Ticket Ticket `asn1:"explicit,tag:3"` Authenticator types.EncryptedData `asn1:"explicit,tag:4"` } // NewAPReq generates a new KRB_AP_REQ struct. func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) { var a APReq ed, err := encryptAuthenticator(auth, sessionKey, tkt) if err != nil { return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ") } a = APReq{ PVNO: iana.PVNO, MsgType: msgtype.KRB_AP_REQ, APOptions: types.NewKrbFlags(), Ticket: tkt, Authenticator: ed, } return a, nil } // Encrypt Authenticator func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) { var ed types.EncryptedData m, err := a.Marshal() if err != nil { return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator") } usage := authenticatorKeyUsage(tkt.SName) ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO) if err != nil { return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator") } return ed, nil } // DecryptAuthenticator decrypts the Authenticator within the AP_REQ. // sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ. func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) (auth types.Authenticator, err error) { usage := authenticatorKeyUsage(a.Ticket.SName) ab, e := crypto.DecryptEncPart(a.Authenticator, sessionKey, uint32(usage)) if e != nil { err = fmt.Errorf("error decrypting authenticator: %v", e) return } e = auth.Unmarshal(ab) if e != nil { err = fmt.Errorf("error unmarshaling authenticator") return } return } func authenticatorKeyUsage(pn types.PrincipalName) int { if pn.NameString[0] == "krbtgt" { return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR } return keyusage.AP_REQ_AUTHENTICATOR } // Unmarshal bytes b into the APReq struct. func (a *APReq) Unmarshal(b []byte) error { var m marshalAPReq _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ") } if m.MsgType != msgtype.KRB_AP_REQ { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AP_REQ. Expected: %v; Actual: %v", msgtype.KRB_AP_REQ, m.MsgType) } a.PVNO = m.PVNO a.MsgType = m.MsgType a.APOptions = m.APOptions a.Authenticator = m.Authenticator a.Ticket, err = UnmarshalTicket(m.Ticket.Bytes) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ") } return nil } // Marshal APReq struct. func (a *APReq) Marshal() ([]byte, error) { m := marshalAPReq{ PVNO: a.PVNO, MsgType: a.MsgType, APOptions: a.APOptions, Authenticator: a.Authenticator, } var b []byte b, err := a.Ticket.Marshal() if err != nil { return b, err } m.Ticket = asn1.RawValue{ Class: asn1.ClassContextSpecific, IsCompound: true, Tag: 3, Bytes: b, } mk, err := asn1.Marshal(m) if err != nil { return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ") } mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ) return mk, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/APReq_test.go000066400000000000000000000042021362537225700247200ustar00rootroot00000000000000package messages import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalAPReq(t *testing.T) { t.Parallel() var a APReq v := "encode_krb5_ap_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AP_REQ, a.MsgType, "MsgType is not as expected") assert.Equal(t, "fedcba98", hex.EncodeToString(a.APOptions.Bytes), "AP Options not as expected") assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "Ticket VNO not as expected") assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket SName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "Ticket SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName name string entries not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Ticket encPart etype not as expected") assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encPart KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Ticket.EncPart.Cipher, "Ticket encPart cipher not as expected") } func TestMarshalAPReq(t *testing.T) { t.Parallel() var a APReq v := "encode_krb5_ap_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshal bytes of Authenticator not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCRep.go000066400000000000000000000335551362537225700237760ustar00rootroot00000000000000package messages // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.4.2 import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/flags" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) type marshalKDCRep struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` PAData types.PADataSequence `asn1:"explicit,optional,tag:2"` CRealm string `asn1:"generalstring,explicit,tag:3"` CName types.PrincipalName `asn1:"explicit,tag:4"` // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag Ticket asn1.RawValue `asn1:"explicit,tag:5"` EncPart types.EncryptedData `asn1:"explicit,tag:6"` } // KDCRepFields represents the KRB_KDC_REP fields. type KDCRepFields struct { PVNO int MsgType int PAData []types.PAData CRealm string CName types.PrincipalName Ticket Ticket EncPart types.EncryptedData DecryptedEncPart EncKDCRepPart } // ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2. type ASRep struct { KDCRepFields } // TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2. type TGSRep struct { KDCRepFields } // EncKDCRepPart is the encrypted part of KRB_KDC_REP. type EncKDCRepPart struct { Key types.EncryptionKey `asn1:"explicit,tag:0"` LastReqs []LastReq `asn1:"explicit,tag:1"` Nonce int `asn1:"explicit,tag:2"` KeyExpiration time.Time `asn1:"generalized,explicit,optional,tag:3"` Flags asn1.BitString `asn1:"explicit,tag:4"` AuthTime time.Time `asn1:"generalized,explicit,tag:5"` StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"` EndTime time.Time `asn1:"generalized,explicit,tag:7"` RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"` SRealm string `asn1:"generalstring,explicit,tag:9"` SName types.PrincipalName `asn1:"explicit,tag:10"` CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"` EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"` } // LastReq part of KRB_KDC_REP. type LastReq struct { LRType int32 `asn1:"explicit,tag:0"` LRValue time.Time `asn1:"generalized,explicit,tag:1"` } // Unmarshal bytes b into the ASRep struct. func (k *ASRep) Unmarshal(b []byte) error { var m marshalKDCRep _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP)) if err != nil { return processUnmarshalReplyError(b, err) } if m.MsgType != msgtype.KRB_AS_REP { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType) } //Process the raw ticket within tkt, err := UnmarshalTicket(m.Ticket.Bytes) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP") } k.KDCRepFields = KDCRepFields{ PVNO: m.PVNO, MsgType: m.MsgType, PAData: m.PAData, CRealm: m.CRealm, CName: m.CName, Ticket: tkt, EncPart: m.EncPart, } return nil } // Unmarshal bytes b into the TGSRep struct. func (k *TGSRep) Unmarshal(b []byte) error { var m marshalKDCRep _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP)) if err != nil { return processUnmarshalReplyError(b, err) } if m.MsgType != msgtype.KRB_TGS_REP { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType) } //Process the raw ticket within tkt, err := UnmarshalTicket(m.Ticket.Bytes) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP") } k.KDCRepFields = KDCRepFields{ PVNO: m.PVNO, MsgType: m.MsgType, PAData: m.PAData, CRealm: m.CRealm, CName: m.CName, Ticket: tkt, EncPart: m.EncPart, } return nil } // Unmarshal bytes b into encrypted part of KRB_KDC_REP. func (e *EncKDCRepPart) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart)) if err != nil { // Try using tag 26 _, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP") } } return nil } // DecryptEncPart decrypts the encrypted part of an AS_REP. func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) { var key types.EncryptionKey var err error if c.HasKeytab() { key, err = c.Keytab.GetEncryptionKey(k.CName.NameString, k.CRealm, k.EncPart.KVNO, k.EncPart.EType) if err != nil { return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") } } if c.HasPassword() { key, _, err = crypto.GetKeyFromPassword(c.Password, k.CName, k.CRealm, k.EncPart.EType, k.PAData) if err != nil { return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") } } if !c.HasKeytab() && !c.HasPassword() { return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to preform decryption of AS_REP encrypted part") } b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART) if err != nil { return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part") } var denc EncKDCRepPart err = denc.Unmarshal(b) if err != nil { return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP") } k.DecryptedEncPart = denc return key, nil } // IsValid checks the validity of AS_REP message. func (k *ASRep) IsValid(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) { //Ref RFC 4120 Section 3.1.5 if k.CName.NameType != asReq.ReqBody.CName.NameType || k.CName.NameString == nil { return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName) } for i := range k.CName.NameString { if k.CName.NameString[i] != asReq.ReqBody.CName.NameString[i] { return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName) } } if k.CRealm != asReq.ReqBody.Realm { return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm) } key, err := k.DecryptEncPart(creds) if err != nil { return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP") } if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce { return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request") } if k.DecryptedEncPart.SName.NameType != asReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil { return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", asReq.ReqBody.SName, k.DecryptedEncPart.SName) } for i := range k.CName.NameString { if k.DecryptedEncPart.SName.NameString[i] != asReq.ReqBody.SName.NameString[i] { return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.SName, k.DecryptedEncPart.SName) } } if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm { return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm) } if len(asReq.ReqBody.Addresses) > 0 { if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) { return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ") } } t := time.Now().UTC() if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew { return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds()) } // RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11 if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) { if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) { return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation") } for _, pa := range k.DecryptedEncPart.EncPAData { if pa.PADataType == patype.PA_REQ_ENC_PA_REP { var pafast types.PAReqEncPARep err := pafast.Unmarshal(pa.PADataValue) if err != nil { return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP") } etype, err := crypto.GetChksumEtype(pafast.ChksumType) if err != nil { return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error") } ab, _ := asReq.Marshal() if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) { return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid") } } } } return true, nil } // DecryptEncPart decrypts the encrypted part of an TGS_REP. func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error { b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY) if err != nil { return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart") } var denc EncKDCRepPart err = denc.Unmarshal(b) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part") } k.DecryptedEncPart = denc return nil } // IsValid checks the validity of the TGS_REP message. func (k *TGSRep) IsValid(cfg *config.Config, tgsReq TGSReq) (bool, error) { if k.CName.NameType != tgsReq.ReqBody.CName.NameType || k.CName.NameString == nil { return false, krberror.NewErrorf(krberror.KRBMsgError, "CName type in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName) } for i := range k.CName.NameString { if k.CName.NameString[i] != tgsReq.ReqBody.CName.NameString[i] { return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName) } } if k.Ticket.Realm != tgsReq.ReqBody.Realm { return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm) } if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce { return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request") } //if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil { // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName) //} //for i := range k.Ticket.SName.NameString { // if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] { // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName) // } //} //if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil { // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName) //} //for i := range k.DecryptedEncPart.SName.NameString { // if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] { // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName) // } //} if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm { return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm) } if len(k.DecryptedEncPart.CAddr) > 0 { if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) { return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ") } } if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew { if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew { return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds()) } } return true, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCRep_test.go000066400000000000000000000515011362537225700250240ustar00rootroot00000000000000package messages import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/etypeID" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) const ( testuser1EType18Keytab = "05020000004b0001000b544553542e474f4b5242350009746573747573657231000000015898e0770100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de900000001" testuser1EType18ASREP = "6b8202f3308202efa003020105a10302010ba22e302c302aa103020113a2230421301f301da003020112a1161b14544553542e474f4b524235746573747573657231a30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a582015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010237e486e32cd18ab1ac9f8d42e93f8babd7b3497084cc5599f18ec61961c6d5242d350354d99d67a7604c451116188d16cb719e84377212eac2743440e8c504ef69c755e489cc6b65f935dd032bfc076f9b2c56d816197845b8fe857d738bc59712787631a50e86833d1b0e4732c8712c856417a6a257758e7d01d3182adb3233f0dde65d228c240ed26aa1af69f8d765dc0bc69096fdb037a75af220fea176839528d44b70f7dabfaa2ea506de1296f847176a60c501fd8cef8e0a51399bb6d5f753962d96292e93ffe344c6630db912931d46d88c0279f00719e22d0efcfd4ee33a702d0b660c1f13970a9beec12c0c8af3dda68bd81ac1fe3f126d2a24ebb445c5a682012c30820128a003020112a282011f0482011bb149cc16018072c4c18788d95a33aba540e52c11b54a93e67e788d05de75d8f3d4aa1afafbbfa6fde3eb40e5aa1890644cea2607efd5213a3fd00345b02eeb9ae1b589f36c74c689cd4ec1239dfe61e42ba6afa33f6240e3cfab291e4abb465d273302dbf7dbd148a299a9369044dd03377c1687e7dd36aa66501284a4ca50c0a7b08f4f87aecfa23b0dd0b11490e3ad330906dab715de81fc52f120d09c39990b8b5330d4601cc396b2ed258834329c4cc02c563a12de3ef9bf11e946258bc2ab5257f4caa4d443a7daf0fc25f6f531c2fcba88af8ca55c85300997cd05abbea52811fe2d038ba8f62fc8e3bc71ce04362d356ea2e1df8ac55c784c53cfb07817d48e39fe99fc8788040d98209c79dcf044d97e80de9f47824646" testRealm = "TEST.GOKRB5" testUser = "testuser1" testUserPassword = "passwordvalue" ) func TestUnmarshalASRep(t *testing.T) { t.Parallel() var a ASRep v := "encode_krb5_as_rep" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AS_REP, a.MsgType, "MsgType not as expected") assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected") for i, pa := range a.PAData { assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1)) } assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } func TestUnmarshalASRep_optionalsNULL(t *testing.T) { t.Parallel() var a ASRep v := "encode_krb5_as_rep(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AS_REP, a.MsgType, "MsgType not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } func TestUnmarshalTGSRep(t *testing.T) { t.Parallel() var a TGSRep v := "encode_krb5_tgs_rep" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_TGS_REP, a.MsgType, "MsgType not as expected") assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected") for i, pa := range a.PAData { assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1)) } assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } func TestUnmarshalTGSRep_optionalsNULL(t *testing.T) { t.Parallel() var a TGSRep v := "encode_krb5_tgs_rep(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_TGS_REP, a.MsgType, "MsgType not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "Client Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, iana.PVNO, a.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testdata.TEST_REALM, a.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.Ticket.SName.NameString), "SName in ticket does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Ticket.SName.NameString, "Ticket SName entries not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, iana.PVNO, a.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.Ticket.EncPart.Cipher), "Ticket encrypted part cipher not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "Encrypted part KVNO not as expected") assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } func TestUnmarshalEncKDCRepPart(t *testing.T) { t.Parallel() var a EncKDCRepPart v := "encode_krb5_enc_kdc_rep_part" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected") assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected") assert.Equal(t, 2, len(a.LastReqs), "Number of last request entries not as expected") for i, r := range a.LastReqs { assert.Equal(t, int32(-5), r.LRType, fmt.Sprintf("Last request typ not as expected for last request entry %d", i+1)) assert.Equal(t, tt, r.LRValue, fmt.Sprintf("Last request time value not as expected for last request entry %d", i+1)) } assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Nonce not as expected") assert.Equal(t, tt, a.KeyExpiration, "key expiration time not as expected") assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected") assert.Equal(t, tt, a.AuthTime, "Auth time not as expected") assert.Equal(t, tt, a.StartTime, "Start time not as expected") assert.Equal(t, tt, a.EndTime, "End time not as expected") assert.Equal(t, tt, a.RenewTill, "Renew Till time not as expected") assert.Equal(t, testdata.TEST_REALM, a.SRealm, "SRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "SName type not as expected") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName string entries not as expected") assert.Equal(t, 2, len(a.CAddr), "Number of client addresses not as expected") for i, addr := range a.CAddr { assert.Equal(t, int32(2), addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1)) } } func TestUnmarshalEncKDCRepPart_optionalsNULL(t *testing.T) { t.Parallel() var a EncKDCRepPart v := "encode_krb5_enc_kdc_rep_part(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected") assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected") assert.Equal(t, 2, len(a.LastReqs), "Number of last request entries not as expected") for i, r := range a.LastReqs { assert.Equal(t, int32(-5), r.LRType, fmt.Sprintf("Last request typ not as expected for last request entry %d", i+1)) assert.Equal(t, tt, r.LRValue, fmt.Sprintf("Last request time value not as expected for last request entry %d", i+1)) } assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Nonce not as expected") assert.Equal(t, "fe5cba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected") assert.Equal(t, tt, a.AuthTime, "Auth time not as expected") assert.Equal(t, tt, a.EndTime, "End time not as expected") assert.Equal(t, testdata.TEST_REALM, a.SRealm, "SRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "SName type not as expected") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName string entries not as expected") } func TestUnmarshalASRepDecodeAndDecrypt(t *testing.T) { t.Parallel() var asRep ASRep b, _ := hex.DecodeString(testuser1EType18ASREP) err := asRep.Unmarshal(b) if err != nil { t.Fatalf("AS REP Unmarshal error: %v\n", err) } assert.Equal(t, 5, asRep.PVNO, "PVNO not as expected") assert.Equal(t, 11, asRep.MsgType, "MsgType not as expected") assert.Equal(t, testRealm, asRep.CRealm, "Client Realm not as expected") assert.Equal(t, int32(1), asRep.CName.NameType, "CName NameType not as expected") assert.Equal(t, testUser, asRep.CName.NameString[0], "CName NameType not as expected") assert.Equal(t, int32(19), asRep.PAData[0].PADataType, "PADataType not as expected") assert.Equal(t, 5, asRep.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testRealm, asRep.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, int32(2), asRep.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, "krbtgt", asRep.Ticket.SName.NameString[0], "Ticket service name string not as expected") assert.Equal(t, testRealm, asRep.Ticket.SName.NameString[1], "Ticket service name string not as expected") assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, 1, asRep.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, etypeID.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected") //t.Log("Finished testing unecrypted parts of AS REP") ktb, _ := hex.DecodeString(testuser1EType18Keytab) kt, err := keytab.Parse(ktb) if err != nil { t.Fatalf("keytab parse error: %v\n", err) } cred := credentials.NewCredentials(testUser, testRealm) _, err = asRep.DecryptEncPart(cred.WithKeytab(kt)) if err != nil { t.Fatalf("Decryption of AS_REP EncPart failed: %v", err) } assert.Equal(t, int32(18), asRep.DecryptedEncPart.Key.KeyType, "KeyType in decrypted EncPart not as expected") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.LastReqs[0].LRValue, "LastReqs did not have a time value") assert.Equal(t, 2069991465, asRep.DecryptedEncPart.Nonce, "Nonce value not as expected") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.KeyExpiration, "Key expiration not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.AuthTime, "AuthTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.StartTime, "StartTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.EndTime, "StartTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.RenewTill, "RenewTill not a time type") assert.Equal(t, testRealm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected") assert.Equal(t, int32(2), asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected") assert.Equal(t, []string{"krbtgt", testRealm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected") } func TestUnmarshalASRepDecodeAndDecrypt_withPassword(t *testing.T) { t.Parallel() var asRep ASRep b, _ := hex.DecodeString(testuser1EType18ASREP) err := asRep.Unmarshal(b) if err != nil { t.Fatalf("AS REP Unmarshal error: %v\n", err) } assert.Equal(t, 5, asRep.PVNO, "PVNO not as expected") assert.Equal(t, 11, asRep.MsgType, "MsgType not as expected") assert.Equal(t, testRealm, asRep.CRealm, "Client Realm not as expected") assert.Equal(t, int32(1), asRep.CName.NameType, "CName NameType not as expected") assert.Equal(t, testUser, asRep.CName.NameString[0], "CName NameType not as expected") assert.Equal(t, int32(19), asRep.PAData[0].PADataType, "PADataType not as expected") assert.Equal(t, 5, asRep.Ticket.TktVNO, "TktVNO not as expected") assert.Equal(t, testRealm, asRep.Ticket.Realm, "Ticket Realm not as expected") assert.Equal(t, int32(2), asRep.Ticket.SName.NameType, "Ticket service nametype not as expected") assert.Equal(t, "krbtgt", asRep.Ticket.SName.NameString[0], "Ticket service name string not as expected") assert.Equal(t, testRealm, asRep.Ticket.SName.NameString[1], "Ticket service name string not as expected") assert.Equal(t, etypeID.AES256_CTS_HMAC_SHA1_96, asRep.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected") assert.Equal(t, 1, asRep.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected") assert.Equal(t, etypeID.AES256_CTS_HMAC_SHA1_96, asRep.EncPart.EType, "Etype of encrypted part not as expected") assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected") cred := credentials.NewCredentials(testUser, testRealm) _, err = asRep.DecryptEncPart(cred.WithPassword(testUserPassword)) if err != nil { t.Fatalf("Decryption of AS_REP EncPart failed: %v", err) } assert.Equal(t, int32(18), asRep.DecryptedEncPart.Key.KeyType, "KeyType in decrypted EncPart not as expected") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.LastReqs[0].LRValue, "LastReqs did not have a time value") assert.Equal(t, 2069991465, asRep.DecryptedEncPart.Nonce, "Nonce value not as expected") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.KeyExpiration, "Key expiration not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.AuthTime, "AuthTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.StartTime, "StartTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.EndTime, "StartTime not a time type") assert.IsType(t, time.Time{}, asRep.DecryptedEncPart.RenewTill, "RenewTill not a time type") assert.Equal(t, testRealm, asRep.DecryptedEncPart.SRealm, "Service realm not as expected") assert.Equal(t, nametype.KRB_NT_SRV_INST, asRep.DecryptedEncPart.SName.NameType, "Name type for AS_REP not as expected") assert.Equal(t, []string{"krbtgt", testRealm}, asRep.DecryptedEncPart.SName.NameString, "Service name string not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCReq.go000066400000000000000000000314741362537225700237750ustar00rootroot00000000000000package messages // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.4.1 import ( "crypto/rand" "fmt" "math" "math/big" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/flags" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) type marshalKDCReq struct { PVNO int `asn1:"explicit,tag:1"` MsgType int `asn1:"explicit,tag:2"` PAData types.PADataSequence `asn1:"explicit,optional,tag:3"` ReqBody asn1.RawValue `asn1:"explicit,tag:4"` } // KDCReqFields represents the KRB_KDC_REQ fields. type KDCReqFields struct { PVNO int MsgType int PAData types.PADataSequence ReqBody KDCReqBody Renewal bool } // ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1. type ASReq struct { KDCReqFields } // TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1. type TGSReq struct { KDCReqFields } type marshalKDCReqBody struct { KDCOptions asn1.BitString `asn1:"explicit,tag:0"` CName types.PrincipalName `asn1:"explicit,optional,tag:1"` Realm string `asn1:"generalstring,explicit,tag:2"` SName types.PrincipalName `asn1:"explicit,optional,tag:3"` From time.Time `asn1:"generalized,explicit,optional,tag:4"` Till time.Time `asn1:"generalized,explicit,tag:5"` RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` Nonce int `asn1:"explicit,tag:7"` EType []int32 `asn1:"explicit,tag:8"` Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"` // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"` } // KDCReqBody implements the KRB_KDC_REQ request body. type KDCReqBody struct { KDCOptions asn1.BitString `asn1:"explicit,tag:0"` CName types.PrincipalName `asn1:"explicit,optional,tag:1"` Realm string `asn1:"generalstring,explicit,tag:2"` SName types.PrincipalName `asn1:"explicit,optional,tag:3"` From time.Time `asn1:"generalized,explicit,optional,tag:4"` Till time.Time `asn1:"generalized,explicit,tag:5"` RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` Nonce int `asn1:"explicit,tag:7"` EType []int32 `asn1:"explicit,tag:8"` Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"` AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"` } // NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request. func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) { sname := types.PrincipalName{ NameType: nametype.KRB_NT_SRV_INST, NameString: []string{"krbtgt", realm}, } return NewASReq(realm, c, cname, sname) } // NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request. func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) { sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"kadmin", "changepw"}, } return NewASReq(realm, c, cname, sname) } // NewASReq generates a new KRB_AS_REQ struct for a given SNAME. func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) { nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) if err != nil { return ASReq{}, err } t := time.Now().UTC() // Copy the default options to make this thread safe kopts := types.NewKrbFlags() copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes) kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength a := ASReq{ KDCReqFields{ PVNO: iana.PVNO, MsgType: msgtype.KRB_AS_REQ, PAData: types.PADataSequence{}, ReqBody: KDCReqBody{ KDCOptions: kopts, Realm: realm, CName: cname, SName: sname, Till: t.Add(c.LibDefaults.TicketLifetime), Nonce: int(nonce.Int64()), EType: c.LibDefaults.DefaultTktEnctypeIDs, }, }, } if c.LibDefaults.Forwardable { types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable) } if c.LibDefaults.Canonicalize { types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize) } if c.LibDefaults.Proxiable { types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable) } if c.LibDefaults.RenewLifetime != 0 { types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable) a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime) a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour) } if !c.LibDefaults.NoAddresses { ha, err := types.LocalHostAddresses() if err != nil { return a, fmt.Errorf("could not get local addresses: %v", err) } ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...) a.ReqBody.Addresses = ha } return a, nil } // NewTGSReq generates a new KRB_TGS_REQ struct. func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tkt Ticket, sessionKey types.EncryptionKey, spn types.PrincipalName, renewal bool) (TGSReq, error) { nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) if err != nil { return TGSReq{}, err } t := time.Now().UTC() a := TGSReq{ KDCReqFields{ PVNO: iana.PVNO, MsgType: msgtype.KRB_TGS_REQ, ReqBody: KDCReqBody{ KDCOptions: types.NewKrbFlags(), Realm: kdcRealm, SName: spn, Till: t.Add(c.LibDefaults.TicketLifetime), Nonce: int(nonce.Int64()), EType: c.LibDefaults.DefaultTGSEnctypeIDs, }, Renewal: renewal, }, } if c.LibDefaults.Forwardable { types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable) } if c.LibDefaults.Canonicalize { types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize) } if c.LibDefaults.Proxiable { types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable) } if c.LibDefaults.RenewLifetime > time.Duration(0) { types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable) a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime) } if !c.LibDefaults.NoAddresses { ha, err := types.LocalHostAddresses() if err != nil { return a, fmt.Errorf("could not get local addresses: %v", err) } ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...) a.ReqBody.Addresses = ha } if renewal { types.SetFlag(&a.ReqBody.KDCOptions, flags.Renew) types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable) } auth, err := types.NewAuthenticator(tkt.Realm, cname) if err != nil { return a, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") } // Add the CName to make validation of the reply easier a.ReqBody.CName = auth.CName b, err := a.ReqBody.Marshal() if err != nil { return a, krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body") } etype, err := crypto.GetEtype(sessionKey.KeyType) if err != nil { return a, krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator") } cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM) if err != nil { return a, krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash") } auth.Cksum = types.Checksum{ CksumType: etype.GetHashID(), Checksum: cb, } apReq, err := NewAPReq(tkt, sessionKey, auth) if err != nil { return a, krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ") } apb, err := apReq.Marshal() if err != nil { return a, krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data") } a.PAData = types.PADataSequence{ types.PAData{ PADataType: patype.PA_TGS_REQ, PADataValue: apb, }, } return a, nil } // Unmarshal bytes b into the ASReq struct. func (k *ASReq) Unmarshal(b []byte) error { var m marshalKDCReq _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ") } expectedMsgType := msgtype.KRB_AS_REQ if m.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType) } var reqb KDCReqBody err = reqb.Unmarshal(m.ReqBody.Bytes) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body") } k.MsgType = m.MsgType k.PAData = m.PAData k.PVNO = m.PVNO k.ReqBody = reqb return nil } // Unmarshal bytes b into the TGSReq struct. func (k *TGSReq) Unmarshal(b []byte) error { var m marshalKDCReq _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ") } expectedMsgType := msgtype.KRB_TGS_REQ if m.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType) } var reqb KDCReqBody err = reqb.Unmarshal(m.ReqBody.Bytes) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body") } k.MsgType = m.MsgType k.PAData = m.PAData k.PVNO = m.PVNO k.ReqBody = reqb return nil } // Unmarshal bytes b into the KRB_KDC_REQ body struct. func (k *KDCReqBody) Unmarshal(b []byte) error { var m marshalKDCReqBody _, err := asn1.Unmarshal(b, &m) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body") } k.KDCOptions = m.KDCOptions if len(k.KDCOptions.Bytes) < 4 { tb := make([]byte, 4-len(k.KDCOptions.Bytes)) k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...) k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8 } k.CName = m.CName k.Realm = m.Realm k.SName = m.SName k.From = m.From k.Till = m.Till k.RTime = m.RTime k.Nonce = m.Nonce k.EType = m.EType k.Addresses = m.Addresses k.EncAuthData = m.EncAuthData if len(m.AdditionalTickets.Bytes) > 0 { k.AdditionalTickets, err = UnmarshalTicketsSequence(m.AdditionalTickets) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets") } } return nil } // Marshal ASReq struct. func (k *ASReq) Marshal() ([]byte, error) { m := marshalKDCReq{ PVNO: k.PVNO, MsgType: k.MsgType, PAData: k.PAData, } b, err := k.ReqBody.Marshal() if err != nil { var mk []byte return mk, err } m.ReqBody = asn1.RawValue{ Class: asn1.ClassContextSpecific, IsCompound: true, Tag: 4, Bytes: b, } mk, err := asn1.Marshal(m) if err != nil { return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ") } mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ) return mk, nil } // Marshal TGSReq struct. func (k *TGSReq) Marshal() ([]byte, error) { m := marshalKDCReq{ PVNO: k.PVNO, MsgType: k.MsgType, PAData: k.PAData, } b, err := k.ReqBody.Marshal() if err != nil { var mk []byte return mk, err } m.ReqBody = asn1.RawValue{ Class: asn1.ClassContextSpecific, IsCompound: true, Tag: 4, Bytes: b, } mk, err := asn1.Marshal(m) if err != nil { return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ") } mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ) return mk, nil } // Marshal KRB_KDC_REQ body struct. func (k *KDCReqBody) Marshal() ([]byte, error) { var b []byte m := marshalKDCReqBody{ KDCOptions: k.KDCOptions, CName: k.CName, Realm: k.Realm, SName: k.SName, From: k.From, Till: k.Till, RTime: k.RTime, Nonce: k.Nonce, EType: k.EType, Addresses: k.Addresses, EncAuthData: k.EncAuthData, } rawtkts, err := MarshalTicketSequence(k.AdditionalTickets) if err != nil { return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets") } //The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody rawtkts.Tag = 11 if len(rawtkts.Bytes) > 0 { m.AdditionalTickets = rawtkts } b, err = asn1.Marshal(m) if err != nil { return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body") } return b, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KDCReq_test.go000066400000000000000000000664151362537225700250370ustar00rootroot00000000000000package messages import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalKDCReqBody(t *testing.T) { t.Parallel() var a KDCReqBody v := "encode_krb5_kdc_req_body" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "fedcba90", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "Request body CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "Request body CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "Request body CName entries not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.From, "Request body From time not as expected") assert.Equal(t, tt, a.Till, "Request body Till time not as expected") assert.Equal(t, tt, a.RTime, "Request body RTime time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected") assert.Equal(t, 2, len(a.Addresses), "Number of client addresses not as expected") for i, addr := range a.Addresses { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1)) } assert.Equal(t, testdata.TEST_ETYPE, a.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected") assert.Equal(t, iana.PVNO, a.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected") assert.Equal(t, 2, len(a.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalKDCReqBody_optionalsNULLexceptsecond_ticket(t *testing.T) { t.Parallel() var a KDCReqBody v := "encode_krb5_kdc_req_body(optionalsNULLexceptsecond_ticket)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "fedcba98", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected") assert.Equal(t, tt, a.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 2, len(a.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalKDCReqBody_optionalsNULLexceptserver(t *testing.T) { t.Parallel() var a KDCReqBody v := "encode_krb5_kdc_req_body(optionalsNULLexceptserver)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "fedcba90", hex.EncodeToString(a.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 0, len(a.AdditionalTickets), "Number of additional tickets not empty") } func TestUnmarshalASReq(t *testing.T) { t.Parallel() var a ASReq v := "encode_krb5_as_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected") for i, pa := range a.PAData { assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1)) } assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.CName.NameType, "Request body CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.CName.NameString), "Request body CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.CName.NameString, "Request body CName entries not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.ReqBody.From, "Request body From time not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, tt, a.ReqBody.RTime, "Request body RTime time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 2, len(a.ReqBody.Addresses), "Number of client addresses not as expected") for i, addr := range a.ReqBody.Addresses { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1)) } assert.Equal(t, testdata.TEST_ETYPE, a.ReqBody.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected") assert.Equal(t, iana.PVNO, a.ReqBody.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.ReqBody.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected") assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.ReqBody.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalASReq_optionalsNULLexceptsecond_ticket(t *testing.T) { t.Parallel() var a ASReq v := "encode_krb5_as_req(optionalsNULLexceptsecond_ticket)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, "fedcba98", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.ReqBody.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalASReq_optionalsNULLexceptserver(t *testing.T) { t.Parallel() var a ASReq v := "encode_krb5_as_req(optionalsNULLexceptserver)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_AS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 0, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not empty") } func TestUnmarshalTGSReq(t *testing.T) { t.Parallel() var a TGSReq v := "encode_krb5_tgs_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 2, len(a.PAData), "Number of PAData items in the sequence not as expected") for i, pa := range a.PAData { assert.Equal(t, patype.PA_SAM_RESPONSE, pa.PADataType, fmt.Sprintf("PAData type for entry %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), pa.PADataValue, fmt.Sprintf("PAData valye for entry %d not as expected", i+1)) } assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.CName.NameType, "Request body CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.CName.NameString), "Request body CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.CName.NameString, "Request body CName entries not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.ReqBody.From, "Request body From time not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, tt, a.ReqBody.RTime, "Request body RTime time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 2, len(a.ReqBody.Addresses), "Number of client addresses not as expected") for i, addr := range a.ReqBody.Addresses { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1)) } assert.Equal(t, testdata.TEST_ETYPE, a.ReqBody.EncAuthData.EType, "Etype of request body encrypted authorization data not as expected") assert.Equal(t, iana.PVNO, a.ReqBody.EncAuthData.KVNO, "KVNO of request body encrypted authorization data not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.ReqBody.EncAuthData.Cipher, "Ciphertext of request body encrypted authorization data not as expected") assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.ReqBody.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalTGSReq_optionalsNULLexceptsecond_ticket(t *testing.T) { t.Parallel() var a TGSReq v := "encode_krb5_tgs_req(optionalsNULLexceptsecond_ticket)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, "fedcba98", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 2, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not as expected") for i, tkt := range a.ReqBody.AdditionalTickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Additional ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Additional ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Additional ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Additional ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Additional ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Additional ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Additional ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Additional ticket (%v) encPart cipher not as expected", i+1)) } } func TestUnmarshalTGSReq_optionalsNULLexceptserver(t *testing.T) { t.Parallel() var a TGSReq v := "encode_krb5_tgs_req(optionalsNULLexceptserver)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_TGS_REQ, a.MsgType, "Message ID not as expected") assert.Equal(t, 0, len(a.PAData), "Number of PAData items in the sequence not as expected") assert.Equal(t, "fedcba90", hex.EncodeToString(a.ReqBody.KDCOptions.Bytes), "Request body flags not as expected") assert.Equal(t, testdata.TEST_REALM, a.ReqBody.Realm, "Request body Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.ReqBody.SName.NameType, "Request body SName nametype not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.ReqBody.SName.NameString), "Request body SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.ReqBody.SName.NameString, "Request body SName entries not as expected") assert.Equal(t, tt, a.ReqBody.Till, "Request body Till time not as expected") assert.Equal(t, testdata.TEST_NONCE, a.ReqBody.Nonce, "Request body nounce not as expected") assert.Equal(t, []int32{0, 1}, a.ReqBody.EType, "Etype list not as expected") assert.Equal(t, 0, len(a.ReqBody.Addresses), "Number of client addresses not empty") assert.Equal(t, 0, len(a.ReqBody.EncAuthData.Cipher), "Ciphertext of request body encrypted authorization data not empty") assert.Equal(t, 0, len(a.ReqBody.AdditionalTickets), "Number of additional tickets not empty") } //// Marshal Tests //// func TestMarshalKDCReqBody(t *testing.T) { t.Parallel() var a KDCReqBody v := "encode_krb5_kdc_req_body" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } // Marshal and re-unmarshal the result nd then compare mb, err := a.Marshal() if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, b, mb, "Marshal bytes of KDCReqBody not as expected") } func TestMarshalASReq(t *testing.T) { t.Parallel() var a ASReq v := "encode_krb5_as_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshal bytes of ASReq not as expected") } func TestMarshalTGSReq(t *testing.T) { t.Parallel() var a TGSReq v := "encode_krb5_tgs_req" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshal bytes of TGSReq not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBCred.go000066400000000000000000000073741362537225700241420ustar00rootroot00000000000000package messages import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) type marshalKRBCred struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` Tickets asn1.RawValue `asn1:"explicit,tag:2"` EncPart types.EncryptedData `asn1:"explicit,tag:3"` } // KRBCred implements RFC 4120 KRB_CRED: https://tools.ietf.org/html/rfc4120#section-5.8.1. type KRBCred struct { PVNO int MsgType int Tickets []Ticket EncPart types.EncryptedData DecryptedEncPart EncKrbCredPart } // EncKrbCredPart is the encrypted part of KRB_CRED. type EncKrbCredPart struct { TicketInfo []KrbCredInfo `asn1:"explicit,tag:0"` Nouce int `asn1:"optional,explicit,tag:1"` Timestamp time.Time `asn1:"generalized,optional,explicit,tag:2"` Usec int `asn1:"optional,explicit,tag:3"` SAddress types.HostAddress `asn1:"optional,explicit,tag:4"` RAddress types.HostAddress `asn1:"optional,explicit,tag:5"` } // KrbCredInfo is the KRB_CRED_INFO part of KRB_CRED. type KrbCredInfo struct { Key types.EncryptionKey `asn1:"explicit,tag:0"` PRealm string `asn1:"generalstring,optional,explicit,tag:1"` PName types.PrincipalName `asn1:"optional,explicit,tag:2"` Flags asn1.BitString `asn1:"optional,explicit,tag:3"` AuthTime time.Time `asn1:"generalized,optional,explicit,tag:4"` StartTime time.Time `asn1:"generalized,optional,explicit,tag:5"` EndTime time.Time `asn1:"generalized,optional,explicit,tag:6"` RenewTill time.Time `asn1:"generalized,optional,explicit,tag:7"` SRealm string `asn1:"optional,explicit,ia5,tag:8"` SName types.PrincipalName `asn1:"optional,explicit,tag:9"` CAddr types.HostAddresses `asn1:"optional,explicit,tag:10"` } // Unmarshal bytes b into the KRBCred struct. func (k *KRBCred) Unmarshal(b []byte) error { var m marshalKRBCred _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBCred)) if err != nil { return processUnmarshalReplyError(b, err) } expectedMsgType := msgtype.KRB_CRED if m.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_CRED. Expected: %v; Actual: %v", expectedMsgType, m.MsgType) } k.PVNO = m.PVNO k.MsgType = m.MsgType k.EncPart = m.EncPart if len(m.Tickets.Bytes) > 0 { k.Tickets, err = UnmarshalTicketsSequence(m.Tickets) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling tickets within KRB_CRED") } } return nil } // DecryptEncPart decrypts the encrypted part of a KRB_CRED. func (k *KRBCred) DecryptEncPart(key types.EncryptionKey) error { b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_CRED_ENCPART) if err != nil { return krberror.Errorf(err, krberror.DecryptingError, "error decrypting KRB_CRED EncPart") } var denc EncKrbCredPart err = denc.Unmarshal(b) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part of KRB_CRED") } k.DecryptedEncPart = denc return nil } // Unmarshal bytes b into the encrypted part of KRB_CRED. func (k *EncKrbCredPart) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbCredPart)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling EncKrbCredPart") } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBCred_test.go000066400000000000000000000215351362537225700251740ustar00rootroot00000000000000package messages import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalKRBCred(t *testing.T) { t.Parallel() var a KRBCred v := "encode_krb5_cred" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_CRED, a.MsgType, "Message type not as expected") assert.Equal(t, 2, len(a.Tickets), "Number of tickets not as expected") for i, tkt := range a.Tickets { assert.Equal(t, iana.PVNO, tkt.TktVNO, fmt.Sprintf("Ticket (%v) ticket-vno not as expected", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.Realm, fmt.Sprintf("Ticket (%v) realm not as expected", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Ticket (%v) SName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Ticket (%v) SName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Ticket (%v) SName name string entries not as expected", i+1)) assert.Equal(t, testdata.TEST_ETYPE, tkt.EncPart.EType, fmt.Sprintf("Ticket (%v) encPart etype not as expected", i+1)) assert.Equal(t, iana.PVNO, tkt.EncPart.KVNO, fmt.Sprintf("Ticket (%v) encPart KVNO not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), tkt.EncPart.Cipher, fmt.Sprintf("Ticket (%v) encPart cipher not as expected", i+1)) } assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "encPart etype not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "encPart KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "encPart cipher not as expected") } func TestUnmarshalEncCredPart(t *testing.T) { t.Parallel() var a EncKrbCredPart v := "encode_krb5_enc_cred_part" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, 2, len(a.TicketInfo), "Number of ticket info items not as expected") for i, tkt := range a.TicketInfo { assert.Equal(t, int32(1), tkt.Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1)) assert.Equal(t, []byte("12345678"), tkt.Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1)) assert.Equal(t, testdata.TEST_REALM, tkt.PRealm, fmt.Sprintf("PRealm not as expected on ticket info item %d", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.PName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.PName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.PName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1)) assert.Equal(t, "fedcba98", hex.EncodeToString(tkt.Flags.Bytes), fmt.Sprintf("Flags not as expected on ticket info %d", i+1)) assert.Equal(t, tt, tkt.AuthTime, fmt.Sprintf("Auth time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, tkt.StartTime, fmt.Sprintf("Start time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, tkt.EndTime, fmt.Sprintf("End time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, tkt.RenewTill, fmt.Sprintf("Renew Till time value not as expected for ticket info %d", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, tkt.SName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(tkt.SName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, tkt.SName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1)) assert.Equal(t, 2, len(tkt.CAddr), "Number of client addresses not as expected") for j, addr := range tkt.CAddr { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d within ticket info %d", j+1, i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d within ticket info %d", j+1, i+1)) } } assert.Equal(t, testdata.TEST_NONCE, a.Nouce, "Nouce not as expected") assert.Equal(t, tt, a.Timestamp, "Timestamp not as expected") assert.Equal(t, 123456, a.Usec, "Microseconds not as expected") assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress") assert.Equal(t, addrtype.IPv4, a.RAddress.AddrType, "RAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.RAddress.Address), "Address not as expected for RAddress") } func TestUnmarshalEncCredPart_optionalsNULL(t *testing.T) { t.Parallel() var a EncKrbCredPart v := "encode_krb5_enc_cred_part(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, 2, len(a.TicketInfo), "Number of ticket info items not as expected") //1st Ticket i := 0 assert.Equal(t, int32(1), a.TicketInfo[i].Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1)) assert.Equal(t, []byte("12345678"), a.TicketInfo[i].Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1)) //2nd Ticket i = 1 assert.Equal(t, int32(1), a.TicketInfo[i].Key.KeyType, fmt.Sprintf("Key type not as expected in ticket info item %d", i+1)) assert.Equal(t, []byte("12345678"), a.TicketInfo[i].Key.KeyValue, fmt.Sprintf("Key value not as expected in ticket info item %d", i+1)) assert.Equal(t, testdata.TEST_REALM, a.TicketInfo[i].PRealm, fmt.Sprintf("PRealm not as expected on ticket info item %d", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.TicketInfo[i].PName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.TicketInfo[i].PName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.TicketInfo[i].PName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1)) assert.Equal(t, "fedcba98", hex.EncodeToString(a.TicketInfo[i].Flags.Bytes), fmt.Sprintf("Flags not as expected on ticket info %d", i+1)) assert.Equal(t, tt, a.TicketInfo[i].AuthTime, fmt.Sprintf("Auth time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, a.TicketInfo[i].StartTime, fmt.Sprintf("Start time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, a.TicketInfo[i].EndTime, fmt.Sprintf("End time value not as expected for ticket info %d", i+1)) assert.Equal(t, tt, a.TicketInfo[i].RenewTill, fmt.Sprintf("Renew Till time value not as expected for ticket info %d", i+1)) assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.TicketInfo[i].SName.NameType, fmt.Sprintf("Ticket info (%v) PName NameType not as expected", i+1)) assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.TicketInfo[i].SName.NameString), fmt.Sprintf("Ticket info (%v) PName does not have the expected number of NameStrings", i+1)) assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.TicketInfo[i].SName.NameString, fmt.Sprintf("Ticket info (%v) PName name string entries not as expected", i+1)) assert.Equal(t, 2, len(a.TicketInfo[i].CAddr), "Number of client addresses not as expected") for j, addr := range a.TicketInfo[i].CAddr { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d within ticket info %d", j+1, i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d within ticket info %d", j+1, i+1)) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBError.go000066400000000000000000000056441362537225700243540ustar00rootroot00000000000000// Package messages implements Kerberos 5 message types and methods. package messages import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) // KRBError implements RFC 4120 KRB_ERROR: https://tools.ietf.org/html/rfc4120#section-5.9.1. type KRBError struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` CTime time.Time `asn1:"generalized,optional,explicit,tag:2"` Cusec int `asn1:"optional,explicit,tag:3"` STime time.Time `asn1:"generalized,explicit,tag:4"` Susec int `asn1:"explicit,tag:5"` ErrorCode int32 `asn1:"explicit,tag:6"` CRealm string `asn1:"generalstring,optional,explicit,tag:7"` CName types.PrincipalName `asn1:"optional,explicit,tag:8"` Realm string `asn1:"generalstring,explicit,tag:9"` SName types.PrincipalName `asn1:"explicit,tag:10"` EText string `asn1:"generalstring,optional,explicit,tag:11"` EData []byte `asn1:"optional,explicit,tag:12"` } // NewKRBError creates a new KRBError. func NewKRBError(sname types.PrincipalName, realm string, code int32, etext string) KRBError { t := time.Now().UTC() return KRBError{ PVNO: iana.PVNO, MsgType: msgtype.KRB_ERROR, STime: t, Susec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)), ErrorCode: code, SName: sname, Realm: realm, EText: etext, } } // Unmarshal bytes b into the KRBError struct. func (k *KRBError) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBError)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "KRB_ERROR unmarshal error") } expectedMsgType := msgtype.KRB_ERROR if k.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_ERROR. Expected: %v; Actual: %v", expectedMsgType, k.MsgType) } return nil } // Error method implementing error interface on KRBError struct. func (k KRBError) Error() string { etxt := fmt.Sprintf("KRB Error: %s", errorcode.Lookup(k.ErrorCode)) if k.EText != "" { etxt = fmt.Sprintf("%s - %s", etxt, k.EText) } return etxt } func processUnmarshalReplyError(b []byte, err error) error { switch err.(type) { case asn1.StructuralError: var krberr KRBError tmperr := krberr.Unmarshal(b) if tmperr != nil { return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply") } return krberr default: return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply") } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBError_test.go000066400000000000000000000070561362537225700254120ustar00rootroot00000000000000package messages import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalKRBError(t *testing.T) { t.Parallel() var a KRBError v := "encode_krb5_error" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO is not as expected") assert.Equal(t, msgtype.KRB_ERROR, a.MsgType, "Message type is not as expected") assert.Equal(t, tt, a.CTime, "CTime not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, tt, a.STime, "STime not as expected") assert.Equal(t, 123456, a.Susec, "Service microseconds not as expected") assert.Equal(t, errorcode.KRB_ERR_GENERIC, a.ErrorCode, "Error code not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Ticket SName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Ticket SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected") assert.Equal(t, "krb5data", a.EText, "EText not as expected") assert.Equal(t, []byte("krb5data"), a.EData, "EData not as expected") } func TestUnmarshalKRBError_optionalsNULL(t *testing.T) { t.Parallel() var a KRBError v := "encode_krb5_error(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO is not as expected") assert.Equal(t, msgtype.KRB_ERROR, a.MsgType, "Message type is not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, tt, a.STime, "STime not as expected") assert.Equal(t, 123456, a.Susec, "Service microseconds not as expected") assert.Equal(t, errorcode.KRB_ERR_GENERIC, a.ErrorCode, "Error code not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Ticket SName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Ticket SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBPriv.go000066400000000000000000000067241362537225700242030ustar00rootroot00000000000000package messages import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) // KRBPriv implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.7.1. type KRBPriv struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` EncPart types.EncryptedData `asn1:"explicit,tag:3"` DecryptedEncPart EncKrbPrivPart `asn1:"optional,omitempty"` // Not part of ASN1 bytes so marked as optional so unmarshalling works } // EncKrbPrivPart is the encrypted part of KRB_PRIV. type EncKrbPrivPart struct { UserData []byte `asn1:"explicit,tag:0"` Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"` Usec int `asn1:"optional,explicit,tag:2"` SequenceNumber int64 `asn1:"optional,explicit,tag:3"` SAddress types.HostAddress `asn1:"explicit,tag:4"` RAddress types.HostAddress `asn1:"optional,explicit,tag:5"` } // NewKRBPriv returns a new KRBPriv type. func NewKRBPriv(part EncKrbPrivPart) KRBPriv { return KRBPriv{ PVNO: iana.PVNO, MsgType: msgtype.KRB_PRIV, DecryptedEncPart: part, } } // Unmarshal bytes b into the KRBPriv struct. func (k *KRBPriv) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBPriv)) if err != nil { return processUnmarshalReplyError(b, err) } expectedMsgType := msgtype.KRB_PRIV if k.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_PRIV. Expected: %v; Actual: %v", expectedMsgType, k.MsgType) } return nil } // Unmarshal bytes b into the EncKrbPrivPart struct. func (k *EncKrbPrivPart) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbPrivPart)) if err != nil { return krberror.Errorf(err, krberror.EncodingError, "KRB_PRIV unmarshal error") } return nil } // Marshal the KRBPriv. func (k *KRBPriv) Marshal() ([]byte, error) { tk := KRBPriv{ PVNO: k.PVNO, MsgType: k.MsgType, EncPart: k.EncPart, } b, err := asn1.Marshal(tk) if err != nil { return []byte{}, err } b = asn1tools.AddASNAppTag(b, asnAppTag.KRBPriv) return b, nil } // EncryptEncPart encrypts the DecryptedEncPart within the KRBPriv. // Use to prepare for marshaling. func (k *KRBPriv) EncryptEncPart(key types.EncryptionKey) error { b, err := asn1.Marshal(k.DecryptedEncPart) if err != nil { return err } b = asn1tools.AddASNAppTag(b, asnAppTag.EncKrbPrivPart) k.EncPart, err = crypto.GetEncryptedData(b, key, keyusage.KRB_PRIV_ENCPART, 1) if err != nil { return err } return nil } // DecryptEncPart decrypts the encrypted part of the KRBPriv message. func (k *KRBPriv) DecryptEncPart(key types.EncryptionKey) error { b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_PRIV_ENCPART) if err != nil { return fmt.Errorf("error decrypting KRBPriv EncPart: %v", err) } err = k.DecryptedEncPart.Unmarshal(b) if err != nil { return fmt.Errorf("error unmarshaling encrypted part: %v", err) } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBPriv_test.go000066400000000000000000000106741362537225700252410ustar00rootroot00000000000000package messages import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestUnmarshalKRBPriv(t *testing.T) { t.Parallel() var a KRBPriv v := "encode_krb5_priv" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_PRIV, a.MsgType, "Message type not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "EncPart KVNO not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "EncPart etype not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Cipher text of EncPart not as expected") } func TestUnmarshalEncPrivPart(t *testing.T) { t.Parallel() var a EncKrbPrivPart v := "encode_krb5_enc_priv_part" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "krb5data", string(a.UserData), "User data not as expected") assert.Equal(t, tt, a.Timestamp, "Timestamp not as expected") assert.Equal(t, 123456, a.Usec, "Microseconds not as expected") assert.Equal(t, int64(17), a.SequenceNumber, "Sequence number not as expected") assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress") assert.Equal(t, addrtype.IPv4, a.RAddress.AddrType, "RAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.RAddress.Address), "Address not as expected for RAddress") } func TestUnmarshalEncPrivPart_optionalsNULL(t *testing.T) { t.Parallel() var a EncKrbPrivPart v := "encode_krb5_enc_priv_part(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, "krb5data", string(a.UserData), "User data not as expected") assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress") } func TestMarshalKRBPriv(t *testing.T) { t.Parallel() var a KRBPriv v := "encode_krb5_priv" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("error marshaling KRBPriv: %v", err) } assert.Equal(t, b, mb, "marshaled bytes not as expected") v = "encode_krb5_enc_priv_part" be, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v", v, err) } err = a.DecryptedEncPart.Unmarshal(be) if err != nil { t.Fatalf("Unmarshal error of %s: %v", v, err) } mb, err = a.Marshal() if err != nil { t.Fatalf("error marshaling KRBPriv: %v", err) } assert.Equal(t, b, mb, "marshaled bytes not as expected when it has decrypted encpart") } func TestKRBPriv_EncryptEncPart(t *testing.T) { t.Parallel() var a KRBPriv v := "encode_krb5_priv" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v", v, err) } v = "encode_krb5_enc_priv_part" b, err = hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v", v, err) } err = a.DecryptedEncPart.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v", v, err) } key := types.EncryptionKey{ KeyType: int32(18), KeyValue: []byte("12345678901234567890123456789012"), } err = a.EncryptEncPart(key) if err != nil { t.Fatalf("error encrypting encpart: %v", err) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBSafe.go000066400000000000000000000030221362537225700241250ustar00rootroot00000000000000package messages import ( "fmt" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/types" ) // KRBSafe implements RFC 4120 KRB_SAFE: https://tools.ietf.org/html/rfc4120#section-5.6.1. type KRBSafe struct { PVNO int `asn1:"explicit,tag:0"` MsgType int `asn1:"explicit,tag:1"` SafeBody KRBSafeBody `asn1:"explicit,tag:2"` Cksum types.Checksum `asn1:"explicit,tag:3"` } // KRBSafeBody implements the KRB_SAFE_BODY of KRB_SAFE. type KRBSafeBody struct { UserData []byte `asn1:"explicit,tag:0"` Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"` Usec int `asn1:"optional,explicit,tag:2"` SequenceNumber int64 `asn1:"optional,explicit,tag:3"` SAddress types.HostAddress `asn1:"explicit,tag:4"` RAddress types.HostAddress `asn1:"optional,explicit,tag:5"` } // Unmarshal bytes b into the KRBSafe struct. func (s *KRBSafe) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, s, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBSafe)) if err != nil { return processUnmarshalReplyError(b, err) } expectedMsgType := msgtype.KRB_SAFE if s.MsgType != expectedMsgType { return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_SAFE. Expected: %v; Actual: %v", expectedMsgType, s.MsgType) } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/KRBSafe_test.go000066400000000000000000000052311362537225700251700ustar00rootroot00000000000000package messages import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" "gopkg.in/jcmturner/gokrb5.v5/iana/msgtype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalKRBSafe(t *testing.T) { t.Parallel() var a KRBSafe v := "encode_krb5_safe" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_SAFE, a.MsgType, "MsgType is not as expected") assert.Equal(t, []byte("krb5data"), a.SafeBody.UserData, "Safe body userdata not as expected") assert.Equal(t, tt, a.SafeBody.Timestamp, "Safe body timestamp not as expected") assert.Equal(t, 123456, a.SafeBody.Usec, "Safe body microseconds not as expected") assert.Equal(t, int64(17), a.SafeBody.SequenceNumber, "Safe body sequence number not as expected") assert.Equal(t, addrtype.IPv4, a.SafeBody.SAddress.AddrType, "SAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.SAddress.Address), "SAddress not as expected") assert.Equal(t, addrtype.IPv4, a.SafeBody.RAddress.AddrType, "RAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.RAddress.Address), "RAddress not as expected") assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected") assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checksum not as expected") } func TestUnmarshalKRBSafe_optionalsNULL(t *testing.T) { t.Parallel() var a KRBSafe v := "encode_krb5_safe(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.PVNO, "PVNO not as expected") assert.Equal(t, msgtype.KRB_SAFE, a.MsgType, "MsgType is not as expected") assert.Equal(t, []byte("krb5data"), a.SafeBody.UserData, "Safe body userdata not as expected") assert.Equal(t, addrtype.IPv4, a.SafeBody.SAddress.AddrType, "SAddress type not as expected") assert.Equal(t, "12d00023", hex.EncodeToString(a.SafeBody.SAddress.Address), "SAddress not as expected") assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected") assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checksum not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/Ticket.go000066400000000000000000000212161362537225700241400ustar00rootroot00000000000000package messages import ( "crypto/rand" "fmt" "strings" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/adtype" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/pac" "gopkg.in/jcmturner/gokrb5.v5/types" ) // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.3 // Ticket implements the Kerberos ticket. type Ticket struct { TktVNO int `asn1:"explicit,tag:0"` Realm string `asn1:"generalstring,explicit,tag:1"` SName types.PrincipalName `asn1:"explicit,tag:2"` EncPart types.EncryptedData `asn1:"explicit,tag:3"` DecryptedEncPart EncTicketPart `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works } // EncTicketPart is the encrypted part of the Ticket. type EncTicketPart struct { Flags asn1.BitString `asn1:"explicit,tag:0"` Key types.EncryptionKey `asn1:"explicit,tag:1"` CRealm string `asn1:"generalstring,explicit,tag:2"` CName types.PrincipalName `asn1:"explicit,tag:3"` Transited TransitedEncoding `asn1:"explicit,tag:4"` AuthTime time.Time `asn1:"generalized,explicit,tag:5"` StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"` EndTime time.Time `asn1:"generalized,explicit,tag:7"` RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"` CAddr types.HostAddresses `asn1:"explicit,optional,tag:9"` AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"` } // TransitedEncoding part of the ticket's encrypted part. type TransitedEncoding struct { TRType int32 `asn1:"explicit,tag:0"` Contents []byte `asn1:"explicit,tag:1"` } // NewTicket creates a new Ticket instance. func NewTicket(cname types.PrincipalName, crealm string, sname types.PrincipalName, srealm string, flags asn1.BitString, sktab keytab.Keytab, eTypeID int32, kvno int, authTime, startTime, endTime, renewTill time.Time) (Ticket, types.EncryptionKey, error) { etype, err := crypto.GetEtype(eTypeID) if err != nil { return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting etype for new ticket") } ks := etype.GetKeyByteSize() kv := make([]byte, ks, ks) rand.Read(kv) sessionKey := types.EncryptionKey{ KeyType: eTypeID, KeyValue: kv, } etp := EncTicketPart{ Flags: flags, Key: sessionKey, CRealm: crealm, CName: cname, Transited: TransitedEncoding{}, AuthTime: authTime, StartTime: startTime, EndTime: endTime, RenewTill: renewTill, } b, err := asn1.Marshal(etp) if err != nil { return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncodingError, "error marshalling ticket encpart") } b = asn1tools.AddASNAppTag(b, asnAppTag.EncTicketPart) skey, err := sktab.GetEncryptionKey(sname.NameString, srealm, kvno, eTypeID) if err != nil { return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting encryption key for new ticket") } ed, err := crypto.GetEncryptedData(b, skey, keyusage.KDC_REP_TICKET, kvno) if err != nil { return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting ticket encpart") } tkt := Ticket{ TktVNO: iana.PVNO, Realm: srealm, SName: sname, EncPart: ed, } return tkt, sessionKey, nil } // Unmarshal bytes b into a Ticket struct. func (t *Ticket) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket)) return err } // Marshal the Ticket. func (t *Ticket) Marshal() ([]byte, error) { b, err := asn1.Marshal(*t) if err != nil { return nil, err } b = asn1tools.AddASNAppTag(b, asnAppTag.Ticket) return b, nil } // Unmarshal bytes b into the EncTicketPart struct. func (t *EncTicketPart) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart)) return err } // UnmarshalTicket returns a ticket from the bytes provided. func UnmarshalTicket(b []byte) (t Ticket, err error) { _, err = asn1.UnmarshalWithParams(b, &t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket)) return } // UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value. func UnmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) { //This is a workaround to a asn1 decoding issue in golang - https://github.com/golang/go/issues/17321. It's not pretty I'm afraid //We pull out raw values from the larger raw value (that is actually the data of the sequence of raw values) and track our position moving along the data. b := in.Bytes // Ignore the head of the asn1 stream (1 byte for tag and those for the length) as this is what tells us its a sequence but we're handling it ourselves p := 1 + asn1tools.GetNumberBytesInLengthHeader(in.Bytes) var tkts []Ticket var raw asn1.RawValue for p < (len(b)) { _, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket)) if err != nil { return nil, fmt.Errorf("unmarshaling sequence of tickets failed geting length of ticket: %v", err) } t, err := UnmarshalTicket(b[p:]) if err != nil { return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err) } p += len(raw.FullBytes) tkts = append(tkts, t) } MarshalTicketSequence(tkts) return tkts, nil } // MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence. func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) { raw := asn1.RawValue{ Class: 2, IsCompound: true, } if len(tkts) < 1 { // There are no tickets to marshal return raw, nil } var btkts []byte for i, t := range tkts { b, err := t.Marshal() if err != nil { return raw, fmt.Errorf("error marshaling ticket number %d in seqence of tickets", i+1) } btkts = append(btkts, b...) } btkts = append(asn1tools.MarshalLengthBytes(len(btkts)), btkts...) btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...) raw.Bytes = btkts // If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11) //fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes) return raw, nil } // DecryptEncPart decrypts the encrypted part of the ticket. func (t *Ticket) DecryptEncPart(keytab keytab.Keytab, ktprinc string) error { var upn types.PrincipalName realm := t.Realm if ktprinc != "" { var r string upn, r = types.ParseSPNString(ktprinc) if r != "" { realm = r } } else { upn = t.SName } key, err := keytab.GetEncryptionKey(upn.NameString, realm, t.EncPart.KVNO, t.EncPart.EType) if err != nil { return NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err)) } b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET) if err != nil { return fmt.Errorf("error decrypting Ticket EncPart: %v", err) } var denc EncTicketPart err = denc.Unmarshal(b) if err != nil { return fmt.Errorf("error unmarshaling encrypted part: %v", err) } t.DecryptedEncPart = denc return nil } // GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed. func (t *Ticket) GetPACType(keytab keytab.Keytab, ktprinc string) (bool, pac.PACType, error) { var isPAC bool for _, ad := range t.DecryptedEncPart.AuthorizationData { if ad.ADType == adtype.ADIfRelevant { var ad2 types.AuthorizationData err := ad2.Unmarshal(ad.ADData) if err != nil { continue } if ad2[0].ADType == adtype.ADWin2KPAC { isPAC = true var p pac.PACType err = p.Unmarshal(ad2[0].ADData) if err != nil { return isPAC, p, fmt.Errorf("error unmarshaling PAC: %v", err) } var upn []string if ktprinc != "" { upn = strings.Split(ktprinc, "/") } else { upn = t.SName.NameString } key, err := keytab.GetEncryptionKey(upn, t.Realm, t.EncPart.KVNO, t.EncPart.EType) if err != nil { return isPAC, p, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err)) } err = p.ProcessPACInfoBuffers(key) return isPAC, p, err } } } return isPAC, pac.PACType{}, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/messages/Ticket_test.go000066400000000000000000000154161362537225700252040ustar00rootroot00000000000000package messages import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" "gopkg.in/jcmturner/gokrb5.v5/iana/adtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/iana/trtype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestUnmarshalTicket(t *testing.T) { t.Parallel() var a Ticket v := "encode_krb5_ticket" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, iana.PVNO, a.TktVNO, "Ticket version number not as expected") assert.Equal(t, testdata.TEST_REALM, a.Realm, "Realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "SName name strings not as expected") assert.Equal(t, testdata.TEST_ETYPE, a.EncPart.EType, "Etype of Ticket EncPart not as expected") assert.Equal(t, iana.PVNO, a.EncPart.KVNO, "KNVO of Ticket EncPart not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.EncPart.Cipher, "Cipher of Ticket EncPart not as expected") } func TestUnmarshalEncTicketPart(t *testing.T) { t.Parallel() var a EncTicketPart v := "encode_krb5_enc_tkt_part" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected") assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected") assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName type not as expected") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName string entries not as expected") assert.Equal(t, trtype.DOMAIN_X500_COMPRESS, a.Transited.TRType, "Transisted type not as expected") assert.Equal(t, []byte("EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS."), a.Transited.Contents, "Transisted content not as expected") assert.Equal(t, tt, a.AuthTime, "Auth time not as expected") assert.Equal(t, tt, a.StartTime, "Start time not as expected") assert.Equal(t, tt, a.EndTime, "End time not as expected") assert.Equal(t, tt, a.RenewTill, "Renew Till time not as expected") assert.Equal(t, 2, len(a.CAddr), "Number of client addresses not as expected") for i, addr := range a.CAddr { assert.Equal(t, addrtype.IPv4, addr.AddrType, fmt.Sprintf("Host address type not as expected for address item %d", i+1)) assert.Equal(t, "12d00023", hex.EncodeToString(addr.Address), fmt.Sprintf("Host address not as expected for address item %d", i+1)) } for i, ele := range a.AuthorizationData { assert.Equal(t, adtype.ADIfRelevant, ele.ADType, fmt.Sprintf("Authorization data type of element %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), ele.ADData, fmt.Sprintf("Authorization data of element %d not as expected", i+1)) } } func TestUnmarshalEncTicketPart_optionalsNULL(t *testing.T) { t.Parallel() var a EncTicketPart v := "encode_krb5_enc_tkt_part(optionalsNULL)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, "fedcba98", hex.EncodeToString(a.Flags.Bytes), "Flags not as expected") assert.Equal(t, int32(1), a.Key.KeyType, "Key type not as expected") assert.Equal(t, []byte("12345678"), a.Key.KeyValue, "Key value not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName type not as expected") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName string entries not as expected") assert.Equal(t, trtype.DOMAIN_X500_COMPRESS, a.Transited.TRType, "Transisted type not as expected") assert.Equal(t, []byte("EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS."), a.Transited.Contents, "Transisted content not as expected") assert.Equal(t, tt, a.AuthTime, "Auth time not as expected") assert.Equal(t, tt, a.EndTime, "End time not as expected") } func TestMarshalTicket(t *testing.T) { t.Parallel() var a Ticket v := "encode_krb5_ticket" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshalled bytes not as expected") } func TestAuthorizationData_GetPACType_GOKRB5TestData(t *testing.T) { t.Parallel() v := "PAC_AuthorizationData_GOKRB5" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } var a types.AuthorizationData err = a.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } tkt := Ticket{ Realm: "TEST.GOKRB5", EncPart: types.EncryptedData{ EType: 18, KVNO: 2, }, DecryptedEncPart: EncTicketPart{ AuthorizationData: a, }, } b, _ = hex.DecodeString(testdata.SYSHTTP_KEYTAB) kt, _ := keytab.Parse(b) isPAC, pac, err := tkt.GetPACType(kt, "sysHTTP") if err != nil { t.Fatalf("Error getting PAC Type: %v\n", err) } assert.True(t, isPAC, "PAC should be present") assert.Equal(t, 5, len(pac.Buffers), "Number of buffers not as expected") assert.Equal(t, uint32(5), pac.CBuffers, "Count of buffers not as expected") assert.Equal(t, uint32(0), pac.Version, "PAC version not as expected") assert.NotNil(t, pac.KerbValidationInfo, "PAC Kerb Validation info is nil") assert.NotNil(t, pac.ClientInfo, "PAC Client Info info is nil") assert.NotNil(t, pac.UPNDNSInfo, "PAC UPN DNS Info info is nil") assert.NotNil(t, pac.KDCChecksum, "PAC KDC Checksum info is nil") assert.NotNil(t, pac.ServerChecksum, "PAC Server checksum info is nil") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/000077500000000000000000000000001362537225700222615ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/claims.go000066400000000000000000000217761362537225700240750ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/binary" "errors" "fmt" "gopkg.in/jcmturner/rpc.v0/ndr" ) // Compression format assigned numbers. const ( CompressionFormatNone uint16 = 0 CompressionFormatLZNT1 uint16 = 2 CompressionFormatXPress uint16 = 3 CompressionFormatXPressHuff uint16 = 4 ) // ClaimsSourceType const ClaimsSourceTypeAD uint16 = 1 // Claim Type assigned numbers const ( ClaimTypeIDInt64 uint16 = 1 ClaimTypeIDUInt64 uint16 = 2 ClaimTypeIDString uint16 = 3 ClaimsTypeIDBoolean uint16 = 6 ) // ClaimsBlob implements https://msdn.microsoft.com/en-us/library/hh554119.aspx type ClaimsBlob struct { Size uint32 EncodedBlob []byte } // ReadClaimsBlob reads a ClaimsBlob from the byte slice. func ReadClaimsBlob(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsBlob) { c.Size = ndr.ReadUint32(b, p, e) c.EncodedBlob = ndr.ReadBytes(b, p, int(c.Size), e) return } // ClaimsSetMetadata implements https://msdn.microsoft.com/en-us/library/hh554073.aspx type ClaimsSetMetadata struct { claimsSetSize uint32 ClaimsSet ClaimsSet CompressionFormat uint16 // Enum see constants for options uncompressedClaimsSetSize uint32 ReservedType uint16 reservedFieldSize uint32 ReservedField []byte } // ClaimSet implements https://msdn.microsoft.com/en-us/library/hh554122.aspx type ClaimsSet struct { ClaimsArrayCount uint32 ClaimsArrays []ClaimsArray ReservedType uint16 reservedFieldSize uint32 ReservedField []byte } // ClaimsArray implements https://msdn.microsoft.com/en-us/library/hh536458.aspx type ClaimsArray struct { ClaimsSourceType uint16 ClaimsCount uint32 ClaimsEntries []ClaimEntry } // ClaimEntry implements https://msdn.microsoft.com/en-us/library/hh536374.aspx type ClaimEntry struct { ID string //utf16string Type uint16 // enums are 16 bit https://msdn.microsoft.com/en-us/library/windows/desktop/aa366818(v=vs.85).aspx TypeInt64 ClaimTypeInt64 TypeUInt64 ClaimTypeUInt64 TypeString ClaimTypeString TypeBool ClaimTypeBoolean } // ClaimTypeInt64 is a claim of type int64 type ClaimTypeInt64 struct { ValueCount uint32 Value []int64 } // ClaimTypeUInt64 is a claim of type uint64 type ClaimTypeUInt64 struct { ValueCount uint32 Value []uint64 } // ClaimTypeString is a claim of type string type ClaimTypeString struct { ValueCount uint32 Value []string } // ClaimTypeBoolean is a claim of type bool type ClaimTypeBoolean struct { ValueCount uint32 Value []bool } // ReadClaimsSetMetadata reads a ClaimsSetMetadata from the bytes slice. func ReadClaimsSetMetadata(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsSetMetadata, err error) { c.claimsSetSize = ndr.ReadUint32(b, p, e) *p += 4 //Move over pointer to ClaimSet array c.CompressionFormat = ndr.ReadUint16(b, p, e) // TODO Currently compression is not supported so if it is compressed we just have to return. if c.CompressionFormat != CompressionFormatNone { *p = len(*b) return } c.uncompressedClaimsSetSize = ndr.ReadUint32(b, p, e) c.ReservedType = ndr.ReadUint16(b, p, e) c.reservedFieldSize = ndr.ReadUint32(b, p, e) *p += 4 //Move over pointer to ReservedField array var ah ndr.ConformantArrayHeader if c.claimsSetSize > 0 { // ClaimsSet is a conformant array https://msdn.microsoft.com/en-us/library/windows/desktop/aa373603(v=vs.85).aspx ah, err = ndr.ReadUniDimensionalConformantArrayHeader(b, p, e) if err != nil { return } if ah.MaxCount != int(c.claimsSetSize) { err = errors.New("error with size of CLAIMS_SET array") return } csb := ndr.ReadBytes(b, p, int(c.claimsSetSize), e) //TODO put decompression here c.ClaimsSet, err = ReadClaimsSet(csb) if err != nil { return } } if c.reservedFieldSize > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(b, p, e) if err != nil { return } if ah.MaxCount != int(c.reservedFieldSize) { err = errors.New("error with size of CLAIMS_SET_METADATA's reserved field array") return } c.ReservedField = ndr.ReadBytes(b, p, int(c.reservedFieldSize), e) } return } // ReadClaimsSet reads a ClaimsSet from the bytes slice. func ReadClaimsSet(b []byte) (c ClaimsSet, err error) { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { err = fmt.Errorf("error parsing NDR byte stream headers of CLAIMS_SET: %v", err) return } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 c.ClaimsArrayCount = ndr.ReadUint32(&b, &p, e) p += 4 //Move over pointer to claims array c.ReservedType = ndr.ReadUint16(&b, &p, e) c.reservedFieldSize = ndr.ReadUint32(&b, &p, e) p += 4 //Move over pointer to ReservedField array var ah ndr.ConformantArrayHeader if c.ClaimsArrayCount > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if err != nil { return } if ah.MaxCount != int(c.ClaimsArrayCount) { err = errors.New("error with size of CLAIMS_SET's claims array") return } c.ClaimsArrays = make([]ClaimsArray, c.ClaimsArrayCount, c.ClaimsArrayCount) for i := range c.ClaimsArrays { c.ClaimsArrays[i], err = ReadClaimsArray(&b, &p, e) if err != nil { return } } } if c.reservedFieldSize > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if err != nil { return } if ah.MaxCount != int(c.reservedFieldSize) { err = errors.New("error with size of CLAIMS_SET's reserved field array") return } c.ReservedField = ndr.ReadBytes(&b, &p, int(c.reservedFieldSize), e) } return c, nil } // ReadClaimsArray reads a ClaimsArray from the bytes slice. func ReadClaimsArray(b *[]byte, p *int, e *binary.ByteOrder) (c ClaimsArray, err error) { c.ClaimsSourceType = ndr.ReadUint16(b, p, e) c.ClaimsCount = ndr.ReadUint32(b, p, e) *p += 4 //Move over pointer to claims array ah, err := ndr.ReadUniDimensionalConformantArrayHeader(b, p, e) if err != nil { return } if ah.MaxCount != int(c.ClaimsCount) { err = errors.New("error with size of CLAIMS_ARRAY's claims entries") return } c.ClaimsEntries = make([]ClaimEntry, c.ClaimsCount, c.ClaimsCount) for i := range c.ClaimsEntries { var vc uint32 c.ClaimsEntries[i].Type, vc, err = ReadClaimEntriesUnionHeaders(b, p, e) if err != nil { return } switch c.ClaimsEntries[i].Type { case ClaimTypeIDInt64: c.ClaimsEntries[i].TypeInt64.ValueCount = vc case ClaimTypeIDUInt64: c.ClaimsEntries[i].TypeUInt64.ValueCount = vc case ClaimTypeIDString: c.ClaimsEntries[i].TypeString.ValueCount = vc case ClaimsTypeIDBoolean: c.ClaimsEntries[i].TypeBool.ValueCount = vc } } for i := range c.ClaimsEntries { err = FillClaimEntry(b, p, e, &c.ClaimsEntries[i]) if err != nil { return } } return } func ReadClaimEntriesUnionHeaders(b *[]byte, p *int, e *binary.ByteOrder) (uint16, uint32, error) { *p += 4 // This is an NDR union: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_39 t1 := ndr.ReadUint16(b, p, e) t2 := ndr.ReadUint16(b, p, e) if t1 != t2 { return 0, 0, ndr.Malformed{EText: "malformed NDR encoding of CLAIM_ENTRY union"} } vc := ndr.ReadUint32(b, p, e) *p += 4 //Move over pointer to array of values return t1, vc, nil } // FillClaimEntry reads a ClaimEntry from the bytes slice. func FillClaimEntry(b *[]byte, p *int, e *binary.ByteOrder, c *ClaimEntry) (err error) { c.ID, err = ndr.ReadConformantVaryingString(b, p, e) if err != nil { return } ah, err := ndr.ReadUniDimensionalConformantArrayHeader(b, p, e) if err != nil { return } switch c.Type { case ClaimTypeIDInt64: if ah.MaxCount != int(c.TypeInt64.ValueCount) { return errors.New("error with size of CLAIM_ENTRY's value") } c.TypeInt64.Value = make([]int64, c.TypeInt64.ValueCount, c.TypeInt64.ValueCount) for i := range c.TypeInt64.Value { buf := bytes.NewReader((*b)[*p : *p+8]) err = binary.Read(buf, *e, &c.TypeInt64.Value[i]) if err != nil { return } *p += 8 // progress position for a uint64 } case ClaimTypeIDUInt64: if ah.MaxCount != int(c.TypeUInt64.ValueCount) { return errors.New("error with size of CLAIM_ENTRY's value") } c.TypeUInt64.Value = make([]uint64, c.TypeUInt64.ValueCount, c.TypeUInt64.ValueCount) for i := range c.TypeUInt64.Value { c.TypeUInt64.Value[i] = ndr.ReadUint64(b, p, e) } case ClaimTypeIDString: if ah.MaxCount != int(c.TypeString.ValueCount) { return errors.New("error with size of CLAIM_ENTRY's value") } c.TypeString.Value = make([]string, c.TypeString.ValueCount, c.TypeString.ValueCount) *p += 4 * (int(c.TypeString.ValueCount)) // Move over pointers for i := range c.TypeString.Value { c.TypeString.Value[i], err = ndr.ReadConformantVaryingString(b, p, e) if err != nil { return } } case ClaimsTypeIDBoolean: if ah.MaxCount != int(c.TypeBool.ValueCount) { return errors.New("error with size of CLAIM_ENTRY's value") } c.TypeBool.Value = make([]bool, c.TypeBool.ValueCount, c.TypeBool.ValueCount) for i := range c.TypeBool.Value { if ndr.ReadUint64(b, p, e) != 0 { c.TypeBool.Value[i] = true } } } return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/filetime.go000066400000000000000000000027561362537225700244200ustar00rootroot00000000000000// Package mstypes implements representations of Microsoft types for PAC processing. package mstypes import ( "encoding/binary" "time" "gopkg.in/jcmturner/rpc.v0/ndr" ) const unixEpochDiff = 116444736000000000 // FileTime implements the Microsoft FILETIME type https://msdn.microsoft.com/en-us/library/cc230324.aspx type FileTime struct { LowDateTime uint32 HighDateTime uint32 } // Time return a golang Time type from the FileTime func (ft FileTime) Time() time.Time { ns := (ft.MSEpoch() - unixEpochDiff) * 100 return time.Unix(0, int64(ns)).UTC() } // MSEpoch returns the FileTime as a Microsoft epoch, the number of 100 nano second periods elapsed from January 1, 1601 UTC. func (ft FileTime) MSEpoch() int64 { return (int64(ft.HighDateTime) << 32) + int64(ft.LowDateTime) } // Unix returns the FileTime as a Unix time, the number of seconds elapsed since January 1, 1970 UTC. func (ft FileTime) Unix() int64 { return (ft.MSEpoch() - unixEpochDiff) / 10000000 } // GetFileTime returns a FileTime type from the provided Golang Time type. func GetFileTime(t time.Time) FileTime { ns := t.UnixNano() fp := (ns / 100) + unixEpochDiff hd := fp >> 32 ld := fp - (hd << 32) return FileTime{ LowDateTime: uint32(ld), HighDateTime: uint32(hd), } } // ReadFileTime reads a FileTime from the bytes slice. func ReadFileTime(b *[]byte, p *int, e *binary.ByteOrder) FileTime { l := ndr.ReadUint32(b, p, e) h := ndr.ReadUint32(b, p, e) return FileTime{ LowDateTime: l, HighDateTime: h, } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/filetime_test.go000066400000000000000000000007671362537225700254570ustar00rootroot00000000000000package mstypes import ( "github.com/stretchr/testify/assert" "testing" "time" ) func TestFileTime(t *testing.T) { t.Parallel() //2007-02-22 17:00:01.6382155 tt := time.Date(2007, 2, 22, 17, 0, 1, 638215500, time.UTC) ft := GetFileTime(tt) assert.Equal(t, tt.Unix(), ft.Unix(), "Unix epoch time not as expected") assert.Equal(t, int64(128166372016382155), ft.MSEpoch(), "MSEpoch not as expected") assert.Equal(t, tt, ft.Time(), "Golang time object returned from FileTime not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/group_membership.go000066400000000000000000000035731362537225700261670ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "gopkg.in/jcmturner/rpc.v0/ndr" ) // GroupMembership implements https://msdn.microsoft.com/en-us/library/cc237945.aspx // RelativeID : A 32-bit unsigned integer that contains the RID of a particular group. // The possible values for the Attributes flags are identical to those specified in KERB_SID_AND_ATTRIBUTES type GroupMembership struct { RelativeID uint32 Attributes uint32 } // ReadGroupMembership reads a GroupMembership from the bytes slice. func ReadGroupMembership(b *[]byte, p *int, e *binary.ByteOrder) GroupMembership { r := ndr.ReadUint32(b, p, e) a := ndr.ReadUint32(b, p, e) return GroupMembership{ RelativeID: r, Attributes: a, } } // DomainGroupMembership implements https://msdn.microsoft.com/en-us/library/hh536344.aspx // DomainId: A SID structure that contains the SID for the domain.This member is used in conjunction with the GroupIds members to create group SIDs for the device. // GroupCount: A 32-bit unsigned integer that contains the number of groups within the domain to which the account belongs. // GroupIds: A pointer to a list of GROUP_MEMBERSHIP structures that contain the groups to which the account belongs in the domain. The number of groups in this list MUST be equal to GroupCount. type DomainGroupMembership struct { DomainID RPCSID GroupCount uint32 GroupIDs []GroupMembership // Size is value of GroupCount } // ReadDomainGroupMembership reads a DomainGroupMembership from the bytes slice. func ReadDomainGroupMembership(b *[]byte, p *int, e *binary.ByteOrder) (DomainGroupMembership, error) { d, err := ReadRPCSID(b, p, e) if err != nil { return DomainGroupMembership{}, err } c := ndr.ReadUint32(b, p, e) g := make([]GroupMembership, c, c) for i := range g { g[i] = ReadGroupMembership(b, p, e) } return DomainGroupMembership{ DomainID: d, GroupCount: c, GroupIDs: g, }, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/kerb_sid_and_attributes.go000066400000000000000000000023141362537225700274620ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "gopkg.in/jcmturner/rpc.v0/ndr" ) // Attributes of a security group membership and can be combined by using the bitwise OR operation. // They are used by an access check mechanism to specify whether the membership is to be used in an access check decision. const ( SEGroupMandatory = 31 SEGroupEnabledByDefault = 30 SEGroupEnabled = 29 SEGroupOwner = 28 SEGroupResource = 2 //All other bits MUST be set to zero and MUST be ignored on receipt. ) // KerbSidAndAttributes implements https://msdn.microsoft.com/en-us/library/cc237947.aspx type KerbSidAndAttributes struct { SID RPCSID // A pointer to an RPC_SID structure. Attributes uint32 } // ReadKerbSidAndAttributes reads a KerbSidAndAttribute from the bytes slice. func ReadKerbSidAndAttributes(b *[]byte, p *int, e *binary.ByteOrder) (KerbSidAndAttributes, error) { s, err := ReadRPCSID(b, p, e) if err != nil { return KerbSidAndAttributes{}, err } a := ndr.ReadUint32(b, p, e) return KerbSidAndAttributes{ SID: s, Attributes: a, }, nil } // SetFlag sets a flag in a uint32 attribute value. func SetFlag(a *uint32, i uint) { *a = *a | (1 << (31 - i)) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/rpc_unicode_string.go000066400000000000000000000017751362537225700265020ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "gopkg.in/jcmturner/rpc.v0/ndr" ) // RPCUnicodeString implements https://msdn.microsoft.com/en-us/library/cc230365.aspx type RPCUnicodeString struct { Length uint16 MaximumLength uint16 BufferPrt uint32 Value string } // ReadRPCUnicodeString reads a RPCUnicodeString from the bytes slice. func ReadRPCUnicodeString(b *[]byte, p *int, e *binary.ByteOrder) (RPCUnicodeString, error) { l := ndr.ReadUint16(b, p, e) ml := ndr.ReadUint16(b, p, e) if ml < l || l%2 != 0 || ml%2 != 0 { return RPCUnicodeString{}, ndr.Malformed{EText: "Invalid data for RPC_UNICODE_STRING"} } ptr := ndr.ReadUint32(b, p, e) return RPCUnicodeString{ Length: l, MaximumLength: ml, BufferPrt: ptr, }, nil } // UnmarshalString populates a golang string into the RPCUnicodeString struct. func (s *RPCUnicodeString) UnmarshalString(b *[]byte, p *int, e *binary.ByteOrder) (err error) { s.Value, err = ndr.ReadConformantVaryingString(b, p, e) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/sid.go000066400000000000000000000042221362537225700233670ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "encoding/hex" "fmt" "gopkg.in/jcmturner/rpc.v0/ndr" ) // RPCSID implements https://msdn.microsoft.com/en-us/library/cc230364.aspx type RPCSID struct { Revision uint8 SubAuthorityCount uint8 IdentifierAuthority RPCSIDIdentifierAuthority SubAuthority []uint32 } // RPCSIDIdentifierAuthority implements https://msdn.microsoft.com/en-us/library/cc230372.aspx type RPCSIDIdentifierAuthority struct { Value []byte // 6 bytes } // ReadRPCSID reads a RPC_SID from the bytes slice. func ReadRPCSID(b *[]byte, p *int, e *binary.ByteOrder) (RPCSID, error) { size := int(ndr.ReadUint32(b, p, e)) // This is part of the NDR encoding rather than the data type. r := ndr.ReadUint8(b, p) if r != uint8(1) { return RPCSID{}, ndr.Malformed{EText: fmt.Sprintf("SID revision value read as %d when it must be 1", r)} } c := ndr.ReadUint8(b, p) a := ReadRPCSIDIdentifierAuthority(b, p, e) s := make([]uint32, c, c) if size != len(s) { return RPCSID{}, ndr.Malformed{EText: fmt.Sprintf("Number of elements (%d) within SID in the byte stream does not equal the SubAuthorityCount (%d)", size, c)} } for i := 0; i < len(s); i++ { s[i] = ndr.ReadUint32(b, p, e) } return RPCSID{ Revision: r, SubAuthorityCount: c, IdentifierAuthority: a, SubAuthority: s, }, nil } // ReadRPCSIDIdentifierAuthority reads a RPC_SIDIdentifierAuthority from the bytes slice. func ReadRPCSIDIdentifierAuthority(b *[]byte, p *int, e *binary.ByteOrder) RPCSIDIdentifierAuthority { return RPCSIDIdentifierAuthority{ Value: ndr.ReadBytes(b, p, 6, e), } } // ToString returns the string representation of the RPC_SID. func (s *RPCSID) ToString() string { var str string b := append(make([]byte, 2, 2), s.IdentifierAuthority.Value...) // For a strange reason this is read big endian: https://msdn.microsoft.com/en-us/library/dd302645.aspx i := binary.BigEndian.Uint64(b) if i >= 4294967296 { str = fmt.Sprintf("S-1-0x%s", hex.EncodeToString(s.IdentifierAuthority.Value)) } else { str = fmt.Sprintf("S-1-%d", i) } for _, sub := range s.SubAuthority { str = fmt.Sprintf("%s-%d", str, sub) } return str } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/mstypes/user_session_key.go000066400000000000000000000012651362537225700262050ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "gopkg.in/jcmturner/rpc.v0/ndr" ) // CypherBlock implements https://msdn.microsoft.com/en-us/library/cc237040.aspx type CypherBlock struct { Data []byte // size = 8 } // UserSessionKey implements https://msdn.microsoft.com/en-us/library/cc237080.aspx type UserSessionKey struct { Data []CypherBlock // size = 2 } // ReadUserSessionKey reads a UserSessionKey from the bytes slice. func ReadUserSessionKey(b *[]byte, p *int, e *binary.ByteOrder) UserSessionKey { cb1 := CypherBlock{ Data: ndr.ReadBytes(b, p, 8, e), } cb2 := CypherBlock{ Data: ndr.ReadBytes(b, p, 8, e), } return UserSessionKey{ Data: []CypherBlock{cb1, cb2}, } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/000077500000000000000000000000001362537225700213405ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/error.go000066400000000000000000000004621362537225700230220ustar00rootroot00000000000000package ndr import "fmt" // Malformed implements the error interface for malformed NDR encoding errors. type Malformed struct { EText string } // Error implements the error interface on the Malformed struct. func (e Malformed) Error() string { return fmt.Sprintf("Malformed NDR steam: %s", e.EText) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/ndr/ndr.go000066400000000000000000000145021362537225700224540ustar00rootroot00000000000000// Package ndr is DEPRECATED and will be removed from next major revision of gokrb5. Please use gopkg.in/jcmturner/rpc.vX instead. This package is a partial implementation of NDR encoding: http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm package ndr import ( "bytes" "encoding/binary" "fmt" "math" ) const ( protocolVersion = 1 commonHeaderBytes = 8 privateHeaderBytes = 8 bigEndian = 0 littleEndian = 1 ascii = 0 ebcdic = 1 ieee = 0 vax = 1 cray = 2 ibm = 3 ) // CommonHeader implements the NDR common header: https://msdn.microsoft.com/en-us/library/cc243889.aspx type CommonHeader struct { Version uint8 Endianness binary.ByteOrder CharacterEncoding uint8 //FloatRepresentation uint8 HeaderLength uint16 Filler []byte } // PrivateHeader implements the NDR private header: https://msdn.microsoft.com/en-us/library/cc243919.aspx type PrivateHeader struct { ObjectBufferLength uint32 Filler []byte } // ReadHeaders processes the bytes to return the NDR Common and Private headers. func ReadHeaders(b *[]byte) (CommonHeader, PrivateHeader, int, error) { ch, p, err := GetCommonHeader(b) if err != nil { return CommonHeader{}, PrivateHeader{}, 0, err } ph, err := GetPrivateHeader(b, &p, &ch.Endianness) if err != nil { return CommonHeader{}, PrivateHeader{}, 0, err } return ch, ph, p, err } // GetCommonHeader processes the bytes to return the NDR Common header. func GetCommonHeader(b *[]byte) (CommonHeader, int, error) { //The first 8 bytes comprise the Common RPC Header for type marshalling. if len(*b) < commonHeaderBytes { return CommonHeader{}, 0, Malformed{EText: "Not enough bytes."} } if (*b)[0] != protocolVersion { return CommonHeader{}, 0, Malformed{EText: fmt.Sprintf("Stream does not indicate a RPC Type serialization of version %v", protocolVersion)} } endian := int((*b)[1] >> 4 & 0xF) if endian != 0 && endian != 1 { return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid endianness"} } charEncoding := uint8((*b)[1] & 0xF) if charEncoding != 0 && charEncoding != 1 { return CommonHeader{}, 1, Malformed{EText: "Common header does not indicate a valid charater encoding"} } var bo binary.ByteOrder switch endian { case littleEndian: bo = binary.LittleEndian case bigEndian: bo = binary.BigEndian } l := bo.Uint16((*b)[2:4]) if l != commonHeaderBytes { return CommonHeader{}, 4, Malformed{EText: fmt.Sprintf("Common header does not indicate a valid length: %v instead of %v", uint8((*b)[3]), commonHeaderBytes)} } return CommonHeader{ Version: uint8((*b)[0]), Endianness: bo, CharacterEncoding: charEncoding, //FloatRepresentation: uint8(b[2]), HeaderLength: l, Filler: (*b)[4:8], }, 8, nil } // GetPrivateHeader processes the bytes to return the NDR Private header. func GetPrivateHeader(b *[]byte, p *int, bo *binary.ByteOrder) (PrivateHeader, error) { //The next 8 bytes comprise the RPC type marshalling private header for constructed types. if len(*b) < (privateHeaderBytes) { return PrivateHeader{}, Malformed{EText: "Not enough bytes."} } var l uint32 buf := bytes.NewBuffer((*b)[*p : *p+4]) binary.Read(buf, *bo, &l) if l%8 != 0 { return PrivateHeader{}, Malformed{EText: "Object buffer length not a multiple of 8"} } *p += 8 return PrivateHeader{ ObjectBufferLength: l, Filler: (*b)[4:8], }, nil } // ReadUint8 reads bytes representing a thirty two bit integer. func ReadUint8(b *[]byte, p *int) (i uint8) { if len((*b)[*p:]) < 1 { return } ensureAlignment(p, 1) i = uint8((*b)[*p]) *p++ return } // ReadUint16 reads bytes representing a thirty two bit integer. func ReadUint16(b *[]byte, p *int, e *binary.ByteOrder) (i uint16) { if len((*b)[*p:]) < 2 { return } ensureAlignment(p, 2) i = (*e).Uint16((*b)[*p : *p+2]) *p += 2 return } // ReadUint32 reads bytes representing a thirty two bit integer. func ReadUint32(b *[]byte, p *int, e *binary.ByteOrder) (i uint32) { if len((*b)[*p:]) < 4 { return } ensureAlignment(p, 4) i = (*e).Uint32((*b)[*p : *p+4]) *p += 4 return } // ReadUint64 reads bytes representing a thirty two bit integer. func ReadUint64(b *[]byte, p *int, e *binary.ByteOrder) (i uint64) { if len((*b)[*p:]) < 8 { return } ensureAlignment(p, 8) i = (*e).Uint64((*b)[*p : *p+8]) *p += 8 return } // ReadBytes reads the number of bytes specified. func ReadBytes(b *[]byte, p *int, s int, e *binary.ByteOrder) (r []byte) { if len((*b)[*p:]) < s { return } buf := bytes.NewBuffer((*b)[*p : *p+s]) r = make([]byte, s) binary.Read(buf, *e, &r) *p += s return r } // ReadBool reads bytes representing a boolean. func ReadBool(b *[]byte, p *int) bool { if len((*b)[*p:]) < 1 { return false } if ReadUint8(b, p) != 0 { return true } return false } // ReadIEEEfloat32 reads bytes representing a IEEE formatted 32 bit float. func ReadIEEEfloat32(b *[]byte, p *int, e *binary.ByteOrder) float32 { ensureAlignment(p, 4) return math.Float32frombits(ReadUint32(b, p, e)) } // ReadIEEEfloat64 reads bytes representing a IEEE formatted 64 bit float. func ReadIEEEfloat64(b *[]byte, p *int, e *binary.ByteOrder) float64 { ensureAlignment(p, 8) return math.Float64frombits(ReadUint64(b, p, e)) } // ReadConformantVaryingString reads a Conformant and Varying String from the bytes slice. func ReadConformantVaryingString(b *[]byte, p *int, e *binary.ByteOrder) (string, error) { m := ReadUint32(b, p, e) // Max element count o := ReadUint32(b, p, e) // Offset a := ReadUint32(b, p, e) // Actual count if a > (m-o) || o > m { return "", Malformed{EText: fmt.Sprintf("Not enough bytes. Max: %d, Offset: %d, Actual: %d", m, o, a)} } //Unicode string so each element is 2 bytes //move position based on the offset if o > 0 { *p += int(o * 2) } s := make([]rune, a, a) for i := 0; i < len(s); i++ { s[i] = rune(ReadUint16(b, p, e)) } ensureAlignment(p, 4) return string(s), nil } // ReadUniDimensionalConformantArrayHeader reads a UniDimensionalConformantArrayHeader from the bytes slice. func ReadUniDimensionalConformantArrayHeader(b *[]byte, p *int, e *binary.ByteOrder) int { return int(ReadUint32(b, p, e)) } func ensureAlignment(p *int, byteSize int) { if byteSize > 0 { if s := *p % byteSize; s != 0 { *p += byteSize - s } } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/000077500000000000000000000000001362537225700213205ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_claims.go000066400000000000000000000017541362537225700244640ustar00rootroot00000000000000package pac import ( "fmt" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // Claims reference: https://msdn.microsoft.com/en-us/library/hh553895.aspx // ClientClaimsInfo implements https://msdn.microsoft.com/en-us/library/hh536365.aspx type ClientClaimsInfo struct { Claims mstypes.ClaimsSetMetadata } // Unmarshal bytes into the ClientClaimsInfo struct func (k *ClientClaimsInfo) Unmarshal(b []byte) error { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { return fmt.Errorf("error parsing byte stream headers of CLIENT_CLAIMS_INFO: %v", err) } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 k.Claims, err = mstypes.ReadClaimsSetMetadata(&b, &p, e) if err != nil { return err } //Check that there is only zero padding left if len(b) >= p { for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_claims_test.go000066400000000000000000000202651362537225700255210ustar00rootroot00000000000000package pac import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) const ( ClaimsEntryIDStr = "ad://ext/sAMAccountName:88d5d9085ea5c0c0" ClaimsEntryValueStr = "testuser1" ClaimsEntryIDInt64 = "ad://ext/msDS-SupportedE:88d5dea8f1af5f19" ClaimsEntryValueInt64 int64 = 28 ClaimsEntryIDUInt64 = "ad://ext/objectClass:88d5de791e7b27e6" ) func TestPAC_ClientClaimsInfoStr_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoStr"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(3), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []string{ClaimsEntryValueStr}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected") } func TestPAC_ClientClaimsMultiValueUint_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMultiUint"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, mstypes.ClaimTypeIDUInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeUInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDUInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []uint64{655369, 65543, 65542, 65536}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeUInt64.Value, "claims value not as expected") assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected") } func TestPAC_ClientClaimsInt_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoInt"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, mstypes.ClaimTypeIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{ClaimsEntryValueInt64}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected") } func TestPAC_ClientClaimsMultiValueStr_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMultiStr"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, mstypes.ClaimTypeIDString, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, "ad://ext/otherIpPhone:88d5de9f6b4af985", k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []string{"str1", "str2", "str3", "str4"}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected") } func TestPAC_ClientClaimsInfoMultiEntry_Unmarshal(t *testing.T) { // Has an int and a str claim type t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfoMulti"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, mstypes.ClaimsSourceTypeAD, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(2), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{int64(28)}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, uint16(3), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].ID, "claims entry ID not as expected") assert.Equal(t, []string{ClaimsEntryValueStr}, k.Claims.ClaimsSet.ClaimsArrays[0].ClaimsEntries[1].TypeString.Value, "claims value not as expected") assert.Equal(t, mstypes.CompressionFormatNone, k.Claims.CompressionFormat, "compression format not as expected") } func TestPAC_ClientClaimsInfo_Unmarshal_UnsupportedCompression(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_ClientClaimsInfo_XPRESS_HUFF"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientClaimsInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, mstypes.CompressionFormatXPressHuff, k.Claims.CompressionFormat, "compression format not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_info.go000066400000000000000000000017511362537225700241440ustar00rootroot00000000000000package pac import ( "encoding/binary" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // ClientInfo implements https://msdn.microsoft.com/en-us/library/cc237951.aspx type ClientInfo struct { ClientID mstypes.FileTime NameLength uint16 Name string } // Unmarshal bytes into the ClientInfo struct func (k *ClientInfo) Unmarshal(b []byte) error { //The PAC_CLIENT_INFO structure is a simple structure that is not NDR-encoded. var p int var e binary.ByteOrder = binary.LittleEndian k.ClientID = mstypes.ReadFileTime(&b, &p, &e) k.NameLength = ndr.ReadUint16(&b, &p, &e) if len(b[p:]) < int(k.NameLength) { return ndr.Malformed{EText: "PAC ClientInfo length truncated"} } k.Name = ndr.ReadUTF16String(int(k.NameLength), &b, &p, &e) //Check that there is only zero padding left if len(b) >= p { for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/client_info_test.go000066400000000000000000000013371362537225700252030ustar00rootroot00000000000000package pac import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestPAC_ClientInfo_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_Client_Info"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k ClientInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 000000000, time.UTC), k.ClientID.Time(), "Client ID time not as expected.") assert.Equal(t, uint16(18), k.NameLength, "Client name length not as expected") assert.Equal(t, "testuser1", k.Name, "Client name not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/credentials_info.go000066400000000000000000000074241362537225700251660ustar00rootroot00000000000000package pac import ( "encoding/binary" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/gokrb5.v5/types" "gopkg.in/jcmturner/rpc.v0/ndr" ) // https://msdn.microsoft.com/en-us/library/cc237931.aspx // CredentialsInfo implements https://msdn.microsoft.com/en-us/library/cc237953.aspx type CredentialsInfo struct { Version uint32 EType uint32 PACCredentialDataEncrypted []byte PACCredentialData CredentialData } // Unmarshal bytes into the CredentialsInfo struct func (c *CredentialsInfo) Unmarshal(b []byte, k types.EncryptionKey) error { //The CredentialsInfo structure is a simple structure that is not NDR-encoded. var p int var e binary.ByteOrder = binary.LittleEndian c.Version = ndr.ReadUint32(&b, &p, &e) if c.Version != 0 { return errors.New("credentials info version is not zero") } c.EType = ndr.ReadUint32(&b, &p, &e) c.PACCredentialDataEncrypted = ndr.ReadBytes(&b, &p, len(b)-p, &e) err := c.DecryptEncPart(k, &e) if err != nil { return fmt.Errorf("error decrypting PAC Credentials Data: %v", err) } return nil } // DecryptEncPart decrypts the encrypted part of the CredentialsInfo. func (c *CredentialsInfo) DecryptEncPart(k types.EncryptionKey, e *binary.ByteOrder) error { if k.KeyType != int32(c.EType) { return fmt.Errorf("key provided is not the correct type. Type needed: %d, type provided: %d", c.EType, k.KeyType) } pt, err := crypto.DecryptMessage(c.PACCredentialDataEncrypted, k, keyusage.KERB_NON_KERB_SALT) if err != nil { return err } var p int c.PACCredentialData = ReadPACCredentialData(&pt, &p, e) return nil } // CredentialData implements https://msdn.microsoft.com/en-us/library/cc237952.aspx type CredentialData struct { CredentialCount uint32 Credentials []SECPKGSupplementalCred // Size is the value of CredentialCount } // ReadPACCredentialData reads a CredentialData from the byte slice. func ReadPACCredentialData(b *[]byte, p *int, e *binary.ByteOrder) CredentialData { c := ndr.ReadUint32(b, p, e) cr := make([]SECPKGSupplementalCred, c, c) for i := range cr { cr[i] = ReadSECPKGSupplementalCred(b, p, e) } return CredentialData{ CredentialCount: c, Credentials: cr, } } // SECPKGSupplementalCred implements https://msdn.microsoft.com/en-us/library/cc237956.aspx type SECPKGSupplementalCred struct { PackageName mstypes.RPCUnicodeString CredentialSize uint32 Credentials []uint8 // Is a ptr. Size is the value of CredentialSize } // ReadSECPKGSupplementalCred reads a SECPKGSupplementalCred from the byte slice. func ReadSECPKGSupplementalCred(b *[]byte, p *int, e *binary.ByteOrder) SECPKGSupplementalCred { n, _ := mstypes.ReadRPCUnicodeString(b, p, e) cs := ndr.ReadUint32(b, p, e) c := make([]uint8, cs, cs) for i := range c { c[i] = ndr.ReadUint8(b, p) } return SECPKGSupplementalCred{ PackageName: n, CredentialSize: cs, Credentials: c, } } // NTLMSupplementalCred implements https://msdn.microsoft.com/en-us/library/cc237949.aspx type NTLMSupplementalCred struct { Version uint32 Flags uint32 LMPassword []byte NTPassword []byte } // ReadNTLMSupplementalCred reads a NTLMSupplementalCred from the byte slice. func ReadNTLMSupplementalCred(b *[]byte, p *int, e *binary.ByteOrder) NTLMSupplementalCred { v := ndr.ReadUint32(b, p, e) f := ndr.ReadUint32(b, p, e) l := ndr.ReadBytes(b, p, 16, e) n := ndr.ReadBytes(b, p, 16, e) return NTLMSupplementalCred{ Version: v, Flags: f, LMPassword: l, NTPassword: n, } } const ( // NTLMSupCredLMOWF indicates that the LM OWF member is present and valid. NTLMSupCredLMOWF = 31 // NTLMSupCredNTOWF indicates that the NT OWF member is present and valid. NTLMSupCredNTOWF = 30 ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/device_claims.go000066400000000000000000000016371362537225700244450ustar00rootroot00000000000000package pac import ( "fmt" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // DeviceClaimsInfo implements https://msdn.microsoft.com/en-us/library/hh554226.aspx type DeviceClaimsInfo struct { Claims mstypes.ClaimsSetMetadata } // Unmarshal bytes into the DeviceClaimsInfo struct func (k *DeviceClaimsInfo) Unmarshal(b []byte) error { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { return fmt.Errorf("error parsing byte stream headers of DEVICE_CLAIMS_INFO: %v", err) } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 k.Claims, err = mstypes.ReadClaimsSetMetadata(&b, &p, e) if err != nil { return err } //Check that there is only zero padding left if len(b) >= p { for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/device_info.go000066400000000000000000000051441362537225700241250ustar00rootroot00000000000000package pac import ( "fmt" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // DeviceInfo implements https://msdn.microsoft.com/en-us/library/hh536402.aspx type DeviceInfo struct { UserID uint32 PrimaryGroupID uint32 AccountDomainID mstypes.RPCSID AccountGroupCount uint32 AccountGroupIDs []mstypes.GroupMembership SIDCount uint32 ExtraSIDs []mstypes.KerbSidAndAttributes DomainGroupCount uint32 DomainGroup []mstypes.DomainGroupMembership } // Unmarshal bytes into the DeviceInfo struct func (k *DeviceInfo) Unmarshal(b []byte) error { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { return fmt.Errorf("error parsing byte stream headers: %v", err) } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 k.UserID = ndr.ReadUint32(&b, &p, e) k.PrimaryGroupID = ndr.ReadUint32(&b, &p, e) k.AccountDomainID, err = mstypes.ReadRPCSID(&b, &p, e) if err != nil { return err } k.AccountGroupCount = ndr.ReadUint32(&b, &p, e) if k.AccountGroupCount > 0 { ag := make([]mstypes.GroupMembership, k.AccountGroupCount, k.AccountGroupCount) for i := range ag { ag[i] = mstypes.ReadGroupMembership(&b, &p, e) } k.AccountGroupIDs = ag } k.SIDCount = ndr.ReadUint32(&b, &p, e) var ah ndr.ConformantArrayHeader if k.SIDCount > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if ah.MaxCount != int(k.SIDCount) { return fmt.Errorf("error with size of ExtraSIDs list. expected: %d, Actual: %d", k.SIDCount, ah.MaxCount) } es := make([]mstypes.KerbSidAndAttributes, k.SIDCount, k.SIDCount) attr := make([]uint32, k.SIDCount, k.SIDCount) ptr := make([]uint32, k.SIDCount, k.SIDCount) for i := range attr { ptr[i] = ndr.ReadUint32(&b, &p, e) attr[i] = ndr.ReadUint32(&b, &p, e) } for i := range es { if ptr[i] != 0 { s, err := mstypes.ReadRPCSID(&b, &p, e) es[i] = mstypes.KerbSidAndAttributes{SID: s, Attributes: attr[i]} if err != nil { return ndr.Malformed{EText: fmt.Sprintf("could not read ExtraSIDs: %v", err)} } } } k.ExtraSIDs = es } k.DomainGroupCount = ndr.ReadUint32(&b, &p, e) if k.DomainGroupCount > 0 { dg := make([]mstypes.DomainGroupMembership, k.DomainGroupCount, k.DomainGroupCount) for i := range dg { dg[i], _ = mstypes.ReadDomainGroupMembership(&b, &p, e) } k.DomainGroup = dg } //Check that there is only zero padding left if len(b) >= p { for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/kerb_validation_info.go000066400000000000000000000232011362537225700260150ustar00rootroot00000000000000// Package pac implements Microsoft Privilege Attribute Certificate (PAC) processing. package pac import ( "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // KERB_VALIDATION_INFO flags. const ( USERFLAG_GUEST = 31 // Authentication was done via the GUEST account; no password was used. USERFLAG_NO_ENCRYPTION_AVAILABLE = 30 // No encryption is available. USERFLAG_LAN_MANAGER_KEY = 28 // LAN Manager key was used for authentication. USERFLAG_SUB_AUTH = 25 // Sub-authentication used; session key came from the sub-authentication package. USERFLAG_EXTRA_SIDS = 26 // Indicates that the ExtraSids field is populated and contains additional SIDs. USERFLAG_MACHINE_ACCOUNT = 24 // Indicates that the account is a machine account. USERFLAG_DC_NTLM2 = 23 // Indicates that the domain controller understands NTLMv2. USERFLAG_RESOURCE_GROUPIDS = 22 // Indicates that the ResourceGroupIds field is populated. USERFLAG_PROFILEPATH = 21 // Indicates that ProfilePath is populated. USERFLAG_NTLM2_NTCHALLENGERESP = 20 // The NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation. USERFLAG_LM2_LMCHALLENGERESP = 19 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and session key generation. USERFLAG_AUTH_LMCHALLENGERESP_KEY_NTCHALLENGERESP = 18 // The LMv2 response from the LmChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used for authentication and the NTLMv2 response from the NtChallengeResponseFields ([MS-NLMP] section 2.2.1.3) was used session key generation. ) // KerbValidationInfo implement https://msdn.microsoft.com/en-us/library/cc237948.aspx type KerbValidationInfo struct { LogOnTime mstypes.FileTime LogOffTime mstypes.FileTime KickOffTime mstypes.FileTime PasswordLastSet mstypes.FileTime PasswordCanChange mstypes.FileTime PasswordMustChange mstypes.FileTime EffectiveName mstypes.RPCUnicodeString FullName mstypes.RPCUnicodeString LogonScript mstypes.RPCUnicodeString ProfilePath mstypes.RPCUnicodeString HomeDirectory mstypes.RPCUnicodeString HomeDirectoryDrive mstypes.RPCUnicodeString LogonCount uint16 BadPasswordCount uint16 UserID uint32 PrimaryGroupID uint32 GroupCount uint32 pGroupIDs uint32 GroupIDs []mstypes.GroupMembership UserFlags uint32 UserSessionKey mstypes.UserSessionKey LogonServer mstypes.RPCUnicodeString LogonDomainName mstypes.RPCUnicodeString pLogonDomainID uint32 LogonDomainID mstypes.RPCSID Reserved1 []uint32 // Has 2 elements UserAccountControl uint32 SubAuthStatus uint32 LastSuccessfulILogon mstypes.FileTime LastFailedILogon mstypes.FileTime FailedILogonCount uint32 Reserved3 uint32 SIDCount uint32 pExtraSIDs uint32 ExtraSIDs []mstypes.KerbSidAndAttributes pResourceGroupDomainSID uint32 ResourceGroupDomainSID mstypes.RPCSID ResourceGroupCount uint32 pResourceGroupIDs uint32 ResourceGroupIDs []mstypes.GroupMembership } // Unmarshal bytes into the DeviceInfo struct func (k *KerbValidationInfo) Unmarshal(b []byte) (err error) { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { return fmt.Errorf("error parsing byte stream headers: %v", err) } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 k.LogOnTime = mstypes.ReadFileTime(&b, &p, e) k.LogOffTime = mstypes.ReadFileTime(&b, &p, e) k.KickOffTime = mstypes.ReadFileTime(&b, &p, e) k.PasswordLastSet = mstypes.ReadFileTime(&b, &p, e) k.PasswordCanChange = mstypes.ReadFileTime(&b, &p, e) k.PasswordMustChange = mstypes.ReadFileTime(&b, &p, e) if k.EffectiveName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.FullName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.LogonScript, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.ProfilePath, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.HomeDirectory, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.HomeDirectoryDrive, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } k.LogonCount = ndr.ReadUint16(&b, &p, e) k.BadPasswordCount = ndr.ReadUint16(&b, &p, e) k.UserID = ndr.ReadUint32(&b, &p, e) k.PrimaryGroupID = ndr.ReadUint32(&b, &p, e) k.GroupCount = ndr.ReadUint32(&b, &p, e) k.pGroupIDs = ndr.ReadUint32(&b, &p, e) k.UserFlags = ndr.ReadUint32(&b, &p, e) k.UserSessionKey = mstypes.ReadUserSessionKey(&b, &p, e) if k.LogonServer, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } if k.LogonDomainName, err = mstypes.ReadRPCUnicodeString(&b, &p, e); err != nil { return } k.pLogonDomainID = ndr.ReadUint32(&b, &p, e) k.Reserved1 = []uint32{ ndr.ReadUint32(&b, &p, e), ndr.ReadUint32(&b, &p, e), } k.UserAccountControl = ndr.ReadUint32(&b, &p, e) k.SubAuthStatus = ndr.ReadUint32(&b, &p, e) k.LastSuccessfulILogon = mstypes.ReadFileTime(&b, &p, e) k.LastFailedILogon = mstypes.ReadFileTime(&b, &p, e) k.FailedILogonCount = ndr.ReadUint32(&b, &p, e) k.Reserved3 = ndr.ReadUint32(&b, &p, e) k.SIDCount = ndr.ReadUint32(&b, &p, e) k.pExtraSIDs = ndr.ReadUint32(&b, &p, e) k.pResourceGroupDomainSID = ndr.ReadUint32(&b, &p, e) k.ResourceGroupCount = ndr.ReadUint32(&b, &p, e) k.pResourceGroupIDs = ndr.ReadUint32(&b, &p, e) // Populate pointers if err = k.EffectiveName.UnmarshalString(&b, &p, e); err != nil { return } if err = k.FullName.UnmarshalString(&b, &p, e); err != nil { return } if err = k.LogonScript.UnmarshalString(&b, &p, e); err != nil { return } if err = k.ProfilePath.UnmarshalString(&b, &p, e); err != nil { return } if err = k.HomeDirectory.UnmarshalString(&b, &p, e); err != nil { return } if err = k.HomeDirectoryDrive.UnmarshalString(&b, &p, e); err != nil { return } var ah ndr.ConformantArrayHeader if k.GroupCount > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if err != nil { return } if ah.MaxCount != int(k.GroupCount) { err = errors.New("error with size of group list") return } g := make([]mstypes.GroupMembership, k.GroupCount, k.GroupCount) for i := range g { g[i] = mstypes.ReadGroupMembership(&b, &p, e) } k.GroupIDs = g } if err = k.LogonServer.UnmarshalString(&b, &p, e); err != nil { return } if err = k.LogonDomainName.UnmarshalString(&b, &p, e); err != nil { return } if k.pLogonDomainID != 0 { k.LogonDomainID, err = mstypes.ReadRPCSID(&b, &p, e) if err != nil { return fmt.Errorf("error reading LogonDomainID: %v", err) } } if k.SIDCount > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if err != nil { return } if ah.MaxCount != int(k.SIDCount) { return fmt.Errorf("error with size of ExtraSIDs list. Expected: %d, Actual: %d", k.SIDCount, ah.MaxCount) } es := make([]mstypes.KerbSidAndAttributes, k.SIDCount, k.SIDCount) attr := make([]uint32, k.SIDCount, k.SIDCount) ptr := make([]uint32, k.SIDCount, k.SIDCount) for i := range attr { ptr[i] = ndr.ReadUint32(&b, &p, e) attr[i] = ndr.ReadUint32(&b, &p, e) } for i := range es { if ptr[i] != 0 { s, err := mstypes.ReadRPCSID(&b, &p, e) es[i] = mstypes.KerbSidAndAttributes{SID: s, Attributes: attr[i]} if err != nil { return ndr.Malformed{EText: fmt.Sprintf("could not read ExtraSIDs: %v", err)} } } } k.ExtraSIDs = es } if k.pResourceGroupDomainSID != 0 { k.ResourceGroupDomainSID, err = mstypes.ReadRPCSID(&b, &p, e) if err != nil { return err } } if k.ResourceGroupCount > 0 { ah, err = ndr.ReadUniDimensionalConformantArrayHeader(&b, &p, e) if err != nil { return } if ah.MaxCount != int(k.ResourceGroupCount) { return fmt.Errorf("error with size of ResourceGroup list. Expected: %d, Actual: %d", k.ResourceGroupCount, ah.MaxCount) } g := make([]mstypes.GroupMembership, k.ResourceGroupCount, k.ResourceGroupCount) for i := range g { g[i] = mstypes.ReadGroupMembership(&b, &p, e) } k.ResourceGroupIDs = g } //Check that there is only zero padding left if len(b) >= p { for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } } return nil } // GetGroupMembershipSIDs returns a slice of strings containing the group membership SIDs found in the PAC. func (k *KerbValidationInfo) GetGroupMembershipSIDs() []string { var g []string lSID := k.LogonDomainID.ToString() for i := range k.GroupIDs { g = append(g, fmt.Sprintf("%s-%d", lSID, k.GroupIDs[i].RelativeID)) } for _, s := range k.ExtraSIDs { var exists = false for _, es := range g { if es == s.SID.ToString() { exists = true break } } if !exists { g = append(g, s.SID.ToString()) } } for _, r := range k.ResourceGroupIDs { var exists = false s := fmt.Sprintf("%s-%d", k.ResourceGroupDomainSID.ToString(), r.RelativeID) for _, es := range g { if es == s { exists = true break } } if !exists { g = append(g, s) } } return g } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/kerb_validation_info_test.go000066400000000000000000000401111362537225700270530ustar00rootroot00000000000000package pac import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestKerbValidationInfo_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info_MS"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k KerbValidationInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling KerbValidationInfo: %v", err) } assert.Equal(t, time.Date(2006, 4, 28, 1, 42, 50, 925640100, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2006, 3, 18, 10, 44, 54, 837147900, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2006, 3, 19, 10, 44, 54, 837147900, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "lzhu", k.EffectiveName.Value, "EffectiveName not as expected") assert.Equal(t, "Liqiang(Larry) Zhu", k.FullName.Value, "EffectiveName not as expected") assert.Equal(t, "ntds2.bat", k.LogonScript.Value, "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.Value, "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.Value, "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.Value, "EffectiveName not as expected") assert.Equal(t, uint32(131088), k.ProfilePath.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131092), k.HomeDirectory.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131096), k.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint16(4180), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(2914711), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(26), k.GroupCount, "GroupCount not as expected") assert.Equal(t, uint32(131100), k.pGroupIDs, "pGroupIDs not as expected") gids := []mstypes.GroupMembership{ {RelativeID: 3392609, Attributes: 7}, {RelativeID: 2999049, Attributes: 7}, {RelativeID: 3322974, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 2931095, Attributes: 7}, {RelativeID: 3338539, Attributes: 7}, {RelativeID: 3354830, Attributes: 7}, {RelativeID: 3026599, Attributes: 7}, {RelativeID: 3338538, Attributes: 7}, {RelativeID: 2931096, Attributes: 7}, {RelativeID: 3392610, Attributes: 7}, {RelativeID: 3342740, Attributes: 7}, {RelativeID: 3392630, Attributes: 7}, {RelativeID: 3014318, Attributes: 7}, {RelativeID: 2937394, Attributes: 7}, {RelativeID: 3278870, Attributes: 7}, {RelativeID: 3038018, Attributes: 7}, {RelativeID: 3322975, Attributes: 7}, {RelativeID: 3513546, Attributes: 7}, {RelativeID: 2966661, Attributes: 7}, {RelativeID: 3338434, Attributes: 7}, {RelativeID: 3271401, Attributes: 7}, {RelativeID: 3051245, Attributes: 7}, {RelativeID: 3271606, Attributes: 7}, {RelativeID: 3026603, Attributes: 7}, {RelativeID: 3018354, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "NTDEV-DC-05", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "NTDEV", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, uint32(131112), k.pLogonDomainID, "pLogonDomainID not as expected") assert.Equal(t, "S-1-5-21-397955417-626881126-188441444", k.LogonDomainID.ToString(), "LogonDomainID not as expected") assert.Equal(t, uint32(16), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(13), k.SIDCount, "SIDCount not as expected") assert.Equal(t, uint32(131116), k.pExtraSIDs, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es = []struct { sid string attr uint32 }{ {"S-1-5-21-773533881-1816936887-355810188-513", uint32(7)}, {"S-1-5-21-397955417-626881126-188441444-3101812", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291368", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291341", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3322973", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3479105", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3271400", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3283393", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3338537", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3038991", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3037999", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3248111", uint32(536870919)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint32(0), k.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected") assert.Equal(t, uint8(0), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, uint32(0), k.pResourceGroupIDs, "pResourceGroupIDs not as expected") assert.Equal(t, 0, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") b, err = hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k2 KerbValidationInfo err = k2.Unmarshal(b) if err != nil { t.Fatal("Could not unmarshal KerbValidationInfo") } assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 825766900, time.UTC), k2.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 5, 6, 7, 23, 8, 968750000, time.UTC), k2.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 5, 7, 7, 23, 8, 968750000, time.UTC), k2.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k2.EffectiveName.Value, "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k2.FullName.Value, "EffectiveName not as expected") assert.Equal(t, "", k2.LogonScript.Value, "EffectiveName not as expected") assert.Equal(t, "", k2.ProfilePath.Value, "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectory.Value, "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectoryDrive.Value, "EffectiveName not as expected") assert.Equal(t, uint32(131088), k2.ProfilePath.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131092), k2.HomeDirectory.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131096), k2.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint16(216), k2.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k2.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1105), k2.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k2.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(5), k2.GroupCount, "GroupCount not as expected") assert.Equal(t, uint32(131100), k2.pGroupIDs, "pGroupIDs not as expected") gids = []mstypes.GroupMembership{ {RelativeID: 513, Attributes: 7}, {RelativeID: 1108, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, {RelativeID: 1115, Attributes: 7}, {RelativeID: 1116, Attributes: 7}, } assert.Equal(t, gids, k2.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k2.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k2.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "ADDC", k2.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "TEST", k2.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, uint32(131112), k2.pLogonDomainID, "pLogonDomainID not as expected") assert.Equal(t, "S-1-5-21-3167651404-3865080224-2280184895", k2.LogonDomainID.ToString(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k2.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k2.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k2.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(2), k2.SIDCount, "SIDCount not as expected") assert.Equal(t, uint32(131116), k2.pExtraSIDs, "SIDCount not as expected") assert.Equal(t, int(k2.SIDCount), len(k2.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es2 = []struct { sid string attr uint32 }{ {"S-1-5-21-3167651404-3865080224-2280184895-1114", uint32(536870919)}, {"S-1-5-21-3167651404-3865080224-2280184895-1111", uint32(536870919)}, } for i, s := range es2 { assert.Equal(t, s.sid, k2.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k2.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint32(0), k2.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected") assert.Equal(t, uint8(0), k2.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, uint32(0), k2.pResourceGroupIDs, "pResourceGroupIDs not as expected") assert.Equal(t, 0, len(k2.ResourceGroupIDs), "ResourceGroupIDs not as expected") } func TestKerbValidationInfo_Unmarshal_DomainTrust(t *testing.T) { b, err := hex.DecodeString(testdata.TestVectors["PAC_Kerb_Validation_Info_Trust"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k KerbValidationInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling KerbValidationInfo: %v", err) } assert.Equal(t, time.Date(2017, 10, 14, 12, 03, 41, 52409900, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 10, 10, 20, 42, 56, 220282300, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 10, 11, 20, 42, 56, 220282300, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k.EffectiveName.Value, "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k.FullName.Value, "EffectiveName not as expected") assert.Equal(t, "", k.LogonScript.Value, "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.Value, "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.Value, "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.Value, "EffectiveName not as expected") assert.Equal(t, uint32(131088), k.ProfilePath.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131092), k.HomeDirectory.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint32(131096), k.HomeDirectoryDrive.BufferPrt, "EffectiveName not as expected") assert.Equal(t, uint16(46), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1106), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(3), k.GroupCount, "GroupCount not as expected") assert.Equal(t, uint32(131100), k.pGroupIDs, "pGroupIDs not as expected") gids := []mstypes.GroupMembership{ {RelativeID: 1110, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(544), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{Data: []mstypes.CypherBlock{{Data: make([]byte, 8, 8)}, {Data: make([]byte, 8, 8)}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "UDC", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "USER", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, uint32(131112), k.pLogonDomainID, "pLogonDomainID not as expected") assert.Equal(t, "S-1-5-21-2284869408-3503417140-1141177250", k.LogonDomainID.ToString(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(1), k.SIDCount, "SIDCount not as expected") assert.Equal(t, uint32(131116), k.pExtraSIDs, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es = []struct { sid string attr uint32 }{ {"S-1-18-1", uint32(7)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.ToString(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint32(131124), k.pResourceGroupDomainSID, "pResourceGroupDomainSID not as expected") assert.Equal(t, uint8(4), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, "S-1-5-21-3062750306-1230139592-1973306805", k.ResourceGroupDomainSID.ToString(), "ResourceGroupDomainSID value not as expected") assert.Equal(t, uint32(131128), k.pResourceGroupIDs, "pResourceGroupIDs not as expected") assert.Equal(t, 2, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") rgids := []mstypes.GroupMembership{ {RelativeID: 1107, Attributes: 536870919}, {RelativeID: 1108, Attributes: 536870919}, } assert.Equal(t, rgids, k.ResourceGroupIDs, "ResourceGroupIDs not as expected") groupSids := []string{"S-1-5-21-2284869408-3503417140-1141177250-1110", "S-1-5-21-2284869408-3503417140-1141177250-513", "S-1-5-21-2284869408-3503417140-1141177250-1109", "S-1-18-1", "S-1-5-21-3062750306-1230139592-1973306805-1107", "S-1-5-21-3062750306-1230139592-1973306805-1108"} assert.Equal(t, groupSids, k.GetGroupMembershipSIDs(), "GroupMembershipSIDs not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_info_buffer.go000066400000000000000000000016501362537225700247600ustar00rootroot00000000000000package pac import ( "encoding/binary" "gopkg.in/jcmturner/rpc.v0/ndr" ) const ( ulTypeKerbValidationInfo = 1 ulTypeCredentials = 2 ulTypePACServerSignatureData = 6 ulTypePACKDCSignatureData = 7 ulTypePACClientInfo = 10 ulTypeS4UDelegationInfo = 11 ulTypeUPNDNSInfo = 12 ulTypePACClientClaimsInfo = 13 ulTypePACDeviceInfo = 14 ulTypePACDeviceClaimsInfo = 15 ) // InfoBuffer implements the PAC Info Buffer: https://msdn.microsoft.com/en-us/library/cc237954.aspx type InfoBuffer struct { ULType uint32 CBBufferSize uint32 Offset uint64 } // ReadPACInfoBuffer reads a InfoBuffer from the byte slice. func ReadPACInfoBuffer(b *[]byte, p *int, e *binary.ByteOrder) InfoBuffer { u := ndr.ReadUint32(b, p, e) s := ndr.ReadUint32(b, p, e) o := ndr.ReadUint64(b, p, e) return InfoBuffer{ ULType: u, CBBufferSize: s, Offset: o, } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_type.go000066400000000000000000000140601362537225700234540ustar00rootroot00000000000000package pac import ( "encoding/binary" "errors" "fmt" "gopkg.in/jcmturner/gokrb5.v5/crypto" "gopkg.in/jcmturner/gokrb5.v5/iana/keyusage" "gopkg.in/jcmturner/gokrb5.v5/types" "gopkg.in/jcmturner/rpc.v0/ndr" ) // PACType implements: https://msdn.microsoft.com/en-us/library/cc237950.aspx type PACType struct { CBuffers uint32 Version uint32 Buffers []InfoBuffer Data []byte KerbValidationInfo *KerbValidationInfo CredentialsInfo *CredentialsInfo ServerChecksum *SignatureData KDCChecksum *SignatureData ClientInfo *ClientInfo S4UDelegationInfo *S4UDelegationInfo UPNDNSInfo *UPNDNSInfo ClientClaimsInfo *ClientClaimsInfo DeviceInfo *DeviceInfo DeviceClaimsInfo *DeviceClaimsInfo ZeroSigData []byte } // Unmarshal bytes into the PACType struct func (pac *PACType) Unmarshal(b []byte) error { var p int var e binary.ByteOrder = binary.LittleEndian pac.Data = b zb := make([]byte, len(b), len(b)) copy(zb, b) pac.ZeroSigData = zb pac.CBuffers = ndr.ReadUint32(&b, &p, &e) pac.Version = ndr.ReadUint32(&b, &p, &e) buf := make([]InfoBuffer, pac.CBuffers, pac.CBuffers) for i := range buf { buf[i] = ReadPACInfoBuffer(&b, &p, &e) } pac.Buffers = buf return nil } // ProcessPACInfoBuffers processes the PAC Info Buffers. // https://msdn.microsoft.com/en-us/library/cc237954.aspx func (pac *PACType) ProcessPACInfoBuffers(key types.EncryptionKey) error { for _, buf := range pac.Buffers { p := make([]byte, buf.CBBufferSize, buf.CBBufferSize) copy(p, pac.Data[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)]) switch int(buf.ULType) { case ulTypeKerbValidationInfo: if pac.KerbValidationInfo != nil { //Must ignore subsequent buffers of this type continue } var k KerbValidationInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing KerbValidationInfo: %v", err) } pac.KerbValidationInfo = &k case ulTypeCredentials: // Currently PAC parsing is only useful on the service side in gokrb5 // The CredentialsInfo are only useful when gokrb5 has implemented RFC4556 and only applied on the client side. // Skipping CredentialsInfo - will be revisited under RFC4556 implementation. continue //if pac.CredentialsInfo != nil { // //Must ignore subsequent buffers of this type // continue //} //var k CredentialsInfo //err := k.Unmarshal(p, key) // The encryption key used is the AS reply key only available to the client. //if err != nil { // return fmt.Errorf("error processing CredentialsInfo: %v", err) //} //pac.CredentialsInfo = &k case ulTypePACServerSignatureData: if pac.ServerChecksum != nil { //Must ignore subsequent buffers of this type continue } var k SignatureData zb, err := k.Unmarshal(p) copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb) if err != nil { return fmt.Errorf("error processing ServerChecksum: %v", err) } pac.ServerChecksum = &k case ulTypePACKDCSignatureData: if pac.KDCChecksum != nil { //Must ignore subsequent buffers of this type continue } var k SignatureData zb, err := k.Unmarshal(p) copy(pac.ZeroSigData[int(buf.Offset):int(buf.Offset)+int(buf.CBBufferSize)], zb) if err != nil { return fmt.Errorf("error processing KDCChecksum: %v", err) } pac.KDCChecksum = &k case ulTypePACClientInfo: if pac.ClientInfo != nil { //Must ignore subsequent buffers of this type continue } var k ClientInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing ClientInfo: %v", err) } pac.ClientInfo = &k case ulTypeS4UDelegationInfo: if pac.S4UDelegationInfo != nil { //Must ignore subsequent buffers of this type continue } var k S4UDelegationInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing S4U_DelegationInfo: %v", err) } pac.S4UDelegationInfo = &k case ulTypeUPNDNSInfo: if pac.UPNDNSInfo != nil { //Must ignore subsequent buffers of this type continue } var k UPNDNSInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing UPN_DNSInfo: %v", err) } pac.UPNDNSInfo = &k case ulTypePACClientClaimsInfo: if pac.ClientClaimsInfo != nil || len(p) < 1 { //Must ignore subsequent buffers of this type continue } var k ClientClaimsInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing ClientClaimsInfo: %v", err) } pac.ClientClaimsInfo = &k case ulTypePACDeviceInfo: if pac.DeviceInfo != nil { //Must ignore subsequent buffers of this type continue } var k DeviceInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing DeviceInfo: %v", err) } pac.DeviceInfo = &k case ulTypePACDeviceClaimsInfo: if pac.DeviceClaimsInfo != nil { //Must ignore subsequent buffers of this type continue } var k DeviceClaimsInfo err := k.Unmarshal(p) if err != nil { return fmt.Errorf("error processing DeviceClaimsInfo: %v", err) } pac.DeviceClaimsInfo = &k } } if ok, err := pac.validate(key); !ok { return err } return nil } func (pac *PACType) validate(key types.EncryptionKey) (bool, error) { if pac.KerbValidationInfo == nil { return false, errors.New("PAC Info Buffers does not contain a KerbValidationInfo") } if pac.ServerChecksum == nil { return false, errors.New("PAC Info Buffers does not contain a ServerChecksum") } if pac.KDCChecksum == nil { return false, errors.New("PAC Info Buffers does not contain a KDCChecksum") } if pac.ClientInfo == nil { return false, errors.New("PAC Info Buffers does not contain a ClientInfo") } etype, err := crypto.GetChksumEtype(int32(pac.ServerChecksum.SignatureType)) if err != nil { return false, err } if ok := etype.VerifyChecksum(key.KeyValue, pac.ZeroSigData, pac.ServerChecksum.Signature, keyusage.KERB_NON_KERB_CKSUM_SALT); !ok { return false, errors.New("PAC service checksum verification failed") } return true, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/pac_type_test.go000066400000000000000000000030711362537225700245130ustar00rootroot00000000000000package pac import ( "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestPACTypeValidate(t *testing.T) { t.Parallel() v := "PAC_AD_WIN2K_PAC" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } var pac PACType err = pac.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } b, _ = hex.DecodeString(testdata.SYSHTTP_KEYTAB) kt, _ := keytab.Parse(b) key, err := kt.GetEncryptionKey([]string{"sysHTTP"}, "TEST.GOKRB5", 2, 18) if err != nil { t.Fatalf("Error getting key: %v", err) } err = pac.ProcessPACInfoBuffers(key) if err != nil { t.Fatalf("Processing reference pac error: %v", err) } pacInvalidServerSig := pac // Check the signature to force failure pacInvalidServerSig.ServerChecksum.Signature[0] ^= 0xFF pacInvalidNilKerbValidationInfo := pac pacInvalidNilKerbValidationInfo.KerbValidationInfo = nil pacInvalidNilServerSig := pac pacInvalidNilServerSig.ServerChecksum = nil pacInvalidNilKdcSig := pac pacInvalidNilKdcSig.KDCChecksum = nil pacInvalidClientInfo := pac pacInvalidClientInfo.ClientInfo = nil var pacs = []struct { pac PACType }{ {pacInvalidServerSig}, {pacInvalidNilKerbValidationInfo}, {pacInvalidNilServerSig}, {pacInvalidNilKdcSig}, {pacInvalidClientInfo}, } for i, s := range pacs { v, _ := s.pac.validate(key) assert.False(t, v, fmt.Sprintf("Validation should have failed for test %v", i)) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/s4u_delegation_info.go000066400000000000000000000030571362537225700255750ustar00rootroot00000000000000package pac import ( "fmt" "gopkg.in/jcmturner/gokrb5.v5/mstypes" "gopkg.in/jcmturner/rpc.v0/ndr" ) // S4UDelegationInfo implements https://msdn.microsoft.com/en-us/library/cc237944.aspx type S4UDelegationInfo struct { S4U2proxyTarget mstypes.RPCUnicodeString // The name of the principal to whom the application can forward the ticket. TransitedListSize uint32 S4UTransitedServices []mstypes.RPCUnicodeString // List of all services that have been delegated through by this client and subsequent services or servers.. Size is value of TransitedListSize } // Unmarshal bytes into the S4UDelegationInfo struct func (k *S4UDelegationInfo) Unmarshal(b []byte) error { ch, _, p, err := ndr.ReadHeaders(&b) if err != nil { return fmt.Errorf("error parsing byte stream headers: %v", err) } e := &ch.Endianness //The next 4 bytes are an RPC unique pointer referent. We just skip these p += 4 k.S4U2proxyTarget, err = mstypes.ReadRPCUnicodeString(&b, &p, e) if err != nil { return err } k.TransitedListSize = ndr.ReadUint32(&b, &p, e) if k.TransitedListSize > 0 { ts := make([]mstypes.RPCUnicodeString, k.TransitedListSize, k.TransitedListSize) for i := range ts { ts[i], err = mstypes.ReadRPCUnicodeString(&b, &p, e) if err != nil { return err } } for i := range ts { ts[i].UnmarshalString(&b, &p, e) } k.S4UTransitedServices = ts } //Check that there is only zero padding left for _, v := range b[p:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/signature_data.go000066400000000000000000000024011362537225700246360ustar00rootroot00000000000000package pac import ( "encoding/binary" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/rpc.v0/ndr" ) /* https://msdn.microsoft.com/en-us/library/cc237955.aspx */ // SignatureData implements https://msdn.microsoft.com/en-us/library/cc237955.aspx type SignatureData struct { SignatureType uint32 Signature []byte RODCIdentifier uint16 } // Unmarshal bytes into the SignatureData struct func (k *SignatureData) Unmarshal(b []byte) ([]byte, error) { var p int var e binary.ByteOrder = binary.LittleEndian k.SignatureType = ndr.ReadUint32(&b, &p, &e) var c int switch k.SignatureType { case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED: c = 16 case uint32(chksumtype.HMAC_SHA1_96_AES128): c = 12 case uint32(chksumtype.HMAC_SHA1_96_AES256): c = 12 } sp := p k.Signature = ndr.ReadBytes(&b, &p, c, &e) k.RODCIdentifier = ndr.ReadUint16(&b, &p, &e) //Check that there is only zero padding left for _, v := range b[p:] { if v != 0 { return []byte{}, ndr.Malformed{EText: "non-zero padding left over at end of data stream"} } } // Create bytes with zeroed signature needed for checksum verification rb := make([]byte, len(b), len(b)) copy(rb, b) z := make([]byte, len(b), len(b)) copy(rb[sp:sp+c], z) return rb, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/signature_data_test.go000066400000000000000000000034671362537225700257120ustar00rootroot00000000000000package pac import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/chksumtype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestPAC_SignatureData_Unmarshal_Server_Signature(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_Server_Signature"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k SignatureData bz, err := k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } sig, _ := hex.DecodeString("1e251d98d552be7df384f550") zeroed, _ := hex.DecodeString("10000000000000000000000000000000") assert.Equal(t, uint32(chksumtype.HMAC_SHA1_96_AES256), k.SignatureType, "Server signature type not as expected") assert.Equal(t, sig, k.Signature, "Server signature not as expected") assert.Equal(t, uint16(0), k.RODCIdentifier, "RODC Identifier not as expected") assert.Equal(t, zeroed, bz, "Returned bytes with zeroed signature not as expected") } func TestPAC_SignatureData_Unmarshal_KDC_Signature(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_KDC_Signature"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k SignatureData bz, err := k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } sig, _ := hex.DecodeString("340be28b48765d0519ee9346cf53d822") zeroed, _ := hex.DecodeString("76ffffff00000000000000000000000000000000") assert.Equal(t, chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED, k.SignatureType, "Server signature type not as expected") assert.Equal(t, sig, k.Signature, "Server signature not as expected") assert.Equal(t, uint16(0), k.RODCIdentifier, "RODC Identifier not as expected") assert.Equal(t, zeroed, bz, "Returned bytes with zeroed signature not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/upn_dns_info.go000066400000000000000000000032021362537225700243250ustar00rootroot00000000000000package pac import ( "encoding/binary" "sort" "gopkg.in/jcmturner/rpc.v0/ndr" ) // UPNDNSInfo implements https://msdn.microsoft.com/en-us/library/dd240468.aspx type UPNDNSInfo struct { UPNLength uint16 UPNOffset uint16 DNSDomainNameLength uint16 DNSDomainNameOffset uint16 Flags uint32 UPN string DNSDomain string } const ( upnNoUPNAttr = 31 ) // Unmarshal bytes into the UPN_DNSInfo struct func (k *UPNDNSInfo) Unmarshal(b []byte) error { //The UPN_DNS_INFO structure is a simple structure that is not NDR-encoded. var p int var e binary.ByteOrder = binary.LittleEndian k.UPNLength = ndr.ReadUint16(&b, &p, &e) k.UPNOffset = ndr.ReadUint16(&b, &p, &e) k.DNSDomainNameLength = ndr.ReadUint16(&b, &p, &e) k.DNSDomainNameOffset = ndr.ReadUint16(&b, &p, &e) k.Flags = ndr.ReadUint32(&b, &p, &e) ub := b[k.UPNOffset : k.UPNOffset+k.UPNLength] db := b[k.DNSDomainNameOffset : k.DNSDomainNameOffset+k.DNSDomainNameLength] u := make([]rune, k.UPNLength/2, k.UPNLength/2) for i := 0; i < len(u); i++ { q := i * 2 u[i] = rune(ndr.ReadUint16(&ub, &q, &e)) } k.UPN = string(u) d := make([]rune, k.DNSDomainNameLength/2, k.DNSDomainNameLength/2) for i := 0; i < len(d); i++ { q := i * 2 d[i] = rune(ndr.ReadUint16(&db, &q, &e)) } k.DNSDomain = string(d) l := []int{ p, int(k.UPNOffset + k.UPNLength), int(k.DNSDomainNameOffset + k.DNSDomainNameLength), } sort.Ints(l) //Check that there is only zero padding left for _, v := range b[l[2]:] { if v != 0 { return ndr.Malformed{EText: "non-zero padding left over at end of data stream."} } } return nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/pac/upn_dns_info_test.go000066400000000000000000000017301362537225700253700ustar00rootroot00000000000000package pac import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUPN_DNSInfo_Unmarshal(t *testing.T) { t.Parallel() b, err := hex.DecodeString(testdata.TestVectors["PAC_UPN_DNS_Info"]) if err != nil { t.Fatal("Could not decode test data hex string") } var k UPNDNSInfo err = k.Unmarshal(b) if err != nil { t.Fatalf("Error unmarshaling test data: %v", err) } assert.Equal(t, uint16(42), k.UPNLength, "UPN Length not as expected") assert.Equal(t, uint16(16), k.UPNOffset, "UPN Offset not as expected") assert.Equal(t, uint16(22), k.DNSDomainNameLength, "DNS Domain Length not as expected") assert.Equal(t, uint16(64), k.DNSDomainNameOffset, "DNS Domain Offset not as expected") assert.Equal(t, "testuser1@test.gokrb5", k.UPN, "UPN not as expected") assert.Equal(t, "TEST.GOKRB5", k.DNSDomain, "DNS Domain not as expected") assert.Equal(t, uint32(0), k.Flags, "DNS Domain not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/000077500000000000000000000000001362537225700222155ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/APExchange.go000066400000000000000000000110451362537225700245100ustar00rootroot00000000000000package service import ( "fmt" "time" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/flags" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/krberror" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/types" ) // ValidateAPREQ validates an AP_REQ sent to the service. Returns a boolean for if the AP_REQ is valid and the client's principal name and realm. func ValidateAPREQ(APReq messages.APReq, kt keytab.Keytab, sa string, cAddr string, requireHostAddr bool) (bool, credentials.Credentials, error) { var creds credentials.Credentials err := APReq.Ticket.DecryptEncPart(kt, sa) if err != nil { return false, creds, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided") } a, err := APReq.DecryptAuthenticator(APReq.Ticket.DecryptedEncPart.Key) if err != nil { return false, creds, krberror.Errorf(err, krberror.DecryptingError, "error extracting authenticator") } // Check CName in Authenticator is the same as that in the ticket if !a.CName.Equal(APReq.Ticket.DecryptedEncPart.CName) { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket") return false, creds, err } if len(APReq.Ticket.DecryptedEncPart.CAddr) > 0 { h, err := types.GetHostAddress(cAddr) if err != nil { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, err.Error()) return false, creds, err } if !types.HostAddressesContains(APReq.Ticket.DecryptedEncPart.CAddr, h) { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Client address not within the list contained in the service ticket") return false, creds, err } } else if requireHostAddr { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "ticket does not contain HostAddress values required") return false, creds, err } // Check the clock skew between the client and the service server ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond) t := time.Now().UTC() // Hardcode 5 min max skew. May want to make this configurable d := time.Duration(5) * time.Minute if t.Sub(ct) > d || ct.Sub(t) > d { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("Clock skew with client too large. Greater than %v seconds", d)) return false, creds, err } // Check for replay rc := GetReplayCache(d) if rc.IsReplay(APReq.Ticket.SName, a) { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected") return false, creds, err } // Check for future tickets or invalid tickets if APReq.Ticket.DecryptedEncPart.StartTime.Sub(t) > d || types.IsFlagSet(&APReq.Ticket.DecryptedEncPart.Flags, flags.Invalid) { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "Service ticket provided is not yet valid") return false, creds, err } // Check for expired ticket if t.Sub(APReq.Ticket.DecryptedEncPart.EndTime) > d { err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "Service ticket provided has expired") return false, creds, err } creds = credentials.NewCredentialsFromPrincipal(a.CName, a.CRealm) creds.SetAuthTime(t) creds.SetAuthenticated(true) creds.SetValidUntil(APReq.Ticket.DecryptedEncPart.EndTime) isPAC, pac, err := APReq.Ticket.GetPACType(kt, sa) if isPAC && err != nil { return false, creds, err } if isPAC { // There is a valid PAC. Adding attributes to creds creds.SetADCredentials(credentials.ADCredentials{ GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(), LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(), LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(), PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(), EffectiveName: pac.KerbValidationInfo.EffectiveName.Value, FullName: pac.KerbValidationInfo.FullName.Value, UserID: int(pac.KerbValidationInfo.UserID), PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID), LogonServer: pac.KerbValidationInfo.LogonServer.Value, LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value, LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(), }) } return true, creds, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/APExchange_test.go000066400000000000000000000224571362537225700255600ustar00rootroot00000000000000package service import ( "encoding/hex" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/errorcode" "gopkg.in/jcmturner/gokrb5.v5/iana/flags" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestValidateAPREQ(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } APReq, err := messages.NewAPReq( tkt, sessionKey, newTestAuthenticator(*cl.Credentials), ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if !ok || err != nil { t.Fatalf("Validation of AP_REQ failed when it should not have: %v", err) } } func TestValidateAPREQ_KRB_AP_ERR_BADMATCH(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } a := newTestAuthenticator(*cl.Credentials) a.CName = types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"BADMATCH"}, } APReq, err := messages.NewAPReq( tkt, sessionKey, a, ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } if _, ok := err.(messages.KRBError); ok { assert.Equal(t, errorcode.KRB_AP_ERR_BADMATCH, err.(messages.KRBError).ErrorCode, "Error code not as expected") } else { t.Fatalf("Error is not a KRBError: %v", err) } } func TestValidateAPREQ_LargeClockSkew(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } a := newTestAuthenticator(*cl.Credentials) a.CTime = a.CTime.Add(time.Duration(-10) * time.Minute) APReq, err := messages.NewAPReq( tkt, sessionKey, a, ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } if _, ok := err.(messages.KRBError); ok { assert.Equal(t, errorcode.KRB_AP_ERR_SKEW, err.(messages.KRBError).ErrorCode, "Error code not as expected") } else { t.Fatalf("Error is not a KRBError: %v", err) } } func TestValidateAPREQ_Replay(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } APReq, err := messages.NewAPReq( tkt, sessionKey, newTestAuthenticator(*cl.Credentials), ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if !ok || err != nil { t.Fatalf("Validation of AP_REQ failed when it should not have: %v", err) } // Replay ok, _, err = ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } assert.IsType(t, messages.KRBError{}, err, "Error is not a KRBError") assert.Equal(t, errorcode.KRB_AP_ERR_REPEAT, err.(messages.KRBError).ErrorCode, "Error code not as expected") } func TestValidateAPREQ_FutureTicket(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st.Add(time.Duration(60)*time.Minute), st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } a := newTestAuthenticator(*cl.Credentials) APReq, err := messages.NewAPReq( tkt, sessionKey, a, ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } if _, ok := err.(messages.KRBError); ok { assert.Equal(t, errorcode.KRB_AP_ERR_TKT_NYV, err.(messages.KRBError).ErrorCode, "Error code not as expected") } else { t.Fatalf("Error is not a KRBError: %v", err) } } func TestValidateAPREQ_InvalidTicket(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() f := types.NewKrbFlags() types.SetFlag(&f, flags.Invalid) tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", f, kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } APReq, err := messages.NewAPReq( tkt, sessionKey, newTestAuthenticator(*cl.Credentials), ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } if _, ok := err.(messages.KRBError); ok { assert.Equal(t, errorcode.KRB_AP_ERR_TKT_NYV, err.(messages.KRBError).ErrorCode, "Error code not as expected") } else { t.Fatalf("Error is not a KRBError: %v", err) } } func TestValidateAPREQ_ExpiredTicket(t *testing.T) { t.Parallel() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(-30)*time.Minute), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } a := newTestAuthenticator(*cl.Credentials) APReq, err := messages.NewAPReq( tkt, sessionKey, a, ) if err != nil { t.Fatalf("Error getting test AP_REQ: %v", err) } ok, _, err := ValidateAPREQ(APReq, kt, "", "127.0.0.1", false) if ok || err == nil { t.Fatal("Validation of AP_REQ passed when it should not have") } if _, ok := err.(messages.KRBError); ok { assert.Equal(t, errorcode.KRB_AP_ERR_TKT_EXPIRED, err.(messages.KRBError).ErrorCode, "Error code not as expected") } else { t.Fatalf("Error is not a KRBError: %v", err) } } func newTestAuthenticator(creds credentials.Credentials) types.Authenticator { auth, _ := types.NewAuthenticator(creds.Realm, creds.CName) auth.GenerateSeqNumberAndSubKey(18, 32) //auth.Cksum = types.Checksum{ // CksumType: chksumtype.GSSAPI, // Checksum: newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}), //} return auth } func getClient() client.Client { b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB) kt, _ := keytab.Parse(b) c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF) cl := client.NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt) cl.WithConfig(c) return cl } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/authenticator.go000066400000000000000000000120731362537225700254210ustar00rootroot00000000000000package service import ( "encoding/base64" "errors" "fmt" "strings" "time" goidentity "gopkg.in/jcmturner/goidentity.v2" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/config" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/gssapi" "gopkg.in/jcmturner/gokrb5.v5/keytab" ) // SPNEGOAuthenticator implements gopkg.in/jcmturner/goidentity.v2.Authenticator interface type SPNEGOAuthenticator struct { SPNEGOHeaderValue string Keytab *keytab.Keytab ServiceAccount string ClientAddr string RequireHostAddr bool } // Authenticate and retrieve a goidentity.Identity. In this case it is a pointer to a credentials.Credentials func (a SPNEGOAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) { b, err := base64.StdEncoding.DecodeString(a.SPNEGOHeaderValue) if err != nil { err = fmt.Errorf("SPNEGO error in base64 decoding negotiation header: %v", err) return } var spnego gssapi.SPNEGO err = spnego.Unmarshal(b) if !spnego.Init { err = fmt.Errorf("SPNEGO negotiation token is not a NegTokenInit: %v", err) return } if !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDKRB5) { err = errors.New("SPNEGO OID of MechToken is not of type KRB5") return } var mt gssapi.MechToken err = mt.Unmarshal(spnego.NegTokenInit.MechToken) if err != nil { err = fmt.Errorf("SPNEGO error unmarshaling MechToken: %v", err) return } if !mt.IsAPReq() { err = errors.New("MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE") return } ok, c, err := ValidateAPREQ(mt.APReq, *a.Keytab, a.ServiceAccount, a.ClientAddr, a.RequireHostAddr) if err != nil { err = fmt.Errorf("SPNEGO validation error: %v", err) return } i = &c return } // Mechanism returns the authentication mechanism. func (a SPNEGOAuthenticator) Mechanism() string { return "SPNEGO Kerberos" } // KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v2.Authenticator interface. // It takes username and password so can be used for basic authentication. type KRB5BasicAuthenticator struct { BasicHeaderValue string realm string username string password string ServiceKeytab *keytab.Keytab ServiceAccount string Config *config.Config SPN string } // Authenticate and return the identity. The boolean indicates if the authentication was successful. func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) { a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue) if err != nil { err = fmt.Errorf("could not parse basic authentication header: %v", err) return } cl := client.NewClientWithPassword(a.username, a.realm, a.password) cl.WithConfig(a.Config) err = cl.Login() if err != nil { // Username and/or password could be wrong err = fmt.Errorf("error with user credentials during login: %v", err) return } tkt, _, err := cl.GetServiceTicket(a.SPN) if err != nil { err = fmt.Errorf("could not get service ticket: %v", err) return } err = tkt.DecryptEncPart(*a.ServiceKeytab, a.ServiceAccount) if err != nil { err = fmt.Errorf("could not decrypt service ticket: %v", err) return } cl.Credentials.SetAuthTime(time.Now().UTC()) cl.Credentials.SetAuthenticated(true) isPAC, pac, err := tkt.GetPACType(*a.ServiceKeytab, a.ServiceAccount) if isPAC && err != nil { err = fmt.Errorf("error processing PAC: %v", err) return } if isPAC { // There is a valid PAC. Adding attributes to creds cl.Credentials.SetADCredentials(credentials.ADCredentials{ GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(), LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(), LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(), PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(), EffectiveName: pac.KerbValidationInfo.EffectiveName.Value, FullName: pac.KerbValidationInfo.FullName.Value, UserID: int(pac.KerbValidationInfo.UserID), PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID), LogonServer: pac.KerbValidationInfo.LogonServer.Value, LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value, LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(), }) } ok = true i = cl.Credentials return } // Mechanism returns the authentication mechanism. func (a KRB5BasicAuthenticator) Mechanism() string { return "Kerberos Basic" } func parseBasicHeaderValue(s string) (domain, username, password string, err error) { b, err := base64.StdEncoding.DecodeString(s) if err != nil { return } v := string(b) vc := strings.SplitN(v, ":", 2) password = vc[1] // Domain and username can be specified in 2 formats: // - no domain specified // \ // @ if strings.Contains(vc[0], `\`) { u := strings.SplitN(vc[0], `\`, 2) domain = u[0] username = u[1] } else if strings.Contains(vc[0], `@`) { u := strings.SplitN(vc[0], `@`, 2) domain = u[1] username = u[0] } else { username = vc[0] } return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/authenticator_test.go000066400000000000000000000006021362537225700264530ustar00rootroot00000000000000package service import ( "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/goidentity.v2" ) func TestImplementsInterface(t *testing.T) { t.Parallel() //s := new(SPNEGOAuthenticator) var s SPNEGOAuthenticator a := new(goidentity.Authenticator) assert.Implements(t, a, s, "SPNEGOAuthenticator type does not implement the goidentity.Authenticator interface") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/cache.go000066400000000000000000000065541362537225700236210ustar00rootroot00000000000000// Package service provides server side integrations for Kerberos authentication. package service import ( "gopkg.in/jcmturner/gokrb5.v5/types" "sync" "time" ) // Cache for tickets received from clients keyed by fully qualified client name. Used to track replay of tickets. type Cache struct { Entries map[string]clientEntries mux sync.RWMutex } // clientEntries holds entries of client details sent to the service. type clientEntries struct { ReplayMap map[time.Time]replayCacheEntry SeqNumber int64 SubKey types.EncryptionKey } // Cache entry tracking client time values of tickets sent to the service. type replayCacheEntry struct { PresentedTime time.Time SName types.PrincipalName CTime time.Time // This combines the ticket's CTime and Cusec } func (c *Cache) getClientEntries(cname types.PrincipalName) (clientEntries, bool) { c.mux.RLock() defer c.mux.RUnlock() ce, ok := c.Entries[cname.GetPrincipalNameString()] return ce, ok } func (c *Cache) getClientEntry(cname types.PrincipalName, t time.Time) (replayCacheEntry, bool) { if ce, ok := c.getClientEntries(cname); ok { c.mux.RLock() defer c.mux.RUnlock() if e, ok := ce.ReplayMap[t]; ok { return e, true } } return replayCacheEntry{}, false } // Instance of the ServiceCache. This needs to be a singleton. var replayCache Cache var once sync.Once // GetReplayCache returns a pointer to the Cache singleton. func GetReplayCache(d time.Duration) *Cache { // Create a singleton of the ReplayCache and start a background thread to regularly clean out old entries once.Do(func() { replayCache = Cache{ Entries: make(map[string]clientEntries), } go func() { for { // TODO consider using a context here. time.Sleep(d) replayCache.ClearOldEntries(d) } }() }) return &replayCache } // AddEntry adds an entry to the Cache. func (c *Cache) AddEntry(sname types.PrincipalName, a types.Authenticator) { ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond) if ce, ok := c.getClientEntries(a.CName); ok { c.mux.Lock() defer c.mux.Unlock() ce.ReplayMap[ct] = replayCacheEntry{ PresentedTime: time.Now().UTC(), SName: sname, CTime: ct, } ce.SeqNumber = a.SeqNumber ce.SubKey = a.SubKey } else { c.mux.Lock() defer c.mux.Unlock() c.Entries[a.CName.GetPrincipalNameString()] = clientEntries{ ReplayMap: map[time.Time]replayCacheEntry{ ct: { PresentedTime: time.Now().UTC(), SName: sname, CTime: ct, }, }, SeqNumber: a.SeqNumber, SubKey: a.SubKey, } } } // ClearOldEntries clears entries from the Cache that are older than the duration provided. func (c *Cache) ClearOldEntries(d time.Duration) { c.mux.Lock() defer c.mux.Unlock() for ke, ce := range c.Entries { for k, e := range ce.ReplayMap { if time.Now().UTC().Sub(e.PresentedTime) > d { delete(ce.ReplayMap, k) } } if len(ce.ReplayMap) == 0 { delete(c.Entries, ke) } } } // IsReplay tests if the Authenticator provided is a replay within the duration defined. If this is not a replay add the entry to the cache for tracking. func (c *Cache) IsReplay(sname types.PrincipalName, a types.Authenticator) bool { ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond) if e, ok := c.getClientEntry(a.CName, ct); ok { if e.SName.Equal(sname) { return true } } c.AddEntry(sname, a) return false } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/http.go000066400000000000000000000125271362537225700235320ustar00rootroot00000000000000package service import ( "context" "encoding/base64" "fmt" "log" "net/http" "strings" "gopkg.in/jcmturner/gokrb5.v5/gssapi" "gopkg.in/jcmturner/gokrb5.v5/keytab" ) // POTENTIAL BREAKING CHANGE notice. Context keys used will change to a name-spaced strings to avoid clashes. // If you are using the constants service.CTXKeyAuthenticated and service.CTXKeyCredentials // defined below when retrieving data from the request context your code will be unaffected. // However if, for example, you are retrieving context like this: r.Context().Value(1) then // you will need to update to replace the 1 with service.CTXKeyCredentials. type ctxKey int const ( // spnegoNegTokenRespKRBAcceptCompleted - The response on successful authentication always has this header. Capturing as const so we don't have marshaling and encoding overhead. spnegoNegTokenRespKRBAcceptCompleted = "Negotiate oRQwEqADCgEAoQsGCSqGSIb3EgECAg==" // spnegoNegTokenRespReject - The response on a failed authentication always has this rejection header. Capturing as const so we don't have marshaling and encoding overhead. spnegoNegTokenRespReject = "Negotiate oQcwBaADCgEC" // CTXKeyAuthenticated is the request context key holding a boolean indicating if the request has been authenticated. CTXKeyAuthenticated ctxKey = 0 // CTXKeyCredentials is the request context key holding the credentials gopkg.in/jcmturner/goidentity.v2/Identity object. CTXKeyCredentials ctxKey = 1 // HTTPHeaderAuthRequest is the header that will hold authn/z information. HTTPHeaderAuthRequest = "Authorization" // HTTPHeaderAuthResponse is the header that will hold SPNEGO data from the server. HTTPHeaderAuthResponse = "WWW-Authenticate" // HTTPHeaderAuthResponseValueKey is the key in the auth header for SPNEGO. HTTPHeaderAuthResponseValueKey = "Negotiate" // UnauthorizedMsg is the message returned in the body when authentication fails. UnauthorizedMsg = "Unauthorised.\n" ) // SPNEGOKRB5Authenticate is a Kerberos SPNEGO authentication HTTP handler wrapper. // // kt - keytab for the service user // // ktprinc - keytab principal override for the service. // The service looks for this principal in the keytab to use to decrypt tickets. // If "" is passed as ktprinc then the principal will be automatically derived // from the service name (SName) and realm in the ticket the service is trying to decrypt. // This is often sufficient if you create the SPN in MIT KDC with: /usr/sbin/kadmin.local -q "add_principal HTTP/" // When Active Directory is used for the KDC this may need to be the account name you have set the SPN against // (setspn.exe -a "HTTP/" ) // If you are unsure run: // // klist -k // // and use the value from the Principal column for the keytab entry the service should use. func SPNEGOKRB5Authenticate(f http.Handler, kt keytab.Keytab, ktprinc string, requireHostAddr bool, l *log.Logger) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s := strings.SplitN(r.Header.Get(HTTPHeaderAuthRequest), " ", 2) if len(s) != 2 || s[0] != HTTPHeaderAuthResponseValueKey { w.Header().Set(HTTPHeaderAuthResponse, HTTPHeaderAuthResponseValueKey) w.WriteHeader(401) w.Write([]byte(UnauthorizedMsg)) return } b, err := base64.StdEncoding.DecodeString(s[1]) if err != nil { rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error in base64 decoding negotiation header: %v", r.RemoteAddr, err)) return } var spnego gssapi.SPNEGO err = spnego.Unmarshal(b) if !spnego.Init { rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO negotiation token is not a NegTokenInit: %v", r.RemoteAddr, err)) return } if !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDKRB5) && !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDMSLegacyKRB5) { rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO OID of MechToken is not of type KRB5", r.RemoteAddr)) return } var mt gssapi.MechToken err = mt.Unmarshal(spnego.NegTokenInit.MechToken) if err != nil { rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO error unmarshaling MechToken: %v", r.RemoteAddr, err)) return } if !mt.IsAPReq() { rejectSPNEGO(w, l, fmt.Sprintf("%v - MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE", r.RemoteAddr)) return } if ok, creds, err := ValidateAPREQ(mt.APReq, kt, ktprinc, r.RemoteAddr, requireHostAddr); ok { ctx := r.Context() ctx = context.WithValue(ctx, CTXKeyCredentials, creds) ctx = context.WithValue(ctx, CTXKeyAuthenticated, true) if l != nil { l.Printf("%v %s@%s - SPNEGO authentication succeeded", r.RemoteAddr, creds.Username, creds.Realm) } spnegoResponseAcceptCompleted(w) f.ServeHTTP(w, r.WithContext(ctx)) } else { rejectSPNEGO(w, l, fmt.Sprintf("%v - SPNEGO Kerberos authentication failed: %v", r.RemoteAddr, err)) return } }) } // Set the headers for a rejected SPNEGO negotiation and return an unauthorized status code. func rejectSPNEGO(w http.ResponseWriter, l *log.Logger, logMsg string) { if l != nil { l.Println(logMsg) } spnegoResponseReject(w) } func spnegoResponseReject(w http.ResponseWriter) { w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespReject) w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(UnauthorizedMsg)) } func spnegoResponseAcceptCompleted(w http.ResponseWriter) { w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespKRBAcceptCompleted) } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/service/http_test.go000066400000000000000000000164411362537225700245700ustar00rootroot00000000000000package service import ( "encoding/hex" "fmt" "io/ioutil" "log" "net/http" "net/http/httptest" "sync" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/client" "gopkg.in/jcmturner/gokrb5.v5/credentials" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/keytab" "gopkg.in/jcmturner/gokrb5.v5/messages" "gopkg.in/jcmturner/gokrb5.v5/testdata" "gopkg.in/jcmturner/gokrb5.v5/types" ) func TestService_SPNEGOKRB_NoAuthHeader(t *testing.T) { s := httpServer() defer s.Close() r, _ := http.NewRequest("GET", s.URL, nil) httpResp, err := http.DefaultClient.Do(r) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected") assert.Equal(t, "Negotiate", httpResp.Header.Get("WWW-Authenticate"), "Negitation header not set by server.") } func TestService_SPNEGOKRB_ValidUser(t *testing.T) { s := httpServer() defer s.Close() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } r, _ := http.NewRequest("GET", s.URL, nil) err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r) if err != nil { t.Fatalf("Error setting client SPNEGO header: %v", err) } httpResp, err := http.DefaultClient.Do(r) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected") } func TestService_SPNEGOKRB_Replay(t *testing.T) { s := httpServer() defer s.Close() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } r1, _ := http.NewRequest("GET", s.URL, nil) err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r1) if err != nil { t.Fatalf("Error setting client SPNEGO header: %v", err) } // First request with this ticket should be accepted httpResp, err := http.DefaultClient.Do(r1) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected") // Use ticket again should be rejected httpResp, err = http.DefaultClient.Do(r1) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.") // Form a 2nd ticket st = time.Now().UTC() tkt2, sessionKey2, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } r2, _ := http.NewRequest("GET", s.URL, nil) err = client.SetSPNEGOHeader(*cl.Credentials, tkt2, sessionKey2, r2) if err != nil { t.Fatalf("Error setting client SPNEGO header: %v", err) } // First use of 2nd ticket should be accepted httpResp, err = http.DefaultClient.Do(r2) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected") // Using the 1st ticket again should still be rejected httpResp, err = http.DefaultClient.Do(r1) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.") // Using the 2nd again should be rejected as replay httpResp, err = http.DefaultClient.Do(r2) if err != nil { t.Fatalf("Request error: %v\n", err) } assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected. Expected a replay to be detected.") } func TestService_SPNEGOKRB_ReplayCache_Concurrency(t *testing.T) { s := httpServer() defer s.Close() cl := getClient() sname := types.PrincipalName{ NameType: nametype.KRB_NT_PRINCIPAL, NameString: []string{"HTTP", "host.test.gokrb5"}, } b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) st := time.Now().UTC() tkt, sessionKey, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } r1, _ := http.NewRequest("GET", s.URL, nil) err = client.SetSPNEGOHeader(*cl.Credentials, tkt, sessionKey, r1) if err != nil { t.Fatalf("Error setting client SPNEGO header: %v", err) } // Form a 2nd ticket st = time.Now().UTC() tkt2, sessionKey2, err := messages.NewTicket(cl.Credentials.CName, cl.Credentials.Realm, sname, "TEST.GOKRB5", types.NewKrbFlags(), kt, 18, 1, st, st, st.Add(time.Duration(24)*time.Hour), st.Add(time.Duration(48)*time.Hour), ) if err != nil { t.Fatalf("Error getting test ticket: %v", err) } r2, _ := http.NewRequest("GET", s.URL, nil) err = client.SetSPNEGOHeader(*cl.Credentials, tkt2, sessionKey2, r2) if err != nil { t.Fatalf("Error setting client SPNEGO header: %v", err) } // Concurrent 1st requests should be OK var wg sync.WaitGroup wg.Add(2) go httpGet(r1, &wg) go httpGet(r2, &wg) wg.Wait() // A number of concurrent requests with the same ticket should be rejected due to replay var wg2 sync.WaitGroup noReq := 10 wg2.Add(noReq * 2) for i := 0; i < noReq; i++ { go httpGet(r1, &wg2) go httpGet(r2, &wg2) } wg2.Wait() } func httpGet(r *http.Request, wg *sync.WaitGroup) { defer wg.Done() http.DefaultClient.Do(r) } func httpServer() *httptest.Server { l := log.New(ioutil.Discard, "GOKRB5 Service Tests: ", log.Ldate|log.Ltime|log.Lshortfile) b, _ := hex.DecodeString(testdata.HTTP_KEYTAB) kt, _ := keytab.Parse(b) th := http.HandlerFunc(testAppHandler) s := httptest.NewServer(SPNEGOKRB5Authenticate(th, kt, "", false, l)) return s } func testAppHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) ctx := r.Context() fmt.Fprintf(w, "\nTEST.GOKRB5 Handler\nAuthenticed user: %s\nUser's realm: %s\n", ctx.Value(CTXKeyCredentials).(credentials.Credentials).Username, ctx.Value(CTXKeyCredentials).(credentials.Credentials).Realm) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/testdata/000077500000000000000000000000001362537225700223665ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/testdata/test_vectors.go000066400000000000000000001756461362537225700254640ustar00rootroot00000000000000// Package testdata provides Kerberos 5 test reference data. package testdata const ( //Expected unmarshaled values TEST_REALM = "ATHENA.MIT.EDU" TEST_CIPHERTEXT = "krbASN.1 test message" TEST_TIME_FORMAT = "20060102150405" TEST_TIME = "19940610060317" TEST_ETYPE int32 = 0 TEST_NONCE = 42 TEST_AUTHORIZATION_DATA_VALUE = "foobar" TEST_PADATA_VALUE = "pa-data" ) var TEST_PRINCIPALNAME_NAMESTRING = []string{"hftsai", "extra"} //The test vectors have been sourced from https://github.com/krb5/krb5/blob/master/src/tests/asn.1/reference_encode.out var TestVectors = map[string]string{ "encode_krb5_authenticator": "6281A130819EA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A30F300DA003020101A106040431323334A405020301E240A511180F31393934303631303036303331375AA6133011A003020101A10A04083132333435363738A703020111A8243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172", "encode_krb5_authenticator(optionalsempty)": "624F304DA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A405020301E240A511180F31393934303631303036303331375A", "encode_krb5_authenticator(optionalsNULL)": "624F304DA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A405020301E240A511180F31393934303631303036303331375A", "encode_krb5_ticket": "615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_keyblock": "3011A003020101A10A04083132333435363738", "encode_krb5_enc_tkt_part": "6382011430820110A007030500FEDCBA98A1133011A003020101A10A04083132333435363738A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A42E302CA003020101A12504234544552C4D49542E2C415448454E412E2C57415348494E47544F4E2E4544552C43532EA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA811180F31393934303631303036303331375AA920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172", "encode_krb5_enc_tkt_part(optionalsNULL)": "6381A53081A2A007030500FEDCBA98A1133011A003020101A10A04083132333435363738A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A42E302CA003020101A12504234544552C4D49542E2C415448454E412E2C57415348494E47544F4E2E4544552C43532EA511180F31393934303631303036303331375AA711180F31393934303631303036303331375A", "encode_krb5_enc_kdc_rep_part": "7A82010E3082010AA0133011A003020101A10A04083132333435363738A13630343018A0030201FBA111180F31393934303631303036303331375A3018A0030201FBA111180F31393934303631303036303331375AA20302012AA311180F31393934303631303036303331375AA407030500FEDCBA98A511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA811180F31393934303631303036303331375AA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261AB20301E300DA003020102A106040412D00023300DA003020102A106040412D00023", "encode_krb5_enc_kdc_rep_part(optionalsNULL)": "7A81B23081AFA0133011A003020101A10A04083132333435363738A13630343018A0030201FBA111180F31393934303631303036303331375A3018A0030201FBA111180F31393934303631303036303331375AA20302012AA407030500FE5CBA98A511180F31393934303631303036303331375AA711180F31393934303631303036303331375AA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261", "encode_krb5_as_rep": "6B81EA3081E7A003020105A10302010BA22630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_as_rep(optionalsNULL)": "6B81C23081BFA003020105A10302010BA3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_tgs_rep": "6D81EA3081E7A003020105A10302010DA22630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_tgs_rep(optionalsNULL)": "6D81C23081BFA003020105A10302010DA3101B0E415448454E412E4D49542E454455A41A3018A003020101A111300F1B066866747361691B056578747261A55E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A6253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_ap_req": "6E819D30819AA003020105A10302010EA207030500FEDCBA98A35E615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A4253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_ap_rep": "6F333031A003020105A10302010FA2253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_ap_rep_enc_part": "7B363034A011180F31393934303631303036303331375AA105020301E240A2133011A003020101A10A04083132333435363738A303020111", "encode_krb5_ap_rep_enc_part(optionalsNULL)": "7B1C301AA011180F31393934303631303036303331375AA105020301E240", "encode_krb5_as_req": "6A8201E4308201E0A103020105A20302010AA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_as_req(optionalsNULLexceptsecond_ticket)": "6A82011430820110A103020105A20302010AA48201023081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_as_req(optionalsNULLexceptserver)": "6A693067A103020105A20302010AA45B3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101", "encode_krb5_tgs_req": "6C8201E4308201E0A103020105A20302010CA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_tgs_req(optionalsNULLexceptsecond_ticket)": "6C82011430820110A103020105A20302010CA48201023081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_tgs_req(optionalsNULLexceptserver)": "6C693067A103020105A20302010CA45B3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101", "encode_krb5_kdc_req_body": "308201A6A007030500FEDCBA90A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_kdc_req_body(optionalsNULLexceptsecond_ticket)": "3081FFA007030500FEDCBA98A2101B0E415448454E412E4D49542E454455A511180F31393934303631303036303331375AA70302012AA8083006020100020101AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_kdc_req_body(optionalsNULLexceptserver)": "3059A007030500FEDCBA90A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A511180F31393934303631303036303331375AA70302012AA8083006020100020101", "encode_krb5_safe": "746E306CA003020105A103020114A24F304DA00A04086B72623564617461A111180F31393934303631303036303331375AA205020301E240A303020111A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023A30F300DA003020101A106040431323334", "encode_krb5_safe(optionalsNULL)": "743E303CA003020105A103020114A21F301DA00A04086B72623564617461A40F300DA003020102A106040412D00023A30F300DA003020101A106040431323334", "encode_krb5_priv": "75333031A003020105A103020115A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_enc_priv_part": "7C4F304DA00A04086B72623564617461A111180F31393934303631303036303331375AA205020301E240A303020111A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023", "encode_krb5_enc_priv_part(optionalsNULL)": "7C1F301DA00A04086B72623564617461A40F300DA003020102A106040412D00023", "encode_krb5_cred": "7681F63081F3A003020105A103020116A281BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_enc_cred_part": "7D8202233082021FA08201DA308201D63081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D000233081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D00023A10302012AA211180F31393934303631303036303331375AA305020301E240A40F300DA003020102A106040412D00023A50F300DA003020102A106040412D00023", "encode_krb5_enc_cred_part(optionalsNULL)": "7D82010E3082010AA0820106308201023015A0133011A003020101A10A040831323334353637383081E8A0133011A003020101A10A04083132333435363738A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A307030500FEDCBA98A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA711180F31393934303631303036303331375AA8101B0E415448454E412E4D49542E454455A91A3018A003020101A111300F1B066866747361691B056578747261AA20301E300DA003020102A106040412D00023300DA003020102A106040412D00023", "encode_krb5_error": "7E81BA3081B7A003020105A10302011EA211180F31393934303631303036303331375AA305020301E240A411180F31393934303631303036303331375AA505020301E240A60302013CA7101B0E415448454E412E4D49542E454455A81A3018A003020101A111300F1B066866747361691B056578747261A9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261AB0A1B086B72623564617461AC0A04086B72623564617461", "encode_krb5_error(optionalsNULL)": "7E60305EA003020105A10302011EA305020301E240A411180F31393934303631303036303331375AA505020301E240A60302013CA9101B0E415448454E412E4D49542E454455AA1A3018A003020101A111300F1B066866747361691B056578747261", "encode_krb5_authorization_data": "3022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172", "encode_krb5_padata_sequence": "30243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461", "encode_krb5_typed_data": "30243010A00302010DA109040770612D646174613010A00302010DA109040770612D64617461", "encode_krb5_padata_sequence(empty)": "3000", "encode_krb5_etype_info": "30333014A003020100A10D040B4D6F72746F6E27732023303005A0030201013014A003020102A10D040B4D6F72746F6E2773202332", "encode_krb5_etype_info(only1)": "30163014A003020100A10D040B4D6F72746F6E2773202330", "encode_krb5_etype_info(noinfo)": "3000", "encode_krb5_etype_info2": "3051301EA003020100A10D1B0B4D6F72746F6E2773202330A208040673326B3A2030300FA003020101A208040673326B3A2031301EA003020102A10D1B0B4D6F72746F6E2773202332A208040673326B3A2032", "encode_krb5_etype_info2(only1)": "3020301EA003020100A10D1B0B4D6F72746F6E2773202330A208040673326B3A2030", "encode_krb5_pa_enc_ts": "301AA011180F31393934303631303036303331375AA105020301E240", "encode_krb5_pa_enc_ts(nousec)": "3013A011180F31393934303631303036303331375A", "encode_krb5_enc_data": "3023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_enc_data(MSB-setkvno)": "3026A003020100A1060204FF000000A21704156B726241534E2E312074657374206D657373616765", "encode_krb5_enc_data(kvno= -1)": "3023A003020100A1030201FFA21704156B726241534E2E312074657374206D657373616765", //"encode_krb5_sam_challenge_2": "3022A00D300B04096368616C6C656E6765A111300F300DA003020101A106040431323334", //"encode_krb5_sam_challenge_2_body": "3064A00302012AA10703050080000000A20B040974797065206E616D65A411040F6368616C6C656E6765206C6162656CA510040E6368616C6C656E67652069707365A6160414726573706F6E73655F70726F6D70742069707365A8050203543210A903020101", //"encode_krb5_sam_response_2": "3042A00302012BA10703050080000000A20C040A747261636B2064617461A31D301BA003020101A10402020D36A20E040C6E6F6E6365206F7220736164A4050203543210", //"encode_krb5_enc_sam_response_enc_2": "301FA003020158A1180416656E635F73616D5F726573706F6E73655F656E635F32", //"encode_krb5_pa_for_user": "304BA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A20F300DA003020101A106040431323334A30A1B086B72623564617461", //"encode_krb5_pa_s4u_x509_user": "3068A0553053A006020400CA149AA11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A312041070615F7334755F783530395F75736572A40703050080000000A10F300DA003020101A106040431323334", "encode_krb5_ad_kdcissued": "3065A00F300DA003020101A106040431323334A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172", //"encode_krb5_ad_signedpath_data": "3081C7A030302EA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A111180F31393934303631303036303331375AA2323030302EA01A3018A003020101A111300F1B066866747361691B056578747261A1101B0E415448454E412E4D49542E454455A32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A4243022300FA003020101A1080406666F6F626172300FA003020101A1080406666F6F626172", //"encode_krb5_ad_signedpath": "303EA003020101A10F300DA003020101A106040431323334A32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461", //"encode_krb5_iakerb_header": "3018A10A04086B72623564617461A20A04086B72623564617461", //"encode_krb5_iakerb_finished": "3011A10F300DA003020101A106040431323334", //"encode_krb5_fast_response": "30819FA02630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A1133011A003020101A10A04083132333435363738A25B3059A011180F31393934303631303036303331375AA105020301E240A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A40F300DA003020101A106040431323334A30302012A", //"encode_krb5_pa_fx_fast_reply": "A0293027A0253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", //"encode_krb5_otp_tokeninfo(optionalsNULL)": "300780050000000000", //"encode_krb5_otp_tokeninfo": "307280050077000000810B4578616D706C65636F727082056861726B2183010A8401028509796F7572746F6B656E862875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F7470A716300B0609608648016503040201300706052B0E03021A880203E8", //"encode_krb5_pa_otp_challenge(optionalsNULL)": "301580086D696E6E6F6E6365A209300780050000000000", //"encode_krb5_pa_otp_challenge": "3081A580086D61786E6F6E6365810B7465737473657276696365A27D300780050000000000307280050077000000810B4578616D706C65636F727082056861726B2183010A8401028509796F7572746F6B656E862875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F7470A716300B0609608648016503040201300706052B0E03021A880203E883076B657973616C74840431323334", //"encode_krb5_pa_otp_req(optionalsNULL)": "302C80050000000000A223A003020100A103020105A21704156B726241534E2E312074657374206D657373616765", //"encode_krb5_pa_otp_req": "3081B98005006000000081056E6F6E6365A223A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A30B0609608648016503040201840203E8850566726F6773860A6D79666972737470696E87056861726B21880F31393934303631303036303331375A89033334368A01028B09796F7572746F6B656E8C2875726E3A696574663A706172616D733A786D6C3A6E733A6B657970726F763A70736B633A686F74708D0B4578616D706C65636F7270", //"encode_krb5_pa_otp_enc_req": "300A80086B72623564617461", //"encode_krb5_kkdcp_message": "308201FCA08201EC048201E86A8201E4308201E0A103020105A20302010AA32630243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461A48201AA308201A6A007030500FEDCBA98A11A3018A003020101A111300F1B066866747361691B056578747261A2101B0E415448454E412E4D49542E454455A31A3018A003020101A111300F1B066866747361691B056578747261A411180F31393934303631303036303331375AA511180F31393934303631303036303331375AA611180F31393934303631303036303331375AA70302012AA8083006020100020101A920301E300DA003020102A106040412D00023300DA003020102A106040412D00023AA253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765AB81BF3081BC615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765615C305AA003020105A1101B0E415448454E412E4D49542E454455A21A3018A003020101A111300F1B066866747361691B056578747261A3253023A003020100A103020105A21704156B726241534E2E312074657374206D657373616765A10A1B086B72623564617461", //"encode_krb5_cammac(optionalsNULL)": "3012A010300E300CA003020101A1050403616431", //"encode_krb5_cammac": "3081F2A01E301C300CA003020101A1050403616431300CA003020102A1050403616432A13D303BA01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A3133011A003020101A10A0408636B73756D6B6463A23D303BA01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A3133011A003020101A10A0408636B73756D737663A35230503013A311300FA003020101A1080406636B73756D313039A01A3018A003020101A111300F1B066866747361691B056578747261A103020105A203020110A311300FA003020101A1080406636B73756D32", //"encode_krb5_secure_cookie": "302C02042DF8022530243010A10302010DA209040770612D646174613010A10302010DA209040770612D64617461", //MS PAC Vectors "PAC_AuthorizationData_MS": "308205523082054ea00402020080a182054404820540040000000000000001000000b004000048000000000000000a00000012000000f804000000000000060000001400000010050000000000000700000014000000280500000000000001100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e00000000000049d90e656ac60108006c007a006800750000000000000076ffffff41edce9a34815d3aef7bc98874805d250000000076fffffff7a534dab2c02986efe0fbe5110a4f3200000000", "PAC_Kerb_Validation_Info_MS": "01100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e0000000000", "PAC_AuthorizationData_GOKRB5": "3082034730820343a003020101a182033a04820336308203323082032ea00402020080a1820324048203200500000000000000010000002802000058000000000000000a0000001c00000080020000000000000c00000058000000a0020000000000000600000010000000f8020000000000000700000014000000080300000000000001100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000808dd1dc80c6d2011200740065007300740075007300650072003100000000002a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000100000001e251d98d552be7df384f55076ffffff340be28b48765d0519ee9346cf53d82200000000", "PAC_AD_WIN2K_PAC": "0500000000000000010000002802000058000000000000000a0000001c00000080020000000000000c00000058000000a0020000000000000600000010000000f8020000000000000700000014000000080300000000000001100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000808dd1dc80c6d2011200740065007300740075007300650072003100000000002a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000100000001e251d98d552be7df384f55076ffffff340be28b48765d0519ee9346cf53d82200000000", "PAC_Kerb_Validation_Info": "01100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000", "PAC_Client_Info": "808dd1dc80c6d2011200740065007300740075007300650072003100", "PAC_UPN_DNS_Info": "2a001000160040000000000000000000740065007300740075007300650072003100400074006500730074002e0067006f006b0072006200350000000000000054004500530054002e0047004f004b005200420035000000", "PAC_Server_Signature": "100000001e251d98d552be7df384f550", "PAC_KDC_Signature": "76ffffff340be28b48765d0519ee9346cf53d822", "PAC_Kerb_Validation_Info_Trust": "01100800cccccccc000200000000000000000200c30bcc79e444d301ffffffffffffff7fffffffffffffff7fc764125a0842d301c7247c84d142d301ffffffffffffff7f12001200040002001600160008000200000000000c0002000000000010000200000000001400020000000000180002002e0000005204000001020000030000001c0002002002000000000000000000000000000000000000060008002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000010000002c00020034000200020000003800020009000000000000000900000074006500730074007500730065007200310000000b000000000000000b0000005400650073007400310020005500730065007200310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000056040000070000000102000007000000550400000700000004000000000000000300000055004400430000000500000000000000040000005500530045005200040000000104000000000005150000002057308834e7d1d0a2fb0444010000003000020007000000010000000101000000000012010000000400000001040000000000051500000062dc8db6c8705249b5459e75020000005304000007000020540400000700002000000000", "PAC_ClientClaimsInfoStr": "01100800cccccccc000100000000000000000200d80000000400020000000000d8000000000000000000000000000000d800000001100800ccccccccc80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000100000010000200290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a0038003800640035006400390030003800350065006100350063003000630030000000000001000000140002000a000000000000000a00000074006500730074007500730065007200310000000000000000000000", "PAC_ClientClaimsInfoInt": "01100800cccccccce00000000000000000000200b80000000400020000000000b8000000000000000000000000000000b800000001100800cccccccca80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c0002000100010001000000100002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c0000000000000000000000", "PAC_ClientClaimsInfoMulti": "01100800cccccccc780100000000000000000200500100000400020000000000500100000000000000000000000000005001000001100800cccccccc400100000000000000000200010000000400020000000000000000000000000001000000010000000200000008000200020000000c000200010001000100000010000200140002000300030001000000180002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c00000000000000290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a00380038006400350064003900300038003500650061003500630030006300300000000000010000001c0002000a000000000000000a000000740065007300740075007300650072003100000000000000", "PAC_ClientClaimsInfoMultiUint": "01100800ccccccccf00000000000000000000200c80000000400020000000000c8000000000000000000000000000000c800000001100800ccccccccb80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200020002000400000010000200260000000000000026000000610064003a002f002f006500780074002f006f0062006a0065006300740043006c006100730073003a00380038006400350064006500370039003100650037006200320037006500360000000400000009000a000000000007000100000000000600010000000000000001000000000000000000", "PAC_ClientClaimsInfoMultiStr": "01100800cccccccc480100000000000000000200200100000400020000000000200100000000000000000000000000002001000001100800cccccccc100100000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000400000010000200270000000000000027000000610064003a002f002f006500780074002f006f00740068006500720049007000500068006f006e0065003a003800380064003500640065003900660036006200340061006600390038003500000000000400000014000200180002001c000200200002000500000000000000050000007300740072003100000000000500000000000000050000007300740072003200000000000500000000000000050000007300740072003300000000000500000000000000050000007300740072003400000000000000000000000000", "PAC_ClientClaimsInfo_XPRESS_HUFF": "01100800ccccccccd00100000000000000000200a80100000400020004000000e0010000000000000000000000000000a8010000727807888708080007000800080008000800080880000080870870887807000080800000000080080000080000000000605767070007777707677700770000000000000000000000000000000000000000000000000000000000000000000000000000000000070007000000000000000000000000000000000000000000000076000700700000007600000000000000750700000000000064770700000000007607000000000000060700000000000077060700000000707770700070000770007700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a85652950bb9d8bae030b2212b90df95764d1b182da22f2c848b23b3cc4efc8e3499701e481cf938e490986a384c3d572250aaab2446572fc26be279c263e4a4c9c2c24f9649e2444d8ddb3277373c600363beb73200baaa783da183dd85830af863e1a00d5cf718aac4879519fbf0745bcc59214493a330f940bf99a446f1ade6df2610c5f154b432eaba964d7ad1f1182e522019fc21ce498a204d06b96a476f7386e6003000000000000", "ChangePasswdData": "3036a00d040b6e657770617373776f7264a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235", "Kpasswd_Req": "037aff8002c16e8202bd308202b9a003020105a10302010ea20703050000000000a38201f1618201ed308201e9a003020105a10d1b0b544553542e474f4b524235a21d301ba003020101a11430121b066b61646d696e1b086368616e67657077a38201b2308201aea003020111a103020101a28201a00482019ca3de94df50e8e9fe7a8c9386f594f469bf08874407fc7b95ddcf22110ef63e62ff0ba3c31c3bb725dc1dde1f4c2f69a4973b4b43c9b4b31f71f676d5e8e7b4d7906b1dfacc9897d865b17f934fb96b802344463bb0746fdd39e9e48ff1b2665dc895a74d3d3aac89512b43bd8ead8f455b9b819cc6f6a34fb7c5975d7c2dbd4349524961215b98f33f5747f1e0c89f3b3637462308953940741ab7fc38ae817ba85800dd911bb78b42264f2d285c2a0a33ca21c1a3d281ec14614010db31c3e3f4d4622b799f97b3d31c4445411278fec62dd8e6e349db280aaa4419b53ef6fbc01f0206bcfea2cbe835b46764c03c138722e54dab53a1080e5d6c99f8cd7a948880677176cfc2d3800f9ef64d1ec4f8bdadc1ae409990c4855a82e265682e8ddaa6dea70a1d7855f3e1e766f5efe428dd6da71c585f5d17d8f81e8f2a4f4b2245f5ff2cc444a2a1ae5d16a15d588597219d5659da537f752ca9b572b635088b325b60e8e62fd99487872261f41dcc466516b89992d277bb8b3a1ca770671fca36dd33c3dd6dab643e6710280661029254054273151ccfca9aaddc55a481ae3081aba003020112a103020101a2819e04819be66387f971d751d7d3ebb6acd815a0991e0ed9f07e2643783e7961fb88127b31f767bf00d1d071a81858b101f4d45460412d8013228f942bc51891e95a06aefa8cedd95e5a3e6e65597c0f05c19ee54dc6dc00b1a3f9d7a95516b5e447c40cd5b462ed6b17a007670311efa44dbe939cab11072b9af1443c3203767bb1a3240542db06dffcaebcedd5c335bb295127bc0e6d99f2c1e87f68de1f547581b03081ada003020105a103020115a381a030819da003020112a103020101a2819004818df272b2726c8f31c578f3b4275bc283828716010a20f0c4369bff474fcf202537060a71edcbe8ba720d0d9b2bac26b58353dc5b2945570374928a819eb3526362eda328e704f1a5ebe3272eed0fa6a6aa7d0f32c4fc0bd2e4ea52a8834ea7b5fb018934df87c18ab625f5c07f6c28e202e0cec63bcc37b1d381d64937998c1bdcd1585695eeffb75f8ce9e736b3", "Kpasswd_Rep": "00ec0001008c6f8189308186a003020105a10302010fa27a3078a003020112a271046f57cb442fd321312aff0b2dcda70fe436812f9805611adf3403ab6cd7708604e86e77f765a8486864f0dbf8d5d065a63790370bc110ed1e3c7eae9890e02407e8a8b349703fed1e7f165e1261a822c5b3e6823c282884f59afeb9f84f2a9845994135dd307eb2f544874393c1c455d475583056a003020105a103020115a34a3048a003020112a241043fdd3edaf0b6cbcab5b663189bafc0a19e6cc03b3c59d989c403735748ebc36088bad852add0f62581eed515fc1f297324df4fa12cb94b7ad5db257165369db5", } const ( TESTUSER1_PASSWORD = "passwordvalue" TESTUSER1_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100110010698c4df8e9f60e7eea5a21bf4526ad25000000010000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200110010698c4df8e9f60e7eea5a21bf4526ad25000000020000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001300102eb8501967a7886e1f0c63ac9be8c4a0000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001300102eb8501967a7886e1f0c63ac9be8c4a0000000020000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee000000010000004b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee00000002000000430001000b544553542e474f4b52423500097465737475736572310000000159beb1d801001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e300000001000000430001000b544553542e474f4b52423500097465737475736572310000000159beb1d802001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e3000000020000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572310000000159beb1d80200170010084768c373663b3bef1f6385883cf7ff00000002" TESTUSER1_USERKRB5_AD_KEYTAB = "05020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100110010528b8ba0ae5131fbf71f6ddc5870cdce000000010000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c04010012002016475f89eba70e62af20a20e7bf3ca4ccad5ae22485c93ffb133650bc6f12585000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040100130010a3ddea306fa06c068bc3e1fcf4b280ca000000010000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c0401001400205d2a66a8af5142db59bcaabac8310777bf60a85e8881469e2063bba4dff0c00a000000010000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200170010084768c373663b3bef1f6385883cf7ff000000020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200110010528b8ba0ae5131fbf71f6ddc5870cdce000000020000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c04020012002016475f89eba70e62af20a20e7bf3ca4ccad5ae22485c93ffb133650bc6f12585000000020000003b0001000b555345522e474f4b52423500097465737475736572310000000159de7c040200130010a3ddea306fa06c068bc3e1fcf4b280ca000000020000004b0001000b555345522e474f4b52423500097465737475736572310000000159de7c0402001400205d2a66a8af5142db59bcaabac8310777bf60a85e8881469e2063bba4dff0c00a00000002" TESTUSER2_USERKRB5_AD_KEYTAB = "05020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0100110010a771a31fae504621fffc644a521e0cee000000010000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001200203262b201af7ec73c77bbee75a4ff10950cf2bda56529ead30ced4f0f0b9a591d000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001300104be17a4cf1761f0494475617f671fa6a000000010000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d01001400208b1c60589b6c0d78d5e8fe265e92c2babf920a6a2828c37fc343e43497ad9fab000000010000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0200170010084768c373663b3bef1f6385883cf7ff000000020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d0200110010a771a31fae504621fffc644a521e0cee000000020000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001200203262b201af7ec73c77bbee75a4ff10950cf2bda56529ead30ced4f0f0b9a591d000000020000003b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001300104be17a4cf1761f0494475617f671fa6a000000020000004b0001000b555345522e474f4b5242350009746573747573657232000000015b2e4f5d02001400208b1c60589b6c0d78d5e8fe265e92c2babf920a6a2828c37fc343e43497ad9fab00000002" TESTUSER1_WRONGPASSWD = "0502000000370001000b544553542e474f4b52423500097465737475736572310000000158ef4bc5010011001039a9a382153105f8708e80f93382654e000000470001000b544553542e474f4b52423500097465737475736572310000000158ef4bc60100120020fc5bb940d6075214e0c6fc0456ce68c33306094198a927b4187d7cf3f4aea50d" TESTUSER2_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb240010011001086824c55ff5de30386dd83dc62b44bb7000000010000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb240020011001086824c55ff5de30386dd83dc62b44bb7000000020000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb24001001300106ccff358aaa8a4a41c444e173b1463c2000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb24002001300106ccff358aaa8a4a41c444e173b1463c2000000020000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb24001001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd7000000010000004b0001000b544553542e474f4b52423500097465737475736572320000000159beb24002001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd700000002000000430001000b544553542e474f4b52423500097465737475736572320000000159beb2400100100018bc025746e9e66bd6b62a918f6413d529803192a28aabf79200000001000000430001000b544553542e474f4b52423500097465737475736572320000000159beb2400200100018bc025746e9e66bd6b62a918f6413d529803192a28aabf792000000020000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572320000000159beb2400200170010084768c373663b3bef1f6385883cf7ff00000002" TESTUSER3_KEYTAB = "05020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb27401001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200110010220789f5cf68e44a852c73b1e3729efc000000020000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb27402001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100130010d2509e2bf9c08f73aafe08745c85e8fd000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200130010d2509e2bf9c08f73aafe08745c85e8fd000000020000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb274010014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e000000010000004b0001000b544553542e474f4b52423500097465737475736572330000000159beb274020014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e00000002000000430001000b544553542e474f4b52423500097465737475736572330000000159beb27401001000182cad768989a125fd26f24adf671976456d8097548c4c83f100000001000000430001000b544553542e474f4b52423500097465737475736572330000000159beb27402001000182cad768989a125fd26f24adf671976456d8097548c4c83f1000000020000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740100170010084768c373663b3bef1f6385883cf7ff000000010000003b0001000b544553542e474f4b52423500097465737475736572330000000159beb2740200170010084768c373663b3bef1f6385883cf7ff00000002" HTTP_KEYTAB = "0502000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc010011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc01001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc020011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc02001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51" SYSHTTP_KEYTAB = "0502000000450001000b544553542e474f4b52423500077379734854545000000001590dc5af020012002043763702868978d1b6d91a36704b987e27e517250055bdfc40b8a6b3848d9aae" SYSHTTP_RESDOM_KEYTAB = "05020000005c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d94070100120020e53945463c231ab747635c96d5fc48f6591ce41cec98ad2620b50f52c2bafa96000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d940701001100103ae5388332dc948e00427332658c537800000001000000540002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d940701001000183da7b93eb698233de3b07f080b07191a49a83d32d3587c8f000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d9407010013001036e58aaaf739aad8bc49115dfd8d304b000000010000005c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d9407010014002055e4bf018dfdd3906f0f84149b6d503a03bdf494ba40f482faf67e7a77b9c05f000000010000004c0002000d524553444f4d2e474f4b5242350004485454500012686f73742e726573646f6d2e676f6b726235000000015a3d94070100170010c050d33acce5fac748f6f26bd686e1c700000001" SYSHTTP_RESGOKRB5_AD_KEYTAB = "0502000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b0100170010c050d33acce5fac748f6f26bd686e1c700000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001100100bed4565fa65bbcc167ee344775339c200000001000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001200209a5faf803b231d69ee7d559be62980cc01b9d4c67d18e42450920b0625a4dd2600000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b01001300103e8f9a29e92595691c9f753312ba4c7e00000001000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b0100140020bcf7aa970b530504cc610eefa4893b5e03a71f9f6962d993a36cf9fc7ba4d7bc00000001000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b0200170010c050d33acce5fac748f6f26bd686e1c700000002000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001100100bed4565fa65bbcc167ee344775339c200000002000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001200209a5faf803b231d69ee7d559be62980cc01b9d4c67d18e42450920b0625a4dd2600000002000000380001000a5245532e474f4b5242350007737973485454500000000159de7b4b02001300103e8f9a29e92595691c9f753312ba4c7e00000002000000480001000a5245532e474f4b5242350007737973485454500000000159de7b4b0200140020bcf7aa970b530504cc610eefa4893b5e03a71f9f6962d993a36cf9fc7ba4d7bc00000002" TEST_AS_REQ = "6a81a63081a3a103020105a20302010aa30e300c300aa10402020095a2020400a48186308183a00703050040000010a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235a320301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a511180f32303137303232303134323530315aa70602040f6755a6a814301202011202011102011002011702011902011a" TEST_AS_REP = "6b8202f3308202efa003020105a10302010ba22e302c302aa103020113a2230421301f301da003020112a1161b14544553542e474f4b524235746573747573657231a30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a582015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010264d3fa49d89b627ed471298846ff92cd8632f657c58fe25322a61fffa32bb7966dc4c44c86a81353def2a11c36c537191406a609147f424a63266c00d02bcc56a27b0969d86ff4352634be9e2a4ac0ad5a36b0b0a3d689f128c0afa97401796e88037a35ad19efaf31d1ed4f3213769c03a58bc90ffac2051db152c0ed0809ad05ffb03aa3afaf731ed85f7a73020cb72355e0de27842dcf7eae3de9f7c14aa237edb25153b217ef3693373bc3cacbebe406910ff9ae9d00b7b08f726cb29a213cb9ad51ba80a8c24fa4b6692a445686889702cfa6ea749bac03e27e982407aca623fbd48586bcf566cfe87e1d9f17a74b1315669c16480f93e9d8782e71a8f11000a682012c30820128a003020112a282011f0482011b99b86153c0393c0e4130628f3e1e0f0a1f034e7e61a111b7fad15884e231c8fd8727e0bc945c9b35be20c57d057c8b09b0de74c53fb38cc15c9a2d483023fc369f5bde4da7324b4732b5a3d9504d92f67026aaa01df4f0138245d2ccb1c5a4014804cf295c7e7e56a867e6cf0c534f667f32da7aa5e700af1461764f1c276a8ff0fbee0e99322fe2059d2321853be09d0956c3afcfd07e3e702646a4678926a77bea20d9aaf3086b6d384821c81900af9013a3519f0e50eab6e1491d72e4ee17c2a44441b2ebc8a796cc3d876e328347dce65f61104e14d4c31532885776c9c8a70186b8b39f928972945c98bd60381ead5448e7ebe93fea308054287ac34b0583b4b9b5e43c5f8518d693ba9eb48a219c27344466b3c693a70462" TEST_TGS_REQ = "6c82038f3082038ba103020105a20302010ca382031a3082031630820245a103020101a282023c048202386e82023430820230a003020105a10302010ea20703050000000000a382015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a28201060482010264d3fa49d89b627ed471298846ff92cd8632f657c58fe25322a61fffa32bb7966dc4c44c86a81353def2a11c36c537191406a609147f424a63266c00d02bcc56a27b0969d86ff4352634be9e2a4ac0ad5a36b0b0a3d689f128c0afa97401796e88037a35ad19efaf31d1ed4f3213769c03a58bc90ffac2051db152c0ed0809ad05ffb03aa3afaf731ed85f7a73020cb72355e0de27842dcf7eae3de9f7c14aa237edb25153b217ef3693373bc3cacbebe406910ff9ae9d00b7b08f726cb29a213cb9ad51ba80a8c24fa4b6692a445686889702cfa6ea749bac03e27e982407aca623fbd48586bcf566cfe87e1d9f17a74b1315669c16480f93e9d8782e71a8f11000a481bc3081b9a003020112a281b10481ae8ae3cb8ac47d77cfc7b0b6bf0d3c5f8fcc6dd569344256a6a40c004fc2d23ebbe6ee0b9e00eccf37e710b7c01a7d2a63bbed6d75f2b230d24d724ef90edad2c5680e7e2436ab1145ff68481673444ebd61e3aef79b9ee05809551672c6c436eb8ac732a7fe78bd8f380e68a541191e3125554e4bab63dcc19ea931c1477366a6039ff7b7e62521ebfeffd6784b6ef0c97f653ac4d8dfb304f3e2e843faab12d838c23f1105f0a281c39325987cb03081caa10402020088a281c10481bea081bb3081b8a1173015a003020110a10e040ce613d8e9d544f0e56c60d3bba2819c308199a003020112a2819104818ec4fabcb1ec2f24e04ef51f9247239b28275653fa5cbc1dc9e747530c597631050fe86a5f3cba2ff54270aa771dcefa87efc8c8604407f84e603f5c01a2d929e18103561c3ffbc3a0cf63340bdd67a0739d4d81989827fc1d3f7f13e9dd5cc2346ca08e26a2aaf6d0102fbef8f7a6ee0a1caae7880e953ea678da619038786122a0b71853e8d0b95f544f8fbd6945a461305fa00703050040810000a20d1b0b544553542e474f4b524235a3233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a511180f32303137303232303032323634325aa706020458a9ab2aa8053003020112" TEST_TGS_REP = "6d82039d30820399a003020105a10302010da281df3081dc3081d9a10402020088a281d00481cda081ca3081c7a081c43081c1a003020112a281b90481b62e2b31bd998e2747a447175bcdbe14b7bc043c747c3289e10cde7403ee685d64f9724055c667ad2d3ce6240b5457ddfe3505a5b4d5d5e35238522ba5d86f1b691f28bbe7290487ffebf7e720ecf772cef061ac2c433cabbd0d5973fbedeb5418c8ba1bb7149dd625a4ff4cb465d9599c9d4cfb899807fa088521fa0e0ed5cfa4dd6d02b2a6854c4feb4ec382de5820134ca7c140be4b0f416ca5ebb328c8a470a55154062b8070683d0897a40277bdc752e94437920aa30d1b0b544553542e474f4b524235a4163014a003020101a10d300b1b09746573747573657231a58201706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020102a28201190482011524db012d81dde2cb3b7b40a35ce4fe17f7898166c7a7534ad73ca761e75316a06504415b1bc62508edb6ea58544a3ac0f98911cd55332442d30e007e0f39efa939b726a5228514984a133d1ba7d93d60fec5b2f7fbb16356c85ad1b0bb7ed6108420514086ea9959037e794b535fa052651385060da83f93acffbde0cb486b2f236f8955bb521ef90bc0a52944d2a8da82389e6861065064cc5178a5a7d302e9950761648726fc4015f8772339ef401fd8327dd335c6692c010f2c31b5ccccbcd83e68f2a1b66f16ca44e0bfb2903801c27ed8dd9a7b2dcea3c2bc91d8055c603f218494b1342490fbb805492d999491c2570b3ab392ba7e62c23659663509838e91b6f560a284889075071c348c701017a0a5e5c7a682010e3082010aa003020112a28201010481fece77ad9ebec9b094b686d4885895a2530471e9a56a93c2a463eaa988932695d77a12b6c02e69edfea6759ed49b029bdef43e446129b80a1ef95b9cea69a382d5c074868e68b676869f0990cad588c6d3589b9bba795ff0572ba648915f68a3b49df1def285645e93f4285a843ead01122c4f00bebefa33ecf6df2584e33da359d3d53e0a9b8ec0f41120a0d612d3a1e4504ce464dddec5e2aa3f0d2b6dae7b8800091760d7a590eaa6d7b33539c1700d9daf22a7c8cc1e108aee75267434997666132f52c08ea7c0c898859214ac4e71e185d3bcec1467ab6e8d91e79ba2b2041d40714da495c79d232d3c4e72a0910b9143fd648fe24bc46ec3416a3c34" CCACHE_TEST = "0504000c00010008000000060000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000001000000010000000b544553542e474f4b5242350000000974657374757365723100000002000000020000000b544553542e474f4b524235000000066b72627467740000000b544553542e474f4b52423500120000002088b94319f2dcd1de20ebd3bf3174778769323bce76ef71fb37a8ba4be93c38df59665b8e59665b8e5967044e5967ad080040c1000000000000000000000000015a6182015630820152a003020105a10d1b0b544553542e474f4b524235a220301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a382011830820114a003020112a103020101a282010604820102ee32bb7e27ad6f71869be098c4002b291f370d26302c87ffa3eb670345a11fc113a9e5ab9e26ea659104b29e2a60c07dda559654c58aaf5f48bbb3bb9a238745861be336a0672554dac9b38126b2929ce9df2add185d1043c6dd89c7308b9def7b98ba7bcdcd1c00eeb5d99e273e1fe53b88c057106ec3dbcf2a86c38a4c1372418f1afb0227975747edf2172e23716ab5f6fa9a2ee5c0d94e9f66936df767498677861926812d1f887de6f44e5ebd93b63fd8313a499372ea9e889620bd0842bc8a8f8a17e5dea328c77b771cfcd49ac7afa4a9c7236efa30fec1b2072255543aee48cd935ece367e08d24f51bea4b407ace8ed7e67a8d5e1cb528eb16c7ebe7ac50000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000000000000030000000c582d4341434845434f4e463a000000156b7262355f6363616368655f636f6e665f646174610000000a666173745f617661696c0000001e6b72627467742f544553542e474f4b52423540544553542e474f4b5242350000000000000000000000000000000000000000000000000000000000000000000000000000037965730000000000000001000000010000000b544553542e474f4b5242350000000974657374757365723100000001000000020000000b544553542e474f4b524235000000044854545000000010686f73742e746573742e676f6b726235001200000020fd325da3f905d743894e828de41b21af7876b6281b66d9e4bb2eefd64078b47659665b8e59665bce5967044e5967ad0800408900000000000000000000000001706182016c30820168a003020105a10d1b0b544553542e474f4b524235a2233021a003020101a11a30181b04485454501b10686f73742e746573742e676f6b726235a382012b30820127a003020112a103020101a282011904820115ad55d79858ce41647e835769b40540bc32ff4debe101217a7a024016697ee5ff758829940ca576905a260732c43c2996d96b83f9bff010fdbfc8f3bff51cef202a956f8d73d18c2c8865553f55229075270f42dca23d7618ff35e578a972d40746398efd478cf4f1094d99371273b3fbe5b95707011b446ff605ea8cb0e6631ea0ffdd7b562b5aa2de5dd455388e1aa18d8a3a8e81dab058e1b223410a752e5ec82797164dabafdbec8eeef7b072304e46d7d15b575f44cce69a368a9004612ba179b41d4655964933f7eb114a457aa1127291fc6d63deb271e5504de6fccca33260645ef5bd1ea301d74a8dbf751aa181ed92f5edb493d68222e1a34892035b88b6fb0ce104db23f7da22a8e73359d9c322b8e1cc00000000" TEST_HTTP_URL = "http://10.80.88.88/index.html" TEST_KDC_ADDR = "10.80.88.88" TEST_KDC = "88" TEST_KDC_LASTEST = "98" TEST_KDC_RESDOM = "188" TEST_KDC_OLD = "78" TEST_KDC_SHORTTICKETS = "58" TEST_KDC_BADADDR = "10.80.88.153" TEST_KDC_AD = "10.80.88.68:88" TEST_KDC_AD_TRUST_USER_DOMAIN = "10.80.88.48:88" TEST_KDC_AD_TRUST_RES_DOMAIN = "10.80.88.49:88" TEST_NS = "10.80.88.88:53" TEST_KRB5CONF = `[libdefaults] default_realm = TEST.GOKRB5 dns_lookup_realm = false dns_lookup_kdc = false ticket_lifetime = 24h forwardable = yes default_tkt_enctypes = aes256-cts-hmac-sha1-96 default_tgs_enctypes = aes256-cts-hmac-sha1-96 noaddresses = false [realms] TEST.GOKRB5 = { kdc = 127.0.0.1:88 admin_server = 127.0.0.1:749 default_domain = test.gokrb5 } RESDOM.GOKRB5 = { kdc = 10.80.88.88:188 admin_server = 127.0.0.1:749 default_domain = resdom.gokrb5 } USER.GOKRB5 = { kdc = 10.80.88.48:88 admin_server = 10.80.88.48:464 default_domain = user.gokrb5 } RES.GOKRB5 = { kdc = 10.80.88.49:88 admin_server = 10.80.88.49:464 default_domain = res.gokrb5 } [domain_realm] .test.gokrb5 = TEST.GOKRB5 test.gokrb5 = TEST.GOKRB5 .resdom.gokrb5 = RESDOM.GOKRB5 resdom.gokrb5 = RESDOM.GOKRB5 .user.gokrb5 = USER.GOKRB5 user.gokrb5 = USER.GOKRB5 .res.gokrb5 = RES.GOKRB5 res.gokrb5 = RES.GOKRB5 ` ) golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/000077500000000000000000000000001362537225700217215ustar00rootroot00000000000000golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Authenticator.go000066400000000000000000000047771362537225700251010ustar00rootroot00000000000000// Package types provides Kerberos 5 data types. package types import ( "crypto/rand" "fmt" "math" "math/big" "time" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/asn1tools" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/asnAppTag" ) // Authenticator - A record containing information that can be shown to have been recently generated using the session key known only by the client and server. // https://tools.ietf.org/html/rfc4120#section-5.5.1 type Authenticator struct { AVNO int `asn1:"explicit,tag:0"` CRealm string `asn1:"generalstring,explicit,tag:1"` CName PrincipalName `asn1:"explicit,tag:2"` Cksum Checksum `asn1:"explicit,optional,tag:3"` Cusec int `asn1:"explicit,tag:4"` CTime time.Time `asn1:"generalized,explicit,tag:5"` SubKey EncryptionKey `asn1:"explicit,optional,tag:6"` SeqNumber int64 `asn1:"explicit,optional,tag:7"` AuthorizationData AuthorizationData `asn1:"explicit,optional,tag:8"` } // NewAuthenticator creates a new Authenticator. func NewAuthenticator(realm string, cname PrincipalName) (Authenticator, error) { seq, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32)) if err != nil { return Authenticator{}, err } t := time.Now().UTC() return Authenticator{ AVNO: iana.PVNO, CRealm: realm, CName: cname, Cksum: Checksum{}, Cusec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)), CTime: t, SeqNumber: seq.Int64(), }, nil } // GenerateSeqNumberAndSubKey sets the Authenticator's sequence number and subkey. func (a *Authenticator) GenerateSeqNumberAndSubKey(keyType int32, keySize int) error { seq, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32)) if err != nil { return err } a.SeqNumber = seq.Int64() //Generate subkey value sk := make([]byte, keySize, keySize) rand.Read(sk) a.SubKey = EncryptionKey{ KeyType: keyType, KeyValue: sk, } return nil } // Unmarshal bytes into the Authenticator. func (a *Authenticator) Unmarshal(b []byte) error { _, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.Authenticator)) return err } // Marshal the Authenticator. func (a *Authenticator) Marshal() ([]byte, error) { b, err := asn1.Marshal(*a) if err != nil { return nil, err } b = asn1tools.AddASNAppTag(b, asnAppTag.Authenticator) return b, nil } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Authenticator_test.go000066400000000000000000000110311362537225700261150ustar00rootroot00000000000000package types import ( "encoding/hex" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/iana/adtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func unmarshalAuthenticatorTest(t *testing.T, v string) Authenticator { var a Authenticator //t.Logf("Starting unmarshal tests of %s", v) b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } return a } func TestUnmarshalAuthenticator(t *testing.T) { t.Parallel() a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator") //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, int32(1), a.Cksum.CksumType, "Checksum type not as expected") assert.Equal(t, []byte("1234"), a.Cksum.Checksum, "Checsum not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, tt, a.CTime, "Client time not as expected") assert.Equal(t, int32(1), a.SubKey.KeyType, "Subkey type not as expected") assert.Equal(t, []byte("12345678"), a.SubKey.KeyValue, "Subkey value not as expected") assert.Equal(t, 2, len(a.AuthorizationData), "Number of Authorization data items not as expected") for i, entry := range a.AuthorizationData { assert.Equal(t, adtype.ADIfRelevant, entry.ADType, fmt.Sprintf("Authorization type of entry %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), entry.ADData, fmt.Sprintf("Authorization data of entry %d not as expected", i+1)) } } func TestUnmarshalAuthenticator_optionalsempty(t *testing.T) { t.Parallel() a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator(optionalsempty)") //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, tt, a.CTime, "Client time not as expected") } func TestUnmarshalAuthenticator_optionalsNULL(t *testing.T) { t.Parallel() a := unmarshalAuthenticatorTest(t, "encode_krb5_authenticator(optionalsNULL)") //Parse the test time value into a time.Time type tt, _ := time.Parse(testdata.TEST_TIME_FORMAT, testdata.TEST_TIME) assert.Equal(t, iana.PVNO, a.AVNO, "Authenticator version number not as expected") assert.Equal(t, testdata.TEST_REALM, a.CRealm, "CRealm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.CName.NameType, "CName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.CName.NameString), "CName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.CName.NameString, "CName entries not as expected") assert.Equal(t, 123456, a.Cusec, "Client microseconds not as expected") assert.Equal(t, tt, a.CTime, "Client time not as expected") } func TestMarshalAuthenticator(t *testing.T) { t.Parallel() var a Authenticator v := "encode_krb5_authenticator" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshal bytes of Authenticator not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/AuthorizationData.go000066400000000000000000000034431362537225700257060ustar00rootroot00000000000000package types import ( "github.com/jcmturner/gofork/encoding/asn1" ) // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.2.6 // AuthorizationData implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6 type AuthorizationData []AuthorizationDataEntry // AuthorizationDataEntry implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6 type AuthorizationDataEntry struct { ADType int32 `asn1:"explicit,tag:0"` ADData []byte `asn1:"explicit,tag:1"` } // ADIfRelevant implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.1 type ADIfRelevant AuthorizationData // ADKDCIssued implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.2 type ADKDCIssued struct { ADChecksum Checksum `asn1:"explicit,tag:0"` IRealm string `asn1:"optional,generalstring,explicit,tag:1"` Isname PrincipalName `asn1:"optional,explicit,tag:2"` Elements AuthorizationData `asn1:"explicit,tag:3"` } // ADAndOr implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.3 type ADAndOr struct { ConditionCount int32 `asn1:"explicit,tag:0"` Elements AuthorizationData `asn1:"explicit,tag:1"` } // ADMandatoryForKDC implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.6.4 type ADMandatoryForKDC AuthorizationData // Unmarshal bytes into the ADKDCIssued. func (a *ADKDCIssued) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } // Unmarshal bytes into the AuthorizationData. func (a *AuthorizationData) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } // Unmarshal bytes into the AuthorizationDataEntry. func (a *AuthorizationDataEntry) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/AuthorizationData_test.go000066400000000000000000000041771362537225700267520ustar00rootroot00000000000000package types import ( "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/adtype" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalAuthorizationData(t *testing.T) { t.Parallel() var a AuthorizationData v := "encode_krb5_authorization_data" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, 2, len(a), "Number of authorization data entries not as expected") for i, entry := range a { assert.Equal(t, adtype.ADIfRelevant, entry.ADType, fmt.Sprintf("Authorization data type of entry %d not as expected", i+1)) assert.Equal(t, []byte("foobar"), entry.ADData, fmt.Sprintf("Authorization data of entry %d not as expected", i+1)) } } func TestUnmarshalAuthorizationData_kdcissued(t *testing.T) { t.Parallel() var a ADKDCIssued v := "encode_krb5_ad_kdcissued" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, int32(1), a.ADChecksum.CksumType, "Checksum type not as expected") assert.Equal(t, []byte("1234"), a.ADChecksum.Checksum, "Checksum not as expected") assert.Equal(t, testdata.TEST_REALM, a.IRealm, "Issuing realm not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.Isname.NameType, "Issuing name type not as expected") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.Isname.NameString, "Issuing name string entries not as expected") assert.Equal(t, 2, len(a.Elements), "Number of authorization data elements not as expected") for i, ele := range a.Elements { assert.Equal(t, adtype.ADIfRelevant, ele.ADType, fmt.Sprintf("Authorization data type of element %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_AUTHORIZATION_DATA_VALUE), ele.ADData, fmt.Sprintf("Authorization data of element %d not as expected", i+1)) } } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Cryptosystem.go000066400000000000000000000025651362537225700250050ustar00rootroot00000000000000package types import ( "github.com/jcmturner/gofork/encoding/asn1" ) // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.2.9 // EncryptedData implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9 type EncryptedData struct { EType int32 `asn1:"explicit,tag:0"` KVNO int `asn1:"explicit,optional,tag:1"` Cipher []byte `asn1:"explicit,tag:2"` } // EncryptionKey implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9 // AKA KeyBlock type EncryptionKey struct { KeyType int32 `asn1:"explicit,tag:0"` KeyValue []byte `asn1:"explicit,tag:1"` } // Checksum implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.9 type Checksum struct { CksumType int32 `asn1:"explicit,tag:0"` Checksum []byte `asn1:"explicit,tag:1"` } // Unmarshal bytes into the EncryptedData. func (a *EncryptedData) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } // Marshal the EncryptedData. func (a *EncryptedData) Marshal() ([]byte, error) { edb, err := asn1.Marshal(*a) if err != nil { return edb, err } return edb, nil } // Unmarshal bytes into the EncryptionKey. func (a *EncryptionKey) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } // Unmarshal bytes into the Checksum. func (a *Checksum) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/Cryptosystem_test.go000066400000000000000000000056731362537225700260470ustar00rootroot00000000000000package types import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalEncryptedData(t *testing.T) { t.Parallel() var a EncryptedData v := "encode_krb5_enc_data" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected") assert.Equal(t, iana.PVNO, a.KVNO, "Encrypted data KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected") } func TestUnmarshalEncryptedData_MSBsetkvno(t *testing.T) { t.Parallel() var a EncryptedData v := "encode_krb5_enc_data(MSB-setkvno)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected") assert.Equal(t, -16777216, a.KVNO, "Encrypted data KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected") } func TestUnmarshalEncryptedData_kvno_neg1(t *testing.T) { t.Parallel() var a EncryptedData v := "encode_krb5_enc_data(kvno= -1)" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, testdata.TEST_ETYPE, a.EType, "Encrypted data Etype not as expected") assert.Equal(t, -1, a.KVNO, "Encrypted data KVNO not as expected") assert.Equal(t, []byte(testdata.TEST_CIPHERTEXT), a.Cipher, "Ecrypted data ciphertext not as expected") } func TestUnmarshalEncryptionKey(t *testing.T) { t.Parallel() var a EncryptionKey v := "encode_krb5_keyblock" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, int32(1), a.KeyType, "Key type not as expected") assert.Equal(t, []byte("12345678"), a.KeyValue, "Key value not as expected") } func TestMarshalEncryptedData(t *testing.T) { t.Parallel() var a EncryptedData v := "encode_krb5_enc_data" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } mb, err := a.Marshal() if err != nil { t.Fatalf("Marshal of ticket errored: %v", err) } assert.Equal(t, b, mb, "Marshal bytes of Encrypted Data not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/HostAddress.go000066400000000000000000000100651362537225700244750ustar00rootroot00000000000000package types // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.2.5 import ( "bytes" "fmt" "net" "github.com/jcmturner/gofork/encoding/asn1" "gopkg.in/jcmturner/gokrb5.v5/iana/addrtype" ) // HostAddresses implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5 type HostAddresses []HostAddress // HostAddress implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.2.5 type HostAddress struct { AddrType int32 `asn1:"explicit,tag:0"` Address []byte `asn1:"explicit,tag:1"` } // GetHostAddress returns a HostAddress struct from a string in the format : func GetHostAddress(s string) (HostAddress, error) { var h HostAddress cAddr, _, err := net.SplitHostPort(s) if err != nil { return h, fmt.Errorf("invalid format of client address: %v", err) } ip := net.ParseIP(cAddr) hb, err := ip.MarshalText() if err != nil { return h, fmt.Errorf("could not marshal client's address into bytes: %v", err) } var ht int32 if ip.To4() != nil { ht = addrtype.IPv4 } else if ip.To16() != nil { ht = addrtype.IPv6 } else { return h, fmt.Errorf("could not determine client's address types: %v", err) } h = HostAddress{ AddrType: ht, Address: hb, } return h, nil } // GetAddress returns a string representation of the HostAddress. func (h *HostAddress) GetAddress() (string, error) { var b []byte _, err := asn1.Unmarshal(h.Address, &b) return string(b), err } // LocalHostAddresses returns a HostAddresses struct for the local machines interface IP addresses. func LocalHostAddresses() (ha HostAddresses, err error) { ifs, err := net.Interfaces() if err != nil { return } for _, iface := range ifs { if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 { // Interface is either loopback of not up continue } addrs, err := iface.Addrs() if err != nil { continue } for _, addr := range addrs { var ip net.IP switch v := addr.(type) { case *net.IPNet: ip = v.IP case *net.IPAddr: ip = v.IP } var a HostAddress if ip.To16() == nil { //neither IPv4 or IPv6 continue } if ip.To4() != nil { //Is IPv4 a.AddrType = addrtype.IPv4 a.Address = ip.To4() } else { a.AddrType = addrtype.IPv6 a.Address = ip.To16() } ha = append(ha, a) } } return ha, nil } // HostAddressesFromNetIPs returns a HostAddresses type from a slice of net.IP func HostAddressesFromNetIPs(ips []net.IP) (ha HostAddresses) { for _, ip := range ips { ha = append(ha, HostAddressFromNetIP(ip)) } return ha } // HostAddressFromNetIP returns a HostAddress type from a net.IP func HostAddressFromNetIP(ip net.IP) HostAddress { if ip.To4() != nil { //Is IPv4 return HostAddress{ AddrType: addrtype.IPv4, Address: ip.To4(), } } return HostAddress{ AddrType: addrtype.IPv6, Address: ip.To16(), } } // HostAddressesEqual tests if two HostAddress slices are equal. func HostAddressesEqual(h, a []HostAddress) bool { if len(h) != len(a) { return false } for _, e := range a { var found bool for _, i := range h { if e.Equal(i) { found = true break } } if !found { return false } } return true } // HostAddressesContains tests if a HostAddress is contained in a HostAddress slice. func HostAddressesContains(h []HostAddress, a HostAddress) bool { for _, e := range h { if e.Equal(a) { return true } } return false } // Equal tests if the HostAddress is equal to another HostAddress provided. func (h *HostAddress) Equal(a HostAddress) bool { if h.AddrType != a.AddrType { return false } return bytes.Equal(h.Address, a.Address) } // Contains tests if a HostAddress is contained within the HostAddresses struct. func (h *HostAddresses) Contains(a HostAddress) bool { for _, e := range *h { if e.Equal(a) { return true } } return false } // Equal tests if a HostAddress slice is equal to the HostAddresses struct. func (h *HostAddresses) Equal(a []HostAddress) bool { if len(*h) != len(a) { return false } for _, e := range a { if !h.Contains(e) { return false } } return true } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/KerberosFlags.go000066400000000000000000000030231362537225700247770ustar00rootroot00000000000000package types // Reference: https://www.ietf.org/rfc/rfc4120.txt // Section: 5.2.8 import ( "github.com/jcmturner/gofork/encoding/asn1" ) // NewKrbFlags returns an ASN1 BitString struct of the right size for KrbFlags. func NewKrbFlags() asn1.BitString { f := asn1.BitString{} f.Bytes = make([]byte, 4) f.BitLength = len(f.Bytes) * 8 return f } // SetFlags sets the flags of an ASN1 BitString. func SetFlags(f *asn1.BitString, j []int) { for _, i := range j { SetFlag(f, i) } } // SetFlag sets a flag in an ASN1 BitString. func SetFlag(f *asn1.BitString, i int) { for l := len(f.Bytes); l < 4; l++ { (*f).Bytes = append((*f).Bytes, byte(0)) (*f).BitLength = len((*f).Bytes) * 8 } //Which byte? b := int(i / 8) //Which bit in byte p := uint(7 - (i - 8*b)) (*f).Bytes[b] = (*f).Bytes[b] | (1 << p) } // UnsetFlags unsets flags in an ASN1 BitString. func UnsetFlags(f *asn1.BitString, j []int) { for _, i := range j { UnsetFlag(f, i) } } // UnsetFlag unsets a flag in an ASN1 BitString. func UnsetFlag(f *asn1.BitString, i int) { for l := len(f.Bytes); l < 4; l++ { (*f).Bytes = append((*f).Bytes, byte(0)) (*f).BitLength = len((*f).Bytes) * 8 } //Which byte? b := int(i / 8) //Which bit in byte p := uint(7 - (i - 8*b)) (*f).Bytes[b] = (*f).Bytes[b] &^ (1 << p) } // IsFlagSet tests if a flag is set in the ASN1 BitString. func IsFlagSet(f *asn1.BitString, i int) bool { //Which byte? b := int(i / 8) //Which bit in byte p := uint(7 - (i - 8*b)) if (*f).Bytes[b]&(1</@ // a PrincipalName type will be returned with the name type set to KRB_NT_PRINCIPAL(1) // and the realm will be returned as a string. If the "@" suffix // is not included in the SPN then the value of realm string returned will be "" func ParseSPNString(spn string) (pn PrincipalName, realm string) { if strings.Contains(spn, "@") { s := strings.Split(spn, "@") realm = s[len(s)-1] spn = strings.TrimSuffix(spn, "@"+realm) } pn = NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn) return } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/PrincipalName_test.go000066400000000000000000000030461362537225700260340ustar00rootroot00000000000000package types import ( "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/nametype" "testing" ) func TestPrincipalName_GetSalt(t *testing.T) { t.Parallel() pn := PrincipalName{ NameType: 1, NameString: []string{"firststring", "secondstring"}, } assert.Equal(t, "TEST.GOKRB5firststringsecondstring", pn.GetSalt("TEST.GOKRB5"), "Principal name default salt not as expected") } func TestParseSPNString(t *testing.T) { pn, realm := ParseSPNString("HTTP/www.example.com@REALM.COM") assert.Equal(t, "REALM.COM", realm, "realm value not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, pn.NameType, "name type not as expected") assert.Equal(t, "HTTP", pn.NameString[0], "first element of name string not as expected") assert.Equal(t, "www.example.com", pn.NameString[1], "second element of name string not as expected") pn, realm = ParseSPNString("HTTP/www.example.com") assert.Equal(t, "", realm, "realm value not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, pn.NameType, "name type not as expected") assert.Equal(t, "HTTP", pn.NameString[0], "first element of name string not as expected") assert.Equal(t, "www.example.com", pn.NameString[1], "second element of name string not as expected") pn, realm = ParseSPNString("www.example.com@REALM.COM") assert.Equal(t, "REALM.COM", realm, "realm value not as expected") assert.Equal(t, nametype.KRB_NT_PRINCIPAL, pn.NameType, "name type not as expected") assert.Equal(t, "www.example.com", pn.NameString[0], "second element of name string not as expected") } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/TypedData.go000066400000000000000000000010561362537225700241310ustar00rootroot00000000000000package types import "github.com/jcmturner/gofork/encoding/asn1" // TypedData implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.9.1 type TypedData struct { DataType int32 `asn1:"explicit,tag:0"` DataValue []byte `asn1:"optional,explicit,tag:1"` } // TypedDataSequence implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.9.1 type TypedDataSequence []TypedData // Unmarshal bytes into the TypedDataSequence. func (a *TypedDataSequence) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } golang-gopkg-jcmturner-gokrb5.v5-5.3.0+dfsg/types/TypedData_test.go000066400000000000000000000015461362537225700251740ustar00rootroot00000000000000package types import ( "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/assert" "gopkg.in/jcmturner/gokrb5.v5/iana/patype" "gopkg.in/jcmturner/gokrb5.v5/testdata" ) func TestUnmarshalTypedData(t *testing.T) { t.Parallel() var a TypedDataSequence v := "encode_krb5_typed_data" b, err := hex.DecodeString(testdata.TestVectors[v]) if err != nil { t.Fatalf("Test vector read error of %s: %v\n", v, err) } err = a.Unmarshal(b) if err != nil { t.Fatalf("Unmarshal error of %s: %v\n", v, err) } assert.Equal(t, 2, len(a), "Number of typed data elements not as expected") for i, d := range a { assert.Equal(t, patype.PA_SAM_RESPONSE, d.DataType, fmt.Sprintf("Data type of element %d not as expected", i+1)) assert.Equal(t, []byte(testdata.TEST_PADATA_VALUE), d.DataValue, fmt.Sprintf("Data value of element %d not as expected", i+1)) } }