diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2023-03-09 09:00:02 +1300 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2023-03-31 01:48:30 +0000 |
commit | a87aae5292d1c43b987dcfa77a51b6aa5aa3e004 (patch) | |
tree | c1af6a7ddb6a196d4af09cd086815be51a709365 /third_party/heimdal | |
parent | f448a1649cf4af11f1ceba55ec62e9b2a3db24f1 (diff) | |
download | samba-a87aae5292d1c43b987dcfa77a51b6aa5aa3e004.tar.gz |
third_party/heimdal: Import lorikeet-heimdal-202303200103 (commit 2ee541b5e963f7cffb1ec4acd1a8cc45426a9f28)
NOTE: THIS COMMIT WON'T COMPILE/WORK ON ITS OWN!
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'third_party/heimdal')
235 files changed, 5973 insertions, 2188 deletions
diff --git a/third_party/heimdal/.github/workflows/coverity.yml b/third_party/heimdal/.github/workflows/coverity.yml index 5a175f52a8c..dfea060d04b 100644 --- a/third_party/heimdal/.github/workflows/coverity.yml +++ b/third_party/heimdal/.github/workflows/coverity.yml @@ -8,7 +8,7 @@ on: jobs: linux: - if: secrets.COVERITY_SCAN_TOKEN != '' + #if: ${{ secrets.COVERITY_SCAN_TOKEN }} != '' runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -32,8 +32,9 @@ jobs: - name: Download Coverity Build Tool env: TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} run: | - wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=ruby" -O cov-analysis-linux64.tar.gz + wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=$PROJECT" -O cov-analysis-linux64.tar.gz mkdir cov-analysis-linux64 tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 - name: Build @@ -43,6 +44,7 @@ jobs: CONFIGURE_OPTS: ${{ matrix.configureopts }} run: | /bin/sh ./autogen.sh + export PATH="$PWD/cov-analysis-linux64/bin:$PATH" mkdir build cd build ../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="-Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" @@ -50,19 +52,17 @@ jobs: # We don't want to scan-build libedit nor SQLite3 because ETOOSLOW (cd lib/libedit && make -j4) (cd lib/sqlite && make -j4) - export PATH=`pwd`/cov-analysis-linux64/bin:$PATH cov-build --dir cov-int make -j4 + tar czvf ../heimdal.tgz cov-int - name: Submit the result to Coverity Scan env: TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} run: | - tar czvf heimdal.tgz cov-int curl \ - --form project=ruby \ - --form token=$TOKEN \ - --form email=$EMAIL \ - --form file=@heimdal.tgz \ - --form version=trunk \ - --form description="`./ruby -v`" "https://scan.coverity.com/builds?project=$PROJECT" + --form "token=$TOKEN" \ + --form "email=$EMAIL" \ + --form "file=@heimdal.tgz" \ + --form version="$(git rev-parse HEAD)" \ + --form description="$GITHUB_REF / $GITHUB_SHA" "https://scan.coverity.com/builds?project=$PROJECT" diff --git a/third_party/heimdal/.github/workflows/linux-mit-interop.yml b/third_party/heimdal/.github/workflows/linux-mit-interop.yml new file mode 100644 index 00000000000..72648ee2ce0 --- /dev/null +++ b/third_party/heimdal/.github/workflows/linux-mit-interop.yml @@ -0,0 +1,117 @@ +name: Linux Build + +on: + push: + branches: + - 'interop-mit*' + +jobs: + unix: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + name: [linux-clang] + include: + - name: linux-clang + os: ubuntu-18.04 + compiler: clang + cflags: '' + steps: + - name: Clone repository + uses: actions/checkout@v1 + - name: Checkout MIT + uses: actions/checkout@v3 + with: + repository: krb5/krb5 + path: mit + - name: Install Heimdal dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y bison comerr-dev flex doxygen + sudo apt-get install -y libcap-ng-dev libdb-dev libedit-dev libjson-perl + sudo apt-get install -y libldap2-dev libncurses5-dev libperl4-corelibs-perl + sudo apt-get install -y libsqlite3-dev libkeyutils-dev pkg-config python + sudo apt-get install -y ss-dev texinfo unzip netbase keyutils ldap-utils + sudo apt-get install -y gdb apport curl libmicrohttpd-dev jq valgrind + - name: Install MIT Kerberos dependencies + run: | + sudo apt-get install -y gettext libcmocka-dev libresolv-wrapper libsasl2-dev libssl-dev python3-kdcproxy python3-pip slapd tcsh + pip3 install pyrad + # Temporary workaround for: + # https://github.com/actions/virtual-environments/issues/3185 + sudo hostname localhost + - name: Build MIT + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + cd mit/src + autoreconf + ./configure --enable-maintainer-mode --with-ldap --with-crypto-impl=openssl --prefix=$HOME/mitkrb5 + make -j4 $MAKEVARS + make install + - name: Build + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + /bin/sh ./autogen.sh + mkdir build + cd build + ../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="${{ matrix.cflags }} -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" --with-mitkrb5=$HOME/mitkrb5 + make -j4 + - name: Test + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + cd build + ulimit -c unlimited + make check + - name: Make Install + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + cd build || true + make DESTDIR=/tmp/h5l install + cd /tmp/h5l + tar czf $HOME/heimdal-install-linux-${{ matrix.compiler }}.tgz . + - name: Core dump stacks + run: | + echo "thread apply all bt" > /tmp/x + find . -name core -print | while read core; do gdb -batch -x x `file "$core"|sed -e "s/^[^']*'//" -e "s/[ '].*$//"` "$core"; done + if [ "$(find . -name core -print | wc -l)" -gt 0 ]; then false; fi + - name: Test logs + run: | + find build -depth -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | tar -czf $HOME/logs-linux-${{ matrix.compiler }}.tgz --verbatim-files-from --files-from - + find build -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | xargs cat + - name: Failed Test logs + if: ${{ failure() }} + run: | + find build -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | xargs cat + - name: Make Dist + run: | + cd build + make dist + make distclean + if [ "$(git ls-files -o|grep -v ^build/ | wc -l)" -ne 0 ]; then + echo "Files not removed by make distclean:" + git ls-files -o|grep -v ^build/ + fi + - name: Upload Install Tarball + uses: actions/upload-artifact@v2 + with: + name: Install Tarball + path: '~/heimdal-install-linux-${{ matrix.compiler }}.tgz' + - name: Upload Dist Tarball + uses: actions/upload-artifact@v2 + with: + name: Dist Tarball + path: 'build/heimdal-*.tar.gz' + - name: Upload Logs Tarball + uses: actions/upload-artifact@v2 + with: + name: Test Logs + path: '~/logs-linux-${{ matrix.compiler }}.tgz' diff --git a/third_party/heimdal/.github/workflows/ubsan.yml b/third_party/heimdal/.github/workflows/ubsan.yml new file mode 100644 index 00000000000..d4bb2eda476 --- /dev/null +++ b/third_party/heimdal/.github/workflows/ubsan.yml @@ -0,0 +1,133 @@ +name: Linux UBSAN Build + +on: + push: + branches: + - 'master' + - 'ubsan' + paths: + - '!docs/**' + - '!**.md' + - '!**.[1-9]' + - '**.[chly]' + - '**.hin' + - '**.in' + - '**.am' + - '**.m4' + - '**.ac' + - '**.pl' + - '**.py' + - '**.asn1' + - '**.opt' + - '**/COPYING' + - '**/INSTALL' + - '**/README*' + - '.github/workflows/ubsan.yml' + - '!appveyor.yml' + - '!.travis.yml' + + pull_request: + paths: + - '!docs/**' + - '!**.md' + - '!**.[1-9]' + - '**.[chly]' + - '**.hin' + - '**.in' + - '**.am' + - '**.m4' + - '**.ac' + - '**.pl' + - '**.py' + - '**.asn1' + - '**.opt' + - '**/COPYING' + - '**/INSTALL' + - '**/README*' + - '.github/workflows/ubsan.yml' + - '!appveyor.yml' + - '!.travis.yml' + +jobs: + unix: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + name: [linux-clang, linux-gcc] + include: + - name: linux-clang + os: ubuntu-18.04 + compiler: clang + cflags: '-fsanitize=undefined' + ldflags: '' + - name: linux-gcc + os: ubuntu-18.04 + compiler: gcc + cflags: '-Wnonnull -fsanitize=undefined' + ldflags: '' + steps: + - name: Clone repository + uses: actions/checkout@v1 + - name: Install packages + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo apt-get update -qq + sudo apt-get install -y bison comerr-dev flex doxygen + sudo apt-get install -y libcap-ng-dev libdb-dev libedit-dev libjson-perl + sudo apt-get install -y libldap2-dev libncurses5-dev libperl4-corelibs-perl + sudo apt-get install -y libsqlite3-dev libkeyutils-dev pkg-config python + sudo apt-get install -y ss-dev texinfo unzip netbase keyutils ldap-utils + sudo apt-get install -y gdb apport curl libmicrohttpd-dev jq valgrind + # Temporary workaround for: + # https://github.com/actions/virtual-environments/issues/3185 + sudo hostname localhost + - name: Build + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + /bin/sh ./autogen.sh + mkdir build + cd build + ../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --enable-dynamic --disable-static --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="${{ matrix.cflags }} -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" LDFLAGS="${{ matrix.ldflags }}" + make -j4 + - name: Test + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + cd build + ulimit -c unlimited + make check + - name: Make Install + env: + CC: ${{ matrix.compiler }} + MAKEVARS: ${{ matrix.makevars }} + run: | + cd build || true + make DESTDIR=/tmp/h5l install + cd /tmp/h5l + tar czf $HOME/heimdal-install-linux-${{ matrix.compiler }}.tgz . + - name: Core dump stacks + run: | + echo "thread apply all bt" > /tmp/x + find . -name core -print | while read core; do gdb -batch -x x `file "$core"|sed -e "s/^[^']*'//" -e "s/[ '].*$//"` "$core"; done + if [ "$(find . -name core -print | wc -l)" -gt 0 ]; then false; fi + - name: Test logs + run: | + find build -depth -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | tar -czf $HOME/logs-linux-${{ matrix.compiler }}.tgz --verbatim-files-from --files-from - + find build -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | xargs cat + - name: Failed Test logs + if: ${{ failure() }} + run: | + find build -name \*.trs | xargs grep -lw FAIL | sed -e 's/trs$/log/' | xargs cat + - name: All Test logs + if: true + run: | + find build -name \*.trs | sed -e 's/trs$/log/' | xargs cat + - name: Upload Logs Tarball + uses: actions/upload-artifact@v2 + with: + name: Test Logs + path: '~/logs-linux-${{ matrix.compiler }}.tgz' diff --git a/third_party/heimdal/NEWS b/third_party/heimdal/NEWS index 79efe803a78..4bb5a703778 100644 --- a/third_party/heimdal/NEWS +++ b/third_party/heimdal/NEWS @@ -1,3 +1,345 @@ + +Partial news for a future Heimdal 8.0 release -- but NOTE WELL that this is NOT +a release at this time! + +Bug fixes + + - Errors found by the Coverity static analysis. + - Errors found by the LLVM scan-build static analyzer. + - Errors found by the valgrind memory debugger. + - Fix out-of-tree SQLite3 ccache permissions / umask issues. + - iprop bugs, race conditions, and performance + - Many misc. bugs + +Features: + + - KDC: Add FAST support for TGS. + - KDC: Greatly improved plugin facility for Samba. + - KDC: Add httpkadmind service providing a subset of kadmin + functionality over HTTP. + - KDC: Add support for virtual service principal namespaces. + - KDC: Add support for synthetic client principals that exist if the + pre-authentication mechanism (e.g., PKINIT) can authenticate + them, thus not requiring an HDB entry. + - KDC: Add experimental GSS-API pre-authentication support. + - KDC: Revamp and enhance kx509 support (though bx509d mostly replaces kx509). + - KDC: Better support for aliases and referrals. + - KDC: Always return the salt in the PA-ETYPE-INFO[2]. + - KDC: Add warn_ticket_addresses configuration parameter. + - KDC: allow anonymous AS requests with long-term keys. + - KDC: Do not include PAC for anonymous AS requests. + - KDC: Enable keepalive mode on incoming sockets. + - KDC: Greatly improved logging. + - KDC: Remove KRB5SignedPath, to be replaced with PAC. + - PKIX: Add bx509d -- an online certification authority (CA) with an HTTP API. + - kadmin: Add HTTP-based kadmin protocol. + - kadmin: Add add_alias, del_alias. + - kadmin: Add command aliases to man page. + - kadmin: Add disallow-client attribute. + - kadmin: add --hdb / -H argument. + - kadmin: Allow enforcing password quality on admin password change. + - kadmin: Improve ext_keytab usage. + - kadmin: Selective pruning of historic key for principal. + - krb5: Add client_aware_channel_bindings option. + - krb5: Add constrained credential delegation option "destination TGT" + - krb5: Add "EFILE:" target for logging. + - krb5: Add include/includedir directives for krb5.conf. + - krb5: Complete DIR ccache collection support. + - krb5: Add FILE ccache collection support. + - krb5: Improved FILE ccache performance. + - krb5: Add KEYRING ccache support. + - krb5: Add kx509 client. + - krb5: Improve FILE keytab performance. + - krb5: Implement KRB5_TRACE environment variable. + - krb5: Add experimental name canonicalization rules configuration. + - krb5: Support start_realm ccconfig entry type. + - kinit: Add --default-for option for ccache collection support. + - kinit: Add --pk-anon-fast-armor option. + - kinit: Don't leave dangling temporary ccaches. + - klist: Better --json + - iprop: Many performance and scaling enhancements. + - iprop: Support hierarchical propagation. + - ASN.1: Document fuzzing process. + - ASN.1: Complete template backend. + - ASN.1: Add partial Information Object System support (template backend + only). This means that open type holes can be decoded recursively + with one codec function call. + - ASN.1: Add JSON encoder functionality (template backend only). + - ASN.1: Greatly enhanced asn1_print(1) command, which can now print a + JSON representation of any DER-encoded value of any type exported + by ASN.1 modules in Heimdal. + - ASN.1: Support circular types. + - ASN.1: Topographically sort declarations. + - ASN.1: Proper support for IMPLICIT tags. + - GSS: Import gss-token(1) command. + - GSS: Add advanced credential store / load functionality. + - GSS: Add name attributes support, with support for many basic attributes + and PAC buffer accessors too. + - GSS: Add SANON mechanism for anonymous-only key exchange using + elliptic curve Diffie-Hellman (ECDH) with Curve25519. + - GSS: Add gss_acquire_cred_from() and credential store extensions. + - GSS: Support fragmented tokens reassembly (for SMB). + - GSS: Support client keytab. + - GSS: Add NegoEx support. + - libhx509: Lots of improvements. + - hxtool: Add "acert" (assert cert contents) command + - hxtool: add cert type: https-negotiate-server + - hxtool: add generate-key command + - hxtool: Add OID symbol resolution and printing of OIDs known to hxtool. + - hxtool: Add print --raw-json option that shows certificates in JSON, with + all extensions and attributes known to Heimdal fully decoded. + - hxtool: Improved SAN support. + - hxtool: Improved CSR support. + - Improved plugin interfaces. + - hcrypto: Add X25519. + - hcrypto: Better RSA key generation. + - hcrypto: import libtommath v1.2.0. + - roken: Add secure_getenv() and issuid(), use them extensively. + +Release Notes - Heimdal - Version Heimdal 7.8 + + Bug fixes + + - CVE-2022-42898 PAC parse integer overflows + + - CVE-2022-3437 Overflows and non-constant time leaks in DES{,3} and arcfour + - Pass correct length to _gssapi_verify_pad() + - Check for overflow in _gsskrb5_get_mech() + - Check buffer length against overflow for DES{,3} unwrap + - Check the result of _gsskrb5_get_mech() + - Avoid undefined behaviour in _gssapi_verify_pad() + - Don't pass NULL pointers to memcpy() in DES unwrap + - Use constant-time memcmp() in unwrap_des3() + - Use constant-time memcmp() for arcfour unwrap + + - CVE-2021-44758 NULL dereference DoS in SPNEGO acceptors + + - CVE-2022-44640 Heimdal KDC: invalid free in ASN.1 codec + + This is a 10.0 on the Common Vulnerability Scoring System (CVSS) v3. + + Heimdal's ASN.1 compiler generates code that allows specially + crafted DER encodings of CHOICEs to invoke the wrong free function + on the decoded structure upon decode error. This is known to impact + the Heimdal KDC, leading to an invalid free() of an address partly + or wholly under the control of the attacker, in turn leading to a + potential remote code execution (RCE) vulnerability. + + This error affects the DER codec for all CHOICE types used in + Heimdal, though not all cases will be exploitable. We have not + completed a thorough analysis of all the Heimdal components + affected, thus the Kerberos client, the X.509 library, and other + parts, may be affected as well. + + This bug has been in Heimdal since 2005. It was first reported by + Douglas Bagnall, though it had been found independently by the + Heimdal maintainers via fuzzing. + + While no zero-day exploit is known, such an exploit will likely be + available soon after public disclosure. + + - Errors found by the LLVM scan-build static analyzer. + + - Errors found by the valgrind memory debugger. + + - Work around GCC Bug 95189 (memcmp wrongly stripped like strcmp). + + - Fix Unicode normalization read of 1 bytes past end of array. + + - Correct ASN.1 OID typo for SHA-384 + + - Fix a deadlock in in the MEMORY ccache type. + + - TGS: strip forwardable and proxiable flags if the server is + disallowed. + + - CVE-2019-14870: Validate client attributes in protocol-transition + - CVE-2019-14870: Apply forwardable policy in protocol-transition + - CVE-2019-14870: Always lookup impersonate client in DB + + - Incremental HDB propagation improvements + + - Refactor send_diffs making it progressive + - Handle partial writes on non-blocking sockets + - Disable Nagle in iprop master and slave + - Use async I/O + - Don't send I_HAVE in response to AYT + - Do not recover log in kadm5_get_principal() + - Don't send diffs to slaves with not yet known version + - Don't stutter in send_diffs + + - Optional backwards-compatible anon-pkinit behaviour + +Release Notes - Heimdal - Version Heimdal 7.7 + + Bug fixes + + - PKCS#11 hcrypto back-end + . initialize the p11_module_load function list + . verify that not only is a mechanism present but that its mechanism + info states that it offers the required encryption, decryption or + digest services + - krb5: + . Starting with 7.6, Heimdal permitted requesting authenticated + anonymous tickets. However, it did not verify that a KDC in fact + returned an anonymous ticket when one was requested. + - Cease setting the KDCOption reaquest_anonymous flag when issuing + S4UProxy (constrained delegation) TGS requests. + . when the Win2K PKINIT compatibility option is set, do + not require krbtgt otherName to match when validating KDC + certificate. + . set PKINIT_BTMM flag per Apple implementation + . use memset_s() instead of memset() + - kdc: + . When generating KRB5SignedPath in the AS, use the reply client name + rather than the one from the request, so validation will work + correctly in the TGS. + . allow checksum of PA-FOR-USER to be HMAC_MD5. Even if tgt used + an enctype with a different checksum. Per [MS-SFU] 2.2.1 + PA-FOR-USER the checksum is always HMAC_MD5, and that's what + Windows and MIT clients send. + + In heimdal both the client and kdc use instead the + checksum of the tgt, and therefore work with each other + but Windows and MIT clients fail against heimdal KDC. + + Both Windows and MIT KDCs would allow any keyed checksum + to be used so Heimdal client interoperates with them. + + Change Heimdal KDC to allow HMAC_MD5 even for non RC4 + based tgt in order to support per-spec clients. + . use memset_s() instead of memset(). + - Detect Heimdal 1.0 through 7.6 clients that issue S4UProxy + (constrained delegation) TGS Requests with the request + anonymous flag set. These requests will be treated as + S4UProxy requests and not anonymous requests. + - HDB: + . Set SQLite3 backend default page size to 8KB. + . Add hdb_set_sync() method + - kadmind: + . disable HDB sync during database load avoiding unnecessary disk i/o. + - ipropd: + . disable HDB sync during receive_everything. Doing an fsync + per-record when receiving the complete HDB is a performance + disaster. Among other things, if the HDB is very large, then + one slave receving a full HDB can cause other slaves to timeout + and, if HDB write activity is high enough to cause iprop log + truncation, then also need full syncs, which leads to a cycle of + full syncs for all slaves until HDB write activity drops. + Allowing the iprop log to be larger helps, but improving + receive_everything() performance helps even more. + - kinit: + . Anonymous PKINIT tickets discard the realm information used + to locate the issuing AS. Store the issuing realm in the + credentials cache in order to locate a KDC which can renew them. + . Do not leak the result of krb5_cc_get_config() when determining + anonymous PKINIT start realm. + - klist: + . Show transited-policy-checked, ok-as-delegate and anonymous + flags when listing credentials. + - tests: + . Regenerate certs so that they expire before the 2038 armageddon + so the test suite will pass on 32-bit operating systems until the + underlying issues can be resolved. + - Solaris: + . Define _STDC_C11_BCI for memset_s prototype + - build tooling: + . Convert from python 2 to python 3 + - documentation + . rename verify-password to verify-password-quality + . hprop default mode is encrypt + . kadmind "all" permission does not include "get-keys" + . verify-password-quality might not be stateless + +Release Notes - Heimdal - Version Heimdal 7.6 + + Security + + - CVE-2018-16860 Heimdal KDC: Reject PA-S4U2Self with unkeyed checksum + + When the Heimdal KDC checks the checksum that is placed on the + S4U2Self packet by the server to protect the requested principal + against modification, it does not confirm that the checksum + algorithm that protects the user name (principal) in the request + is keyed. This allows a man-in-the-middle attacker who can + intercept the request to the KDC to modify the packet by replacing + the user name (principal) in the request with any desired user + name (principal) that exists in the KDC and replace the checksum + protecting that name with a CRC32 checksum (which requires no + prior knowledge to compute). + + This would allow a S4U2Self ticket requested on behalf of user + name (principal) user@EXAMPLE.COM to any service to be changed + to a S4U2Self ticket with a user name (principal) of + Administrator@EXAMPLE.COM. This ticket would then contain the + PAC of the modified user name (principal). + + - CVE-2019-12098, client-only: + + RFC8062 Section 7 requires verification of the PA-PKINIT-KX key excahnge + when anonymous PKINIT is used. Failure to do so can permit an active + attacker to become a man-in-the-middle. + + Bug fixes + + - Happy eyeballs: Don't wait for responses from known-unreachable KDCs. + - kdc: check return copy_Realm, copy_PrincipalName, copy_EncryptionKey + - kinit: + . cleanup temporary ccaches + . see man page for "kinit --anonymous" command line syntax change + - kdc: Make anonymous AS-requests more RFC8062-compliant. + - Updated expired test certificates + - Solaris: + . PKCS#11 hcrypto backend broken since 7.0.1 + . Building with Sun Pro C + + Features + + - kuser: support authenticated anonymous AS-REQs in kinit + - kdc: support for anonymous TGS-REQs + - kgetcred support for anonymous service tickets + - Support builds with OpenSSL 1.1.1 + +Release Notes - Heimdal - Version Heimdal 7.5 + + Security + + - Fix CVE-2017-17439, which is a remote denial of service + vulnerability: + + In Heimdal 7.1 through 7.4, remote unauthenticated attackers + are able to crash the KDC by sending a crafted UDP packet + containing empty data fields for client name or realm. + + Bug fixes + + - Handle long input lines when reloading database dumps. + + - In pre-forked mode (default on Unix), correctly clear + the process ids of exited children, allowing new child processes + to replace the old. + + - Fixed incorrect KDC response when no-cross realm TGT exists, + allowing client requests to fail quickly rather than time + out after trying to get a correct answer from each KDC. + +Release Notes - Heimdal - Version Heimdal 7.4 + + Security + + - Fix CVE-2017-11103: Orpheus' Lyre KDC-REP service name validation + + This is a critical vulnerability. + + In _krb5_extract_ticket() the KDC-REP service name must be obtained from + encrypted version stored in 'enc_part' instead of the unencrypted version + stored in 'ticket'. Use of the unecrypted version provides an + opportunity for successful server impersonation and other attacks. + + Identified by Jeffrey Altman, Viktor Duchovni and Nico Williams. + + See https://www.orpheus-lyre.info/ for more details. + Release Notes - Heimdal - Version Heimdal 7.3 Security diff --git a/third_party/heimdal/admin/NTMakefile b/third_party/heimdal/admin/NTMakefile index 06f90c9fdbf..f78a201a801 100644 --- a/third_party/heimdal/admin/NTMakefile +++ b/third_party/heimdal/admin/NTMakefile @@ -49,6 +49,7 @@ KTUTIL_OBJS= \ $(OBJ)\rename.obj KTUTIL_LIBS= \ + $(LIBHEIMBASE) \ $(LIBHEIMDAL) \ $(LIBKADM5SRV) \ $(LIBSL) \ diff --git a/third_party/heimdal/admin/add.c b/third_party/heimdal/admin/add.c index 5f1920ff8be..3fba3913881 100644 --- a/third_party/heimdal/admin/add.c +++ b/third_party/heimdal/admin/add.c @@ -278,6 +278,7 @@ json2keytab_entry(heim_dict_t d, krb5_keytab kt, size_t idx) (unsigned long)idx); bad: krb5_free_principal(context, e.principal); + free(buf); } int @@ -296,6 +297,7 @@ kt_import(void *opt, int argc, char **argv) err(1, "Could not open file %s", argv[0]); json = read_file(f); + fclose(f); o = heim_json_create(json, 10, flags, &json_err); free(json); if (o == NULL) { diff --git a/third_party/heimdal/admin/get.c b/third_party/heimdal/admin/get.c index ecd6f6a160e..1c0a6333a5a 100644 --- a/third_party/heimdal/admin/get.c +++ b/third_party/heimdal/admin/get.c @@ -109,6 +109,7 @@ parse_enctypes(struct get_options *opt, free(s); return krb5_enomem(context); } + free(s); s = tmp; } ret = krb5_string_to_keysalts2(context, s, nks, ks); @@ -165,7 +166,7 @@ kt_get(struct get_options *opt, int argc, char **argv) kadm5_principal_ent_rec princ; int mask = 0; krb5_keyblock *keys; - int n_keys; + int n_keys = 0; int created = 0; krb5_keytab_entry entry; diff --git a/third_party/heimdal/admin/list.c b/third_party/heimdal/admin/list.c index 9d1e9d5d483..22ccdcac8cb 100644 --- a/third_party/heimdal/admin/list.c +++ b/third_party/heimdal/admin/list.c @@ -233,7 +233,6 @@ do_list1_json(struct list_options *opt, } heim_dict_set_value(d, HSTR("aliases"), aliases); heim_release(aliases); - free(s); } krb5_kt_free_entry(context, &entry); diff --git a/third_party/heimdal/appl/gssmask/common.c b/third_party/heimdal/appl/gssmask/common.c index 8d7d8fa2252..733db9013eb 100644 --- a/third_party/heimdal/appl/gssmask/common.c +++ b/third_party/heimdal/appl/gssmask/common.c @@ -55,7 +55,7 @@ add_list(char ****list, size_t *listlen, char **str, size_t len) size_t i; *list = erealloc(*list, sizeof(**list) * (*listlen + 1)); - (*list)[*listlen] = ecalloc(len, sizeof(**list)); + (*list)[*listlen] = ecalloc(len, sizeof(***list)); for (i = 0; i < len; i++) (*list)[*listlen][i] = str[i]; (*listlen)++; diff --git a/third_party/heimdal/appl/test/gssapi_server.c b/third_party/heimdal/appl/test/gssapi_server.c index baf13ecff5f..74ceb3bee2e 100644 --- a/third_party/heimdal/appl/test/gssapi_server.c +++ b/third_party/heimdal/appl/test/gssapi_server.c @@ -192,6 +192,9 @@ proto (int sock, const char *service) gss_OID mech_oid; char *mech, *p; + memset(&remote, 0, sizeof(remote)); + local = remote; + addrlen = sizeof(local); if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0 || addrlen != sizeof(local)) diff --git a/third_party/heimdal/appl/test/http_client.c b/third_party/heimdal/appl/test/http_client.c index 24c4e6e2d06..88a8fee35e5 100644 --- a/third_party/heimdal/appl/test/http_client.c +++ b/third_party/heimdal/appl/test/http_client.c @@ -316,6 +316,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; + memset(&req, 0, sizeof(req)); mech_oid = select_mech(mech); if (argc != 1 && argc != 2) @@ -346,6 +347,9 @@ main(int argc, char **argv) } num_headers = 0; + if (req.response == NULL) + errx(1, "Got no response"); + if (strstr(req.response, " 200 ") != NULL) { print_body = 1; done = 1; diff --git a/third_party/heimdal/cf/broken-realloc.m4 b/third_party/heimdal/cf/broken-realloc.m4 index b6d962aceba..a397e96882e 100644 --- a/third_party/heimdal/cf/broken-realloc.m4 +++ b/third_party/heimdal/cf/broken-realloc.m4 @@ -19,7 +19,9 @@ int main(int argc, char **argv) if test "$ac_cv_func_realloc_broken" = yes ; then AC_DEFINE(BROKEN_REALLOC, 1, [Define if realloc(NULL) doesn't work.]) fi -AH_BOTTOM([#ifdef BROKEN_REALLOC -#define realloc(X, Y) rk_realloc((X), (Y)) -#endif]) +dnl AH_BOTTOM([#ifdef BROKEN_REALLOC +dnl #ifndef realloc +dnl #define realloc(X, Y) rk_realloc((X), (Y)) +dnl #endif +dnl #endif]) ]) diff --git a/third_party/heimdal/cf/crypto.m4 b/third_party/heimdal/cf/crypto.m4 index b8b011dd154..a8ef5198411 100644 --- a/third_party/heimdal/cf/crypto.m4 +++ b/third_party/heimdal/cf/crypto.m4 @@ -134,6 +134,23 @@ if test "$with_openssl" != "no"; then LDFLAGS="${saved_LDFLAGS}" fi +if test "$openssl" = "yes"; then + AC_CHECK_LIB([crypto], + [OSSL_EC_curve_nid2name], + [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_30], 1, + [whether OpenSSL is 3.0 or higher])] + ) + AC_CHECK_HEADERS([openssl/fips.h], + [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_FIPS_H], 1, + [whether openssl/fips.h is available])] + ) + AC_CHECK_LIB([crypto], + [FIPS_mode_set], + [AC_DEFINE_UNQUOTED([HAVE_OPENSSL_FIPS_MODE_SET_API], 1, + [whether FIPS_mode_set API is available])] + ) +fi + LIB_hcrypto='$(top_builddir)/lib/hcrypto/libhcrypto.la' LIB_hcrypto_a='$(top_builddir)/lib/hcrypto/.libs/libhcrypto.a' LIB_hcrypto_so='$(top_builddir)/lib/hcrypto/.libs/libhcrypto.so' diff --git a/third_party/heimdal/cf/roken-frag.m4 b/third_party/heimdal/cf/roken-frag.m4 index 2c2ef834e7a..90e514c20e1 100644 --- a/third_party/heimdal/cf/roken-frag.m4 +++ b/third_party/heimdal/cf/roken-frag.m4 @@ -398,6 +398,8 @@ AC_BROKEN([ \ writev \ ]) +rk_LIBOBJ(closefrom) + AM_CONDITIONAL(have_fnmatch_h, test "$ac_cv_header_fnmatch_h" = yes -a "$ac_cv_func_fnmatch" = yes) @@ -492,13 +494,13 @@ dnl AC_HAVE_STRUCT_FIELD(struct sockaddr, sa_len, [#include <sys/types.h> #include <sys/socket.h>]) -if test "$ac_cv_func_getaddrinfo" = "yes"; then - rk_BROKEN_GETADDRINFO - if test "$ac_cv_func_getaddrinfo_numserv" = no; then - AC_LIBOBJ(getaddrinfo) - AC_LIBOBJ(freeaddrinfo) - fi -fi +#if test "$ac_cv_func_getaddrinfo" = "yes"; then +# rk_BROKEN_GETADDRINFO +# if test "$ac_cv_func_getaddrinfo_numserv" = no; then +# AC_LIBOBJ(getaddrinfo) +# AC_LIBOBJ(freeaddrinfo) +# fi +#fi AC_NEED_PROTO([#include <stdlib.h>], setenv) AC_NEED_PROTO([#include <stdlib.h>], unsetenv) diff --git a/third_party/heimdal/cf/sunos.m4 b/third_party/heimdal/cf/sunos.m4 index 938fc3a5ec8..d9aa52cb642 100644 --- a/third_party/heimdal/cf/sunos.m4 +++ b/third_party/heimdal/cf/sunos.m4 @@ -26,4 +26,5 @@ if test "$sunos" != no; then AC_DEFINE_UNQUOTED(SunOS, $sunos, [Define to what version of SunOS you are running.]) fi +AM_CONDITIONAL(SUNOS, test "$sunos" != no) ]) diff --git a/third_party/heimdal/configure.ac b/third_party/heimdal/configure.ac index b946dfff4c1..e8eea4e6d3a 100644 --- a/third_party/heimdal/configure.ac +++ b/third_party/heimdal/configure.ac @@ -245,6 +245,13 @@ AM_CONDITIONAL([HAVE_CJSON], [test "$with_cjson" != "no"]) AC_SUBST([CJSON_CFLAGS]) AC_SUBST([CJSON_LIBS]) +dnl mitkrb5 +AC_ARG_WITH([mitkrb5], + AC_HELP_STRING([--with-mitkrb5], [Path to MIT Kerberos for interop testing @<:@default=check@:>@]), + [], + [with_mikrb5=check]) +AM_CONDITIONAL([MITKRB5], [test "$with_mitkrb5" != "no"]) + dnl Check for sqlite rk_TEST_PACKAGE(sqlite3, [#include <sqlite3.h> diff --git a/third_party/heimdal/doc/Makefile.am b/third_party/heimdal/doc/Makefile.am index aa7f8130f49..60c6a8c58a1 100644 --- a/third_party/heimdal/doc/Makefile.am +++ b/third_party/heimdal/doc/Makefile.am @@ -6,12 +6,14 @@ AUTOMAKE_OPTIONS = no-texinfo.tex MAKEINFOFLAGS = --css-include=$(srcdir)/heimdal.css -TEXI2DVI = true # ARGH, make distcheck can't be disabled to not build dvifiles +#TEXI2DVI = true # ARGH, make distcheck can't be disabled to not build dvifiles info_TEXINFOS = heimdal.texi hx509.texi BUILT_SOURCES = vars.texi +#all: html pdf dvi ps info + dxy_subst = sed -e 's,[@]srcdir[@],$(srcdir),g' \ -e 's,[@]objdir[@],.,g' \ -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' diff --git a/third_party/heimdal/doc/whatis.texi b/third_party/heimdal/doc/whatis.texi index 2b0e98de552..8c7770b8184 100644 --- a/third_party/heimdal/doc/whatis.texi +++ b/third_party/heimdal/doc/whatis.texi @@ -35,12 +35,6 @@ services can authenticate each other. @end macro @end ifinfo -@iftex -@macro sub{arg} -@textsubscript{\arg\} -@end macro -@end iftex - @ifhtml @macro sub{arg} @@ -51,13 +45,19 @@ services can authenticate each other. @end macro @end ifhtml -@c ifdocbook -@c macro sub{arg} -@c docbook -@c <subscript>\arg\</subscript> -@c end docbook -@c end macro -@c end ifdocbook +@ifdocbook +@macro sub{arg} +@docbook +@<subscript>\arg\</subscript> +@end docbook +@end macro +@end ifdocbook + +@c @iftex +@c @macro sub{arg} +@c @textsubscript{\arg\} +@c @end macro +@c @end iftex @quotation @strong{Note} This discussion is about Kerberos version 4, but version diff --git a/third_party/heimdal/include/config.h.w32 b/third_party/heimdal/include/config.h.w32 index 6e0f6bcf147..f8be38b5e29 100644 --- a/third_party/heimdal/include/config.h.w32 +++ b/third_party/heimdal/include/config.h.w32 @@ -57,6 +57,10 @@ static const char *const rcsid[] = { (const char *)rcsid, "@(#)" msg } #define MAXPATHLEN MaxPathLen #endif +#if _MSC_VER < 1900 +# define __func__ __FUNCTION__ +#endif + #ifdef BUILD_KRB5_LIB #ifndef KRB5_LIB #ifdef _WIN32 diff --git a/third_party/heimdal/include/hcrypto/Makefile.am b/third_party/heimdal/include/hcrypto/Makefile.am index f15c779a894..e9a59fd38d5 100644 --- a/third_party/heimdal/include/hcrypto/Makefile.am +++ b/third_party/heimdal/include/hcrypto/Makefile.am @@ -16,7 +16,6 @@ CLEANFILES = \ evp-hcrypto.h \ evp-cc.h \ hmac.h \ - md2.h \ md4.h \ md5.h \ pkcs12.h \ diff --git a/third_party/heimdal/kadmin/Makefile.am b/third_party/heimdal/kadmin/Makefile.am index e7fe58f8d73..d9b8fee1c65 100644 --- a/third_party/heimdal/kadmin/Makefile.am +++ b/third_party/heimdal/kadmin/Makefile.am @@ -69,6 +69,7 @@ LDADD_common = \ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) kadmind_LDADD = $(top_builddir)/lib/kadm5/libkadm5srv.la \ + $(top_builddir)/lib/kadm5/libkadm5clnt.la \ ../lib/gssapi/libgssapi.la \ $(LDADD_common) \ $(LIB_pidfile) \ diff --git a/third_party/heimdal/kadmin/ank.c b/third_party/heimdal/kadmin/ank.c index 4b89ca6eedd..fba3450aa89 100644 --- a/third_party/heimdal/kadmin/ank.c +++ b/third_party/heimdal/kadmin/ank.c @@ -182,8 +182,13 @@ add_one_principal(const char *name, krb5_free_keyblock_contents(context, &new_keys[i]); if (n_keys > 0) free(new_keys); - kadm5_get_principal(kadm_handle, princ_ent, &princ, - KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES); + ret = kadm5_get_principal(kadm_handle, princ_ent, &princ, + KADM5_PRINCIPAL | KADM5_KVNO | + KADM5_ATTRIBUTES); + if (ret) { + krb5_warn(context, ret, "kadm5_get_principal"); + goto out; + } krb5_free_principal(context, princ_ent); princ_ent = princ.principal; princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX); diff --git a/third_party/heimdal/kadmin/cpw.c b/third_party/heimdal/kadmin/cpw.c index 7ffc828cf30..13973177710 100644 --- a/third_party/heimdal/kadmin/cpw.c +++ b/third_party/heimdal/kadmin/cpw.c @@ -156,8 +156,10 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) int i; struct cpw_entry_data data; int num; + int16_t n_key_data = 0; krb5_key_data key_data[3]; + memset(key_data, 0, sizeof(key_data)); data.kadm_handle = NULL; ret = kadm5_dup_context(kadm_handle, &data.kadm_handle); if (ret) @@ -214,6 +216,7 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) opt->key_string, error); return 1; } + n_key_data = sizeof(key_data)/sizeof(key_data[0]); data.key_data = key_data; } @@ -222,10 +225,8 @@ cpw_entry(struct passwd_options *opt, int argc, char **argv) kadm5_destroy(data.kadm_handle); - if (data.key_data) { - int16_t dummy; - kadm5_free_key_data (kadm_handle, &dummy, key_data); - } + if (opt->key_string) + kadm5_free_key_data(kadm_handle, &n_key_data, key_data); return ret != 0; } diff --git a/third_party/heimdal/kadmin/get.c b/third_party/heimdal/kadmin/get.c index 6e8ada01ea4..1942d63894e 100644 --- a/third_party/heimdal/kadmin/get.c +++ b/third_party/heimdal/kadmin/get.c @@ -233,6 +233,8 @@ format_field(struct get_entry_data *data, size_t buf_len, int condensed) { + krb5_error_code ret; + switch(field) { case KADM5_PRINCIPAL: if(condensed) @@ -302,7 +304,10 @@ format_field(struct get_entry_data *data, krb5_salt def_salt; int i; char buf2[1024]; - krb5_get_pw_salt (context, princ->principal, &def_salt); + + ret = krb5_get_pw_salt(context, princ->principal, &def_salt); + if (ret) + krb5_err(context, 1, ret, "krb5_get_pw_salt"); *buf = '\0'; for (i = 0; i < princ->n_key_data; ++i) { @@ -335,7 +340,6 @@ format_field(struct get_entry_data *data, HDB_EncTypeList etypes; size_t i, size; char *str; - int ret; ret = decode_HDB_EncTypeList(tl->tl_data_contents, tl->tl_data_length, @@ -360,7 +364,6 @@ format_field(struct get_entry_data *data, case KRB5_TL_PKINIT_ACL: { HDB_Ext_PKINIT_acl acl; size_t size; - int ret; size_t i; ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents, @@ -403,7 +406,6 @@ format_field(struct get_entry_data *data, case KRB5_TL_ALIASES: { HDB_Ext_Aliases alias; size_t size; - int ret; size_t i; ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, diff --git a/third_party/heimdal/kadmin/kadmin.c b/third_party/heimdal/kadmin/kadmin.c index d2f5abadd15..607ba03e236 100644 --- a/third_party/heimdal/kadmin/kadmin.c +++ b/third_party/heimdal/kadmin/kadmin.c @@ -296,10 +296,13 @@ main(int argc, char **argv) } else { while(!exit_seen) { ret = sl_command_loop(commands, "kadmin> ", NULL); - if (ret == -2) + if (ret == -2) { exit_seen = 1; - else if (ret != 0) + } else if (ret != 0) { exit_status = 1; + if (!isatty(STDIN_FILENO)) + exit_seen = 1; + } } } diff --git a/third_party/heimdal/kadmin/kadmind.c b/third_party/heimdal/kadmin/kadmind.c index cf335d6dc01..4ea513e08d3 100644 --- a/third_party/heimdal/kadmin/kadmind.c +++ b/third_party/heimdal/kadmin/kadmind.c @@ -32,6 +32,8 @@ */ #include "kadmin_locl.h" +#include "heim_threads.h" +#include "krb5-protos.h" static char *check_library = NULL; static char *check_function = NULL; @@ -39,6 +41,13 @@ static getarg_strings policy_libraries = { 0, NULL }; static char *config_file; static char sHDB[] = "HDBGET:"; static char *keytab_str = sHDB; +#ifndef WIN32 +static char *fuzz_file; +static char *fuzz_client_name; +static char *fuzz_keytab_name; +static char *fuzz_service_name; +static char *fuzz_admin_server; +#endif static int help_flag; static int version_flag; static int debug_flag; @@ -88,6 +97,16 @@ static struct getargs args[] = { "ports to listen to", "port" }, { "read-only", 0, arg_flag, &readonly_flag, "read-only operations", NULL }, +#ifndef WIN32 + { "fuzz-file", 0, arg_string, &fuzz_file, + "Kadmin RPC body for fuzzing", "FILE" }, + { "fuzz-client", 0, arg_string, &fuzz_client_name, + "Client name for fuzzing", "PRINCIPAL" }, + { "fuzz-keytab", 0, arg_string, &fuzz_keytab_name, + "Keytab for fuzzing", "KEYTAB" }, + { "fuzz-server", 0, arg_string, &fuzz_admin_server, + "Name of kadmind self instance", "HOST:PORT" }, +#endif { "help", 'h', arg_flag, &help_flag, NULL, NULL }, { "version", 'v', arg_flag, &version_flag, NULL, NULL } }; @@ -103,6 +122,8 @@ usage(int ret) exit (ret); } +static void *fuzz_thread(void *); + int main(int argc, char **argv) { @@ -220,7 +241,78 @@ main(int argc, char **argv) if(realm) krb5_set_default_realm(context, realm); /* XXX */ +#ifndef WIN32 + if (fuzz_file) { + HEIMDAL_THREAD_ID tid; + + if (fuzz_admin_server == NULL) + errx(1, "If --fuzz-file is given then --fuzz-server must be too"); + HEIMDAL_THREAD_create(&tid, fuzz_thread, NULL); + } +#endif + kadmind_loop(context, keytab, sfd, readonly_flag); return 0; } + +#ifndef WIN32 +static void * +fuzz_thread(void *arg) +{ + kadm5_config_params conf; + krb5_error_code ret; + krb5_context context2; + krb5_storage *sp; + krb5_data reply; + void *server_handle = NULL; + int fd; + + memset(&conf, 0, sizeof(conf)); + conf.admin_server = fuzz_admin_server; + + fd = open(fuzz_file, O_RDONLY); + if (fd < 0) + err(1, "Could not open fuzz file %s", fuzz_file); + sp = krb5_storage_from_fd(fd); + if (sp == NULL) + err(1, "Could not read fuzz file %s", fuzz_file); + (void) close(fd); + + ret = krb5_init_context(&context2); + if (ret) + errx(1, "Fuzzing failed: krb5_init_context failed: %d", ret); + ret = kadm5_c_init_with_skey_ctx(context2, + fuzz_client_name, + fuzz_keytab_name, + fuzz_service_name ? + fuzz_service_name : + KADM5_ADMIN_SERVICE, + &conf, + 0, /* struct_version */ + 0, /* api_version */ + &server_handle); + if (ret) + errx(1, "Fuzzing failed: kadm5_c_init_with_skey_ctx failed: %d", ret); + + ret = _kadm5_connect(server_handle, 1 /* want_write */); + if (ret) + errx(1, "Fuzzing failed: Could not connect to self (%s): " + "_kadm5_connect failed: %d", fuzz_admin_server, ret); + ret = _kadm5_client_send(server_handle, sp); + if (ret) + errx(1, "Fuzzing failed: Could not send request to self (%s): " + "_kadm5_client_send failed: %d", fuzz_admin_server, ret); + krb5_data_zero(&reply); + ret = _kadm5_client_recv(server_handle, &reply); + if (ret) + errx(1, "Fuzzing failed: Could not read reply from self (%s): " + "_kadm5_client_recv failed: %d", fuzz_admin_server, ret); + krb5_storage_free(sp); + krb5_data_free(&reply); + fprintf(stderr, "Fuzzed with %s", fuzz_file); + exit(0); + + return NULL; +} +#endif diff --git a/third_party/heimdal/kadmin/mod.c b/third_party/heimdal/kadmin/mod.c index 3bcd9ac31d5..2d4bd5d5077 100644 --- a/third_party/heimdal/kadmin/mod.c +++ b/third_party/heimdal/kadmin/mod.c @@ -262,6 +262,7 @@ add_kvno_diff(krb5_context contextp, kadm5_principal_ent_rec *princ, if (kvno_diff > 2048) kvno_diff = 2048; + ext.mandatory = 0; if (is_svc_diff) { ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc; ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff; diff --git a/third_party/heimdal/kadmin/rpc.c b/third_party/heimdal/kadmin/rpc.c index 5cae3d2c239..8a176da6399 100644 --- a/third_party/heimdal/kadmin/rpc.c +++ b/third_party/heimdal/kadmin/rpc.c @@ -529,6 +529,7 @@ ret_principal_ent(krb5_context contextp, CHECK(krb5_ret_uint32(sp, &flag)); ent->key_data[i].key_data_type[1] = flag; } + CHECK(i == num); return 0; } diff --git a/third_party/heimdal/kdc/Makefile.am b/third_party/heimdal/kdc/Makefile.am index 48248d8248b..ca5835930dd 100644 --- a/third_party/heimdal/kdc/Makefile.am +++ b/third_party/heimdal/kdc/Makefile.am @@ -6,8 +6,9 @@ WFLAGS += $(WFLAGS_ENUM_CONV) AM_CPPFLAGS += $(INCLUDE_libintl) $(INCLUDE_openssl_crypto) -I$(srcdir)/../lib/krb5 -lib_LTLIBRARIES = simple_csr_authorizer.la ipc_csr_authorizer.la \ - libkdc.la negotiate_token_validator.la +lib_LTLIBRARIES = ipc_csr_authorizer.la \ + negotiate_token_validator.la \ + libkdc.la if HAVE_CJWT lib_LTLIBRARIES += cjwt_token_validator.la @@ -97,8 +98,6 @@ endif negotiate_token_validator_la_SOURCES = negotiate_token_validator.c negotiate_token_validator_la_LDFLAGS = -module $(LIB_gssapi) # CSR Authorizer plugins (for kdc/kx509 and bx509d) -simple_csr_authorizer_la_SOURCES = simple_csr_authorizer.c -simple_csr_authorizer_la_LDFLAGS = -module ipc_csr_authorizer_la_SOURCES = ipc_csr_authorizer.c ipc_csr_authorizer_la_LDFLAGS = -module \ $(top_builddir)/lib/krb5/libkrb5.la \ @@ -155,7 +154,6 @@ ALL_OBJECTS += $(digest_service_OBJECTS) ALL_OBJECTS += $(bx509d_OBJECTS) ALL_OBJECTS += $(httpkadmind_OBJECTS) ALL_OBJECTS += $(cjwt_token_validator_la_OBJECTS) -ALL_OBJECTS += $(simple_csr_authorizer_la_OBJECTS) ALL_OBJECTS += $(test_token_validator_OBJECTS) ALL_OBJECTS += $(test_csr_authorizer_OBJECTS) ALL_OBJECTS += $(test_kdc_ca_OBJECTS) @@ -237,7 +235,12 @@ digest_service_LDADD = \ kdc_replay_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) kdc_tester_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) test_token_validator_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) $(LIB_gssapi) -test_csr_authorizer_LDADD = libkdc.la $(top_builddir)/lib/hx509/libhx509.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) +test_csr_authorizer_LDADD = libkdc.la \ + $(top_builddir)/lib/hx509/libhx509.la \ + $(LDADD) \ + $(LIB_pidfile) \ + $(LIB_heimbase) \ + $(top_builddir)/lib/ipc/libheim-ipcs.la test_kdc_ca_LDADD = libkdc.la $(top_builddir)/lib/hx509/libhx509.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase) include_HEADERS = kdc.h $(srcdir)/kdc-protos.h diff --git a/third_party/heimdal/kdc/bx509d.c b/third_party/heimdal/kdc/bx509d.c index 4d1b694a914..b7e9096f737 100644 --- a/third_party/heimdal/kdc/bx509d.c +++ b/third_party/heimdal/kdc/bx509d.c @@ -185,6 +185,7 @@ typedef struct bx509_request_desc { const char *redir; const char *method; size_t post_data_size; + size_t san_idx; /* For /get-tgts */ enum k5_creds_kind cckind; char *pkix_store; char *tgts_filename; @@ -260,6 +261,8 @@ get_krb5_context(krb5_context *contextp) return 0; if ((ret = krb5_init_context(contextp))) return *contextp = NULL, ret; + if (logfac) + krb5_set_log_dest(*contextp, logfac); (void) pthread_setspecific(k5ctx, *contextp); return *contextp ? 0 : ENOMEM; } @@ -566,7 +569,6 @@ bad_reqv(struct bx509_request_desc *r, va_list ap) { krb5_error_code ret; - krb5_context context = NULL; const char *k5msg = NULL; const char *emsg = NULL; char *formatted = NULL; @@ -586,8 +588,10 @@ bad_reqv(struct bx509_request_desc *r, if (code) { if (r->context) emsg = k5msg = krb5_get_error_message(r->context, code); - else + else if (code > -1) emsg = strerror(code); + else + emsg = "Unknown error"; } ret = vasprintf(&formatted, fmt, ap); @@ -600,10 +604,11 @@ bad_reqv(struct bx509_request_desc *r, } heim_audit_addreason((heim_svc_req_desc)r, "%s", msg); audit_trail(r, code); - krb5_free_error_message(context, k5msg); + if (r->context) + krb5_free_error_message(r->context, k5msg); if (ret == -1 || msg == NULL) { - if (context) + if (r->context) krb5_log_msg(r->context, logfac, 1, NULL, "Out of memory"); return resp(r, MHD_HTTP_SERVICE_UNAVAILABLE, MHD_RESPMEM_PERSISTENT, NULL, "Out of memory", sizeof("Out of memory") - 1, NULL); @@ -882,7 +887,7 @@ do_CA(struct bx509_request_desc *r, const char *csr) bytes = rk_base64_decode(csr2, d.data); free(csr2); if (bytes < 0) - ret = errno; + ret = errno ? errno : EINVAL; else d.length = bytes; if (ret) { @@ -1360,10 +1365,12 @@ do_pkinit(struct bx509_request_desc *r, enum k5_creds_kind kind) ret = krb5_append_addresses(r->context, &r->tgt_addresses, &addr); } - if (ret == 0 && r->tgt_addresses.len == 0) - ret = krb5_get_init_creds_opt_set_addressless(r->context, opt, 1); - else - krb5_get_init_creds_opt_set_address_list(opt, &r->tgt_addresses); + if (ret == 0) { + if (r->tgt_addresses.len == 0) + ret = krb5_get_init_creds_opt_set_addressless(r->context, opt, 1); + else + krb5_get_init_creds_opt_set_address_list(opt, &r->tgt_addresses); + } if (ret == 0) ret = krb5_get_init_creds_opt_set_pkinit(r->context, opt, p, r->pkix_store, @@ -1543,7 +1550,7 @@ k5_get_creds(struct bx509_request_desc *r, enum k5_creds_kind kind) static void acc_str(char **acc, char *adds, size_t addslen) { - char *tmp; + char *tmp = NULL; int l = addslen <= INT_MAX ? (int)addslen : INT_MAX; if (asprintf(&tmp, "%s%s%.*s", @@ -1570,7 +1577,7 @@ fmt_gss_error(OM_uint32 code, gss_OID mech) acc_str(&r, (char *)buf.value, buf.length); gss_release_buffer(&minor, &buf); } while (!GSS_ERROR(major) && more); - return r ? r : "Out of memory while formatting GSS-API error"; + return r; } static char * @@ -1580,7 +1587,10 @@ fmt_gss_errors(const char *r, OM_uint32 major, OM_uint32 minor, gss_OID mech) ma = fmt_gss_error(major, GSS_C_NO_OID); mi = mech == GSS_C_NO_OID ? NULL : fmt_gss_error(minor, mech); - if (asprintf(&s, "%s: %s%s%s", r, ma, mi ? ": " : "", mi ? mi : "") > -1 && + if (asprintf(&s, "%s: %s%s%s", r, + ma ? ma : "Out of memory", + mi ? ": " : "", + mi ? mi : "") > -1 && s) { free(ma); free(mi); @@ -1605,8 +1615,13 @@ bad_req_gss(struct bx509_request_desc *r, if (major == GSS_S_BAD_NAME || major == GSS_S_BAD_NAMETYPE) http_status_code = MHD_HTTP_BAD_REQUEST; - ret = resp(r, http_status_code, MHD_RESPMEM_MUST_COPY, NULL, - msg, strlen(msg), NULL); + if (msg) + ret = resp(r, http_status_code, MHD_RESPMEM_MUST_COPY, NULL, + msg, strlen(msg), NULL); + else + ret = resp(r, http_status_code, MHD_RESPMEM_MUST_COPY, NULL, + "Out of memory while formatting GSS error message", + sizeof("Out of memory while formatting GSS error message") - 1, NULL); free(msg); return ret; } @@ -1839,9 +1854,7 @@ authorize_TGT_REQ(struct bx509_request_desc *r) if (for_cname == r->cname || strcmp(r->cname, r->for_cname) == 0) return 0; - ret = krb5_parse_name(r->context, r->cname, &p); - if (ret == 0) - ret = hx509_request_init(r->context->hx509ctx, &r->req); + ret = hx509_request_init(r->context->hx509ctx, &r->req); if (ret) return bad_500(r, ret, "Out of resources"); heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, @@ -1852,9 +1865,12 @@ authorize_TGT_REQ(struct bx509_request_desc *r) ret = hx509_request_add_pkinit(r->context->hx509ctx, r->req, for_cname); if (ret == 0) + ret = krb5_parse_name(r->context, r->cname, &p); + if (ret == 0) ret = kdc_authorize_csr(r->context, "get-tgt", r->req, p); krb5_free_principal(r->context, p); hx509_request_free(&r->req); + r->req = NULL; if (ret) return bad_403(r, ret, "Not authorized to requested TGT"); return ret; @@ -1972,7 +1988,7 @@ get_tgts_accumulate_ccache_write_json(struct bx509_request_desc *r, if (o && k && v) ret = heim_dict_set_value(o, k, v); else - ret = errno; + ret = ENOMEM; if (ret == 0) { heim_release(v); @@ -1991,10 +2007,13 @@ get_tgts_accumulate_ccache_write_json(struct bx509_request_desc *r, ret = heim_dict_set_value(o, k, v); } if (ret == 0 && code != 0) { + const char *s = krb5_get_error_message(r->context, code); + heim_release(v); heim_release(k); k = heim_string_create("error"); - v = heim_string_create(krb5_get_error_message(r->context, code)); + v = heim_string_create(s ? s : "Out of memory"); + krb5_free_error_message(r->context, s); if (k && v) ret = heim_dict_set_value(o, k, v); } @@ -2120,17 +2139,79 @@ get_tgts_param_execute_cb(void *d, const char *val) { struct bx509_request_desc *r = d; - heim_mhd_result res = MHD_YES; + hx509_san_type san_type; krb5_error_code ret; + size_t san_idx = r->san_idx++; + const char *save_for_cname = r->for_cname; + char *s = NULL; + int res; - if (strcmp(key, "cname") == 0 && val) { - /* Handled upstairs */ + /* We expect only cname=principal q-params here */ + if (strcmp(key, "cname") != 0 || val == NULL) + return MHD_YES; + + /* + * We expect the `san_idx'th SAN in the `r->req' request checked by + * kdc_authorize_csr() to be the same as this cname. This happens + * naturally because we add these SANs to `r->req' in the same order as we + * visit them here (unless our HTTP library somehow went crazy). + * + * Still, we check that it's the same SAN. + */ + ret = hx509_request_get_san(r->req, san_idx, &san_type, &s); + if (ret == HX509_NO_ITEM || + san_type != HX509_SAN_TYPE_PKINIT || + strcmp(s, val) != 0) { + /* + * If the cname and SAN don't match, it's some weird internal error + * (can't happen). + */ + krb5_set_error_message(r->context, r->error_code = EACCES, + "PKINIT SAN not granted: %s (internal error)", + val); + ret = EACCES; + } + + /* + * We're going to pretend to be this SAN for the purpose of acquring a TGT + * for it. So we "push" `r->for_cname'. + */ + if (ret == 0) r->for_cname = val; + + /* + * Our authorizer supports partial authorization where the whole request is + * rejected but some features of it are permitted. + * + * (In most end-points we don't want partial authorization, but in + * /get-tgts we very much do.) + */ + if (ret == 0 && !hx509_request_san_authorized_p(r->req, san_idx)) { + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, + "REJECT_krb5PrincipalName", "%s", val); + krb5_set_error_message(r->context, r->error_code = EACCES, + "PKINIT SAN denied: %s", val); + ret = EACCES; + } + if (ret == 0) { + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, + "ACCEPT_krb5PrincipalName", "%s", val); ret = k5_get_creds(r, K5_CREDS_EPHEMERAL); - res = get_tgts_accumulate_ccache(r, ret); - } else { - /* Handled upstairs */ + if (ret == 0) + heim_audit_addkv((heim_svc_req_desc)r, KDC_AUDIT_VIS, + "ISSUE_krb5PrincipalName", "%s", val); } + + /* + * If ret == 0 this will gather the TGT we acquired, else it will acquire + * the error we got. + */ + res = get_tgts_accumulate_ccache(r, ret); + + /* Now we "pop" `r->for_cname' */ + r->for_cname = save_for_cname; + + hx509_xfree(s); return res; } @@ -2161,39 +2242,63 @@ get_tgts(struct bx509_request_desc *r) r->error_code = 0; res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND, get_tgt_param_cb, r); - if (r->response || res == MHD_NO) + if (r->response || res == MHD_NO) { + krb5_free_principal(r->context, p); return res; + } ret = r->error_code; } if (ret == 0) { - /* Authorize requested client principal names (calls bad_req()) */ + /* + * Check authorization of the authenticated client to the requested + * client principal names (calls bad_req()). + */ r->error_code = 0; res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND, get_tgts_param_authorize_cb, r); - if (r->response || res == MHD_NO) + if (r->response || res == MHD_NO) { + krb5_free_principal(r->context, p); return res; + } ret = r->error_code; if (ret == 0) { + /* Use the same configuration as /get-tgt (or should we?) */ ret = kdc_authorize_csr(r->context, "get-tgt", r->req, p); + + /* + * We tolerate EACCES because we support partial approval. + * + * (KRB5_PLUGIN_NO_HANDLE means no plugin handled the authorization + * check.) + */ + if (ret == EACCES || ret == KRB5_PLUGIN_NO_HANDLE) + ret = 0; if (ret) { krb5_free_principal(r->context, p); return bad_403(r, ret, "Permission denied"); } } - hx509_request_free(&r->req); } if (ret == 0) { - /* get_tgts_param_execute_cb() calls bad_req() */ + /* + * Get the actual TGTs that were authorized. + * + * get_tgts_param_execute_cb() calls bad_req() + */ r->error_code = 0; res = MHD_get_connection_values(r->connection, MHD_GET_ARGUMENT_KIND, get_tgts_param_execute_cb, r); - if (r->response || res == MHD_NO) + if (r->response || res == MHD_NO) { + krb5_free_principal(r->context, p); return res; + } ret = r->error_code; } krb5_free_principal(r->context, p); + hx509_request_free(&r->req); + r->req = NULL; /* * get_tgts_param_execute_cb() will write its JSON response to the file @@ -2322,7 +2427,7 @@ make_csrf_token(struct bx509_request_desc *r, if (ret == 0 && data.length > INT_MAX) ret = ERANGE; if (ret == 0 && - (dlen = rk_base64_encode(data.data, data.length, token)) < 0) + rk_base64_encode(data.data, data.length, token) < 0) ret = errno; krb5_storage_free(sp); krb5_data_free(&data); @@ -2420,6 +2525,7 @@ ip(void *cls, if (ftl == NULL || keydup == NULL || valdup == NULL) { free(ftl); free(keydup); + free(valdup); return MHD_NO; } ftl->freeme1 = keydup; @@ -2494,7 +2600,7 @@ route(void *cls, * possibly multiple times. */ if ((ret = set_req_desc(connection, method, url, &r))) - return bad_503(r, ret, "Could not initialize request state"); + return MHD_NO; *ctx = r; /* All requests other than /health require authentication */ @@ -2816,6 +2922,7 @@ main(int argc, char **argv) err(1, "Could not init krb5 context"); bx509_openlog(context, "bx509d", &logfac); + krb5_set_log_dest(context, logfac); load_plugins(context); if (allow_GET_flag == -1) diff --git a/third_party/heimdal/kdc/fast.c b/third_party/heimdal/kdc/fast.c index 392fc966050..e6c523ced95 100644 --- a/third_party/heimdal/kdc/fast.c +++ b/third_party/heimdal/kdc/fast.c @@ -605,9 +605,11 @@ fast_unwrap_request(astgs_request_t r, ticket = tgs_ticket; } - krb5_unparse_name(r->context, ticket->client, &armor_client_principal_name); + (void) krb5_unparse_name(r->context, ticket->client, &armor_client_principal_name); kdc_audit_addkv((kdc_request_t)r, 0, "armor_client_name", "%s", - armor_client_principal_name ? armor_client_principal_name : "<unknown>"); + armor_client_principal_name ? + armor_client_principal_name : + "<out of memory>"); if (ac->remote_subkey == NULL) { krb5_auth_con_free(r->context, ac); diff --git a/third_party/heimdal/kdc/hpropd.c b/third_party/heimdal/kdc/hpropd.c index fa06a1fd401..255d60949cc 100644 --- a/third_party/heimdal/kdc/hpropd.c +++ b/third_party/heimdal/kdc/hpropd.c @@ -78,7 +78,7 @@ main(int argc, char **argv) krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; - char *tmp_db; + char *tmp_db = NULL; krb5_log_facility *fac; int nprincs; @@ -208,20 +208,15 @@ main(int argc, char **argv) krb5_err(context, 1, ret, "krb5_kt_close"); } - if (!print_dump) { - int aret; + if (asprintf(&tmp_db, "%s~", database) < 0 || tmp_db == NULL) + krb5_errx(context, 1, "hdb_create: out of memory"); - aret = asprintf(&tmp_db, "%s~", database); - if (aret == -1) - krb5_errx(context, 1, "hdb_create: out of memory"); - - ret = hdb_create(context, &db, tmp_db); - if (ret) - krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); - ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); - if (ret) - krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); - } + ret = hdb_create(context, &db, tmp_db); + if (ret) + krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); + ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (ret) + krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); nprincs = 0; while (1){ @@ -244,14 +239,6 @@ main(int argc, char **argv) data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } - if (!print_dump) { - ret = db->hdb_close(context, db); - if (ret) - krb5_err(context, 1, ret, "db_close"); - ret = db->hdb_rename(context, db, database); - if (ret) - krb5_err(context, 1, ret, "db_rename"); - } break; } memset(&entry, 0, sizeof(entry)); @@ -284,6 +271,13 @@ main(int argc, char **argv) if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); + ret = db->hdb_close(context, db); + if (ret) + krb5_err(context, 1, ret, "db_close"); + ret = db->hdb_rename(context, db, database); + if (ret) + krb5_err(context, 1, ret, "db_rename"); + if (inetd_flag == 0) rk_closesocket(sock); diff --git a/third_party/heimdal/kdc/httpkadmind.c b/third_party/heimdal/kdc/httpkadmind.c index 068b5acbf90..3b57539cf4b 100644 --- a/third_party/heimdal/kdc/httpkadmind.c +++ b/third_party/heimdal/kdc/httpkadmind.c @@ -398,9 +398,6 @@ get_kadm_handle(krb5_context context, set_conf(conf, realm, want_realm, KADM5_CONFIG_REALM); set_conf(conf, dbname, hdb, KADM5_CONFIG_DBNAME); set_conf(conf, stash_file, stash_file, KADM5_CONFIG_STASH_FILE); - set_conf(conf, admin_server, writable_kadmin_server, KADM5_CONFIG_ADMIN_SERVER); - set_conf(conf, readonly_admin_server, kadmin_server, - KADM5_CONFIG_READONLY_ADMIN_SERVER); /* * If we have a local HDB we'll use it if we can. If the local HDB is @@ -425,6 +422,11 @@ get_kadm_handle(krb5_context context, * * Note that kadmin_client_keytab can be an HDB: or HDBGET: keytab. */ + if (writable_kadmin_server) + set_conf(conf, admin_server, writable_kadmin_server, KADM5_CONFIG_ADMIN_SERVER); + if (kadmin_server) + set_conf(conf, readonly_admin_server, kadmin_server, + KADM5_CONFIG_READONLY_ADMIN_SERVER); ret = kadm5_c_init_with_skey_ctx(context, kadmin_client_name, kadmin_client_keytab, @@ -761,13 +763,11 @@ bad_reqv(kadmin_request_desc r, char *formatted = NULL; char *msg = NULL; - if (r && r->context) - context = r->context; - if (r && r->hcontext && r->kv) + context = r->context; + if (r->hcontext && r->kv) heim_audit_setkv_number((heim_svc_req_desc)r, "http-status-code", http_status_code); - if (r) - (void) gettimeofday(&r->tv_end, NULL); + (void) gettimeofday(&r->tv_end, NULL); if (code == ENOMEM) { if (context) krb5_log_msg(context, logfac, 1, NULL, "Out of memory"); @@ -790,7 +790,7 @@ bad_reqv(kadmin_request_desc r, msg = formatted; formatted = NULL; } - if (r && r->hcontext) + if (r->hcontext) heim_audit_addreason((heim_svc_req_desc)r, "%s", formatted); krb5_free_error_message(context, k5msg); @@ -2034,7 +2034,7 @@ make_csrf_token(kadmin_request_desc r, if (ret == 0 && data.length > INT_MAX) ret = ERANGE; if (ret == 0 && - (dlen = rk_base64_encode(data.data, data.length, token)) < 0) + rk_base64_encode(data.data, data.length, token) < 0) ret = errno; krb5_storage_free(sp); krb5_data_free(&data); @@ -2139,6 +2139,7 @@ ip(void *cls, if (ftl == NULL || keydup == NULL || valdup == NULL) { free(ftl); free(keydup); + free(valdup); return MHD_NO; } ftl->freeme1 = keydup; @@ -2186,11 +2187,8 @@ route(void *cls, * handling a POST then we'll also get called with upload_data != NULL, * possibly multiple times. */ - if ((ret = set_req_desc(connection, method, url, &r))) { - return - bad_503(r, ret, "Could not initialize request state") == -1 - ? MHD_NO : MHD_YES; - } + if ((ret = set_req_desc(connection, method, url, &r))) + return MHD_NO; *ctx = r; /* @@ -2512,6 +2510,11 @@ main(int argc, char **argv) if (port < 0) errx(1, "Port number must be given"); + if (writable_kadmin_server == NULL && kadmin_server == NULL && + !local_hdb && !local_hdb_read_only) + errx(1, "One of --local or --local-read-only must be given, or a " + "remote kadmind must be given"); + if (audiences.num_strings == 0) { char localhost[MAXHOSTNAMELEN]; diff --git a/third_party/heimdal/kdc/ipc_csr_authorizer.c b/third_party/heimdal/kdc/ipc_csr_authorizer.c index 7d77e7f812a..86717f6f9e0 100644 --- a/third_party/heimdal/kdc/ipc_csr_authorizer.c +++ b/third_party/heimdal/kdc/ipc_csr_authorizer.c @@ -197,8 +197,189 @@ cmd_append(struct rk_strpool **cmd, const char *s0, ...) return ret; } +/* Like strpbrk(), but from the end of the string */ +static char * +strrpbrk(char *s, const char *accept) +{ + char *last = NULL; + char *p = s; + + do { + p = strpbrk(p, accept); + if (p != NULL) { + last = p; + p++; + } + } while (p != NULL); + return last; +} + +/* + * For /get-tgts we need to support partial authorization of requests. The + * hx509_request APIs support that. + * + * Here we just step through the IPC server's response and mark the + * corresponding request elements authorized so that /get-tgts can issue or not + * issue TGTs according to which requested principals are authorized and which + * are not. + */ static int -call_svc(krb5_context context, heim_ipc ipc, const char *cmd) +mark_piecemeal_authorized(krb5_context context, + hx509_request csr, + heim_octet_string *rep) +{ + size_t san_idx = 0; + size_t eku_idx = 0; + char *s, *p, *rep2, *tok, *next = NULL; + int slow_path = 0; + int partial = 0; + int ret = 0; + + /* We have a data, but we want a C string */ + if ((rep2 = strndup(rep->data, rep->length)) == NULL) + return krb5_enomem(context); + + /* The first token should be "denied"; skip it */ + if ((s = strchr(rep2, ' ')) == NULL) { + free(rep2); + return EACCES; + } + s++; + + while ((tok = strtok_r(s, ",", &next))) { + hx509_san_type san_type, san_type2; + char *s2 = NULL; + + s = NULL; /* for strtok_r() */ + + if (strncmp(tok, "eku=", sizeof("eku=") -1) == 0) { + /* + * Very simplistic handling of partial authz for EKUs: + * + * - denial of an EKU -> deny the whole request + * - else below mark all EKUs approved + */ + if (strstr(tok, ":denied")) { + krb5_set_error_message(context, EACCES, "CSR denied because " + "EKU denied: %s", tok); + ret = EACCES; + break; + } + continue; + } + + /* + * For SANs we check that the nth SAN in the response matches the nth + * SAN in the hx509_request. + */ + + if (strncmp(tok, "san_pkinit=", sizeof("san_pkinit=") - 1) == 0) { + tok += sizeof("san_pkinit=") - 1; + san_type = HX509_SAN_TYPE_PKINIT; + } else if (strncmp(tok, "san_dnsname=", sizeof("san_dnsname=") -1) == 0) { + tok += sizeof("san_dnsname=") - 1; + san_type = HX509_SAN_TYPE_DNSNAME; + } else if (strncmp(tok, "san_email=", sizeof("san_email=") -1) == 0) { + tok += sizeof("san_email=") - 1; + san_type = HX509_SAN_TYPE_EMAIL; + } else if (strncmp(tok, "san_xmpp=", sizeof("san_xmpp=") -1) == 0) { + tok += sizeof("san_xmpp=") - 1; + san_type = HX509_SAN_TYPE_XMPP; + } else if (strncmp(tok, "san_ms_upn=", sizeof("san_ms_upn=") -1) == 0) { + tok += sizeof("san_ms_upn=") - 1; + san_type = HX509_SAN_TYPE_MS_UPN; + } else { + krb5_set_error_message(context, EACCES, "CSR denied because could " + "not parse token in response: %s", tok); + ret = EACCES; + break; + } + + /* + * This token has to end in ":granted" or ":denied". Using our + * `strrpbrk()' means we can deal with principals names that have ':' + * in them. + */ + if ((p = strrpbrk(tok, ":")) == NULL) { + san_idx++; + continue; + } + *(p++) = '\0'; + + /* Now we get the nth SAN from the authorization */ + ret = hx509_request_get_san(csr, san_idx, &san_type2, &s2); + if (ret == HX509_NO_ITEM) { + /* See below */ + slow_path = 1; + break; + } + + /* And we check that it matches the SAN in this token */ + if (ret == 0) { + if (san_type != san_type2 || + strcmp(tok, s2) != 0) { + /* + * We expect the tokens in the reply to be in the same order as + * in the request. If not, we must take a slow path where we + * have to sort requests and responses then iterate them in + * order. + */ + slow_path = 1; + hx509_xfree(s2); + break; + } + hx509_xfree(s2); + + if (strcmp(p, "granted") == 0) { + ret = hx509_request_authorize_san(csr, san_idx); + } else { + partial = 1; + ret = hx509_request_reject_san(csr, san_idx); + } + if (ret) + break; + } + san_idx++; + } + + if (slow_path) { + /* + * FIXME? Implement the slow path? + * + * Basically, we'd get all the SANs from the request into an array of + * {SAN, index} and sort that array, then all the SANs from the + * response into an array and sort it, then step a cursor through both, + * using the index from the first to mark SANs in the request + * authorized or rejected. + */ + krb5_set_error_message(context, EACCES, "CSR denied because " + "authorizer service did not include all " + "piecemeal grants/denials in order"); + ret = EACCES; + } + + /* Mark all the EKUs authorized */ + for (eku_idx = 0; ret == 0; eku_idx++) + ret = hx509_request_authorize_eku(csr, eku_idx); + if (ret == HX509_NO_ITEM) + ret = 0; + if (ret == 0 && partial) { + krb5_set_error_message(context, EACCES, "CSR partially authorized"); + ret = EACCES; + } + + free(rep2); + return ret; +} + +static krb5_error_code mark_authorized(hx509_request); + +static int +call_svc(krb5_context context, + heim_ipc ipc, + hx509_request csr, + const char *cmd, + int piecemeal_check_ok) { heim_octet_string req, resp; int ret; @@ -207,40 +388,66 @@ call_svc(krb5_context context, heim_ipc ipc, const char *cmd) req.length = strlen(cmd); resp.length = 0; resp.data = NULL; - if ((ret = heim_ipc_call(ipc, &req, &resp, NULL))) { - if (resp.length && resp.length < INT_MAX) { - krb5_set_error_message(context, ret, "CSR denied: %.*s", - (int)resp.length, (const char *)resp.data); - ret = EACCES; - } else { - krb5_set_error_message(context, EACCES, "CSR denied because could " - "not reach CSR authorizer IPC service"); + ret = heim_ipc_call(ipc, &req, &resp, NULL); + + /* Check for all granted case */ + if (ret == 0 && + resp.length == sizeof("granted") - 1 && + strncasecmp(resp.data, "granted", sizeof("granted") - 1) == 0) { + free(resp.data); + return mark_authorized(csr); /* Full approval */ + } + + /* Check for "denied ..." piecemeal authorization case */ + if ((ret == 0 || ret == EACCES || ret == KRB5_PLUGIN_NO_HANDLE) && + piecemeal_check_ok && + resp.length > sizeof("denied") - 1 && + strncasecmp(resp.data, "denied", sizeof("denied") - 1) == 0) { + /* Piecemeal authorization */ + ret = mark_piecemeal_authorized(context, csr, &resp); + + /* mark_piecemeal_authorized() should return EACCES; just in case: */ + if (ret == 0) ret = EACCES; - } + free(resp.data); return ret; } + + /* All other failure cases */ + if (resp.data == NULL || resp.length == 0) { - free(resp.data); krb5_set_error_message(context, ret, "CSR authorizer IPC service " "failed silently"); + free(resp.data); return EACCES; } + + if (resp.length == sizeof("ignore") - 1 && + strncasecmp(resp.data, "ignore", sizeof("ignore") - 1) == 0) { + /* + * In this case the server is saying "I can't handle this request, try + * some other authorizer plugin". + */ + free(resp.data); + return KRB5_PLUGIN_NO_HANDLE; + } + if (resp.length == sizeof("denied") - 1 && strncasecmp(resp.data, "denied", sizeof("denied") - 1) == 0) { - free(resp.data); krb5_set_error_message(context, ret, "CSR authorizer rejected %s", cmd); - return EACCES; - } - if (resp.length == sizeof("granted") - 1 && - strncasecmp(resp.data, "granted", sizeof("granted") - 1) == 0) { free(resp.data); - return 0; + return EACCES; } - krb5_set_error_message(context, ret, "CSR authorizer failed %s: %.*s", - cmd, resp.length < INT_MAX ? (int)resp.length : 0, - resp.data); - return EACCES; + + if (resp.length > INT_MAX) + krb5_set_error_message(context, ret, "CSR authorizer rejected %s", cmd); + else + krb5_set_error_message(context, ret, "CSR authorizer rejected %s: %.*s", + cmd, resp.length, resp.data); + + free(resp.data); + return ret; } static void @@ -294,13 +501,25 @@ authorize(void *ctx, char *princ = NULL; char *s = NULL; int do_check = 0; + int piecemeal_check_ok = 1; - if ((svc = krb5_config_get_string(context, NULL, app ? app : "kdc", - "ipc_csr_authorizer", "service", NULL)) - == NULL) + if ((svc = krb5_config_get_string_default(context, NULL, + "ANY:org.h5l.csr_authorizer", + app ? app : "kdc", + "ipc_csr_authorizer", "service", + NULL)) == NULL) return KRB5_PLUGIN_NO_HANDLE; if ((ret = heim_ipc_init_context(svc, &ipc))) { + /* + * If the IPC authorizer is optional, then fallback on whatever is + * next. + */ + if (krb5_config_get_bool_default(context, NULL, FALSE, + app ? app : "kdc", + "ipc_csr_authorizer", "optional", + NULL)) + return KRB5_PLUGIN_NO_HANDLE; krb5_set_error_message(context, ret, "Could not set up IPC client " "end-point for service %s", svc); return ret; @@ -318,10 +537,22 @@ authorize(void *ctx, for (i = 0; ret == 0; i++) { hx509_san_type san_type; + size_t p; ret = hx509_request_get_san(csr, i, &san_type, &s); if (ret) break; + + /* + * We cannot do a piecemeal check if any of the SANs could make the + * response ambiguous. + */ + p = strcspn(s, ",= "); + if (s[p] != '\0') + piecemeal_check_ok = 0; + if (piecemeal_check_ok && strstr(s, ":granted") != NULL) + piecemeal_check_ok = 0; + switch (san_type) { case HX509_SAN_TYPE_EMAIL: if ((ret = cmd_append(&cmd, " san_email=", s, NULL))) @@ -380,15 +611,13 @@ authorize(void *ctx, hx509_request_authorize_ku(csr, ku); if (do_check) { - if ((s = rk_strpoolcollect(cmd)) == NULL) - goto enomem; + s = rk_strpoolcollect(cmd); cmd = NULL; - if ((ret = call_svc(context, ipc, s))) + if (s == NULL) + goto enomem; + if ((ret = call_svc(context, ipc, csr, s, piecemeal_check_ok))) goto out; - } /* else -> permit */ - - if ((ret = mark_authorized(csr))) - goto out; + } /* else there was nothing to check -> permit */ *result = TRUE; ret = 0; diff --git a/third_party/heimdal/kdc/kdc-plugin.c b/third_party/heimdal/kdc/kdc-plugin.c index 925c250597a..c575d7df479 100644 --- a/third_party/heimdal/kdc/kdc-plugin.c +++ b/third_party/heimdal/kdc/kdc-plugin.c @@ -51,7 +51,7 @@ static const char *kdc_plugin_deps[] = { static struct heim_plugin_data kdc_plugin_data = { "krb5", "kdc", - KRB5_PLUGIN_KDC_VERSION_10, + KRB5_PLUGIN_KDC_VERSION_11, kdc_plugin_deps, kdc_get_instance }; @@ -145,7 +145,8 @@ struct verify_uc { hdb_entry *client; hdb_entry *server; hdb_entry *krbtgt; - krb5_pac *pac; + krb5_pac pac; + krb5_boolean *is_trusted; }; static krb5_error_code KRB5_LIB_CALL @@ -162,7 +163,8 @@ verify(krb5_context context, const void *plug, void *plugctx, void *userctx) uc->r, uc->client_principal, uc->delegated_proxy_principal, - uc->client, uc->server, uc->krbtgt, uc->pac); + uc->client, uc->server, uc->krbtgt, uc->pac, + uc->is_trusted); return ret; } @@ -173,7 +175,8 @@ _kdc_pac_verify(astgs_request_t r, hdb_entry *client, hdb_entry *server, hdb_entry *krbtgt, - krb5_pac *pac) + krb5_pac pac, + krb5_boolean *is_trusted) { struct verify_uc uc; @@ -187,11 +190,66 @@ _kdc_pac_verify(astgs_request_t r, uc.server = server; uc.krbtgt = krbtgt; uc.pac = pac; + uc.is_trusted = is_trusted; return _krb5_plugin_run_f(r->context, &kdc_plugin_data, 0, &uc, verify); } +struct update_uc { + astgs_request_t r; + krb5_principal client_principal; + krb5_principal delegated_proxy_principal; + hdb_entry *client; + hdb_entry *server; + hdb_entry *krbtgt; + krb5_pac *pac; +}; + +static krb5_error_code KRB5_LIB_CALL +update(krb5_context context, const void *plug, void *plugctx, void *userctx) +{ + const krb5plugin_kdc_ftable *ft = (const krb5plugin_kdc_ftable *)plug; + struct update_uc *uc = (struct update_uc *)userctx; + krb5_error_code ret; + + if (ft->pac_update == NULL) + return KRB5_PLUGIN_NO_HANDLE; + + ret = ft->pac_update((void *)plug, + uc->r, + uc->client_principal, + uc->delegated_proxy_principal, + uc->client, uc->server, uc->krbtgt, uc->pac); + return ret; +} + +krb5_error_code +_kdc_pac_update(astgs_request_t r, + const krb5_principal client_principal, + const krb5_principal delegated_proxy_principal, + hdb_entry *client, + hdb_entry *server, + hdb_entry *krbtgt, + krb5_pac *pac) +{ + struct update_uc uc; + + if (!have_plugin) + return KRB5_PLUGIN_NO_HANDLE; + + uc.r = r; + uc.client_principal = client_principal; + uc.delegated_proxy_principal = delegated_proxy_principal; + uc.client = client; + uc.server = server; + uc.krbtgt = krbtgt; + uc.pac = pac; + + return _krb5_plugin_run_f(r->context, &kdc_plugin_data, + 0, &uc, update); +} + static krb5_error_code KRB5_LIB_CALL check(krb5_context context, const void *plug, void *plugctx, void *userctx) { diff --git a/third_party/heimdal/kdc/kdc-plugin.h b/third_party/heimdal/kdc/kdc-plugin.h index 05286257bf7..4ec92a575b3 100644 --- a/third_party/heimdal/kdc/kdc-plugin.h +++ b/third_party/heimdal/kdc/kdc-plugin.h @@ -57,8 +57,9 @@ typedef krb5_error_code /* * Verify the PAC KDC signatures by fetching the appropriate TGS key - * and calling krb5_pac_verify() with that key. Optionally update the - * PAC buffers on success. + * and calling krb5_pac_verify() with that key. The possibly-NULL + * is_trusted may be set by the plugin to indicate that the PAC was + * issued by a trusted server, and not, for example, by an RODC. */ typedef krb5_error_code @@ -69,7 +70,25 @@ typedef krb5_error_code hdb_entry *,/* client */ hdb_entry *,/* server */ hdb_entry *,/* krbtgt */ - krb5_pac *); + krb5_pac, /* pac */ + krb5_boolean *); /* is_trusted */ + +/* + * Update the KDC PAC buffers. This function may be used after verifying the PAC + * with a call to krb5plugin_kdc_pac_verify(), and it resembles the latter + * function in the parameters it takes. The 'pac' parameter always points to a + * non-NULL PAC. + */ + +typedef krb5_error_code +(KRB5_CALLCONV *krb5plugin_kdc_pac_update)(void *, + astgs_request_t, + const krb5_principal, /* new ticket client */ + const krb5_principal, /* delegation proxy */ + hdb_entry *,/* client */ + hdb_entry *,/* server */ + hdb_entry *,/* krbtgt */ + krb5_pac *); /* pac */ /* * Authorize the client principal's access to the Authentication Service (AS). @@ -117,12 +136,13 @@ typedef krb5_error_code * Plugins should carefully check API contract notes for changes * between plugin API versions. */ -#define KRB5_PLUGIN_KDC_VERSION_10 10 +#define KRB5_PLUGIN_KDC_VERSION_11 11 typedef struct krb5plugin_kdc_ftable { HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context); krb5plugin_kdc_pac_generate pac_generate; krb5plugin_kdc_pac_verify pac_verify; + krb5plugin_kdc_pac_update pac_update; krb5plugin_kdc_client_access client_access; krb5plugin_kdc_referral_policy referral_policy; krb5plugin_kdc_finalize_reply finalize_reply; diff --git a/third_party/heimdal/kdc/kdc-tester.c b/third_party/heimdal/kdc/kdc-tester.c index 65d52ec66a4..beb9e1f4a23 100644 --- a/third_party/heimdal/kdc/kdc-tester.c +++ b/third_party/heimdal/kdc/kdc-tester.c @@ -195,11 +195,14 @@ copy_keytab(krb5_context context, krb5_keytab from, krb5_keytab to) ret = krb5_kt_start_seq_get(context, from, &cursor); if (ret) return ret; - while((ret = krb5_kt_next_entry(context, from, &entry, &cursor)) == 0){ + while ((ret = krb5_kt_next_entry(context, from, &entry, &cursor)) == 0){ krb5_kt_add_entry(context, to, &entry); krb5_kt_free_entry(context, &entry); } - return krb5_kt_end_seq_get(context, from, &cursor); + (void) krb5_kt_end_seq_get(context, from, &cursor); + if (ret == KRB5_KT_END) + return 0; + return ret; } /* diff --git a/third_party/heimdal/kdc/kerberos5.c b/third_party/heimdal/kdc/kerberos5.c index e75686c625a..ecca52cdcdd 100644 --- a/third_party/heimdal/kdc/kerberos5.c +++ b/third_party/heimdal/kdc/kerberos5.c @@ -2079,13 +2079,11 @@ get_local_tgs(krb5_context context, KRB5_TGS_NAME, realm, NULL); - if (ret) - return ret; + if (ret == 0) + ret = _kdc_db_fetch(context, config, tgs_name, + HDB_F_GET_KRBTGT, NULL, krbtgtdb, krbtgt); - ret = _kdc_db_fetch(context, config, tgs_name, - HDB_F_GET_KRBTGT, NULL, krbtgtdb, krbtgt); krb5_free_principal(context, tgs_name); - return ret; } diff --git a/third_party/heimdal/kdc/krb5tgs.c b/third_party/heimdal/kdc/krb5tgs.c index 71991c17975..0bad42aa3b7 100644 --- a/third_party/heimdal/kdc/krb5tgs.c +++ b/third_party/heimdal/kdc/krb5tgs.c @@ -96,6 +96,7 @@ _kdc_check_pac(astgs_request_t r, krb5_pac pac = NULL; krb5_error_code ret; krb5_boolean signedticket; + krb5_boolean is_trusted = FALSE; *kdc_issued = FALSE; *ppac = NULL; @@ -122,32 +123,25 @@ _kdc_check_pac(astgs_request_t r, return ret; } - if (pac_canon_name) { - ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name); - if (ret && ret != ENOENT) { - krb5_pac_free(context, pac); - return ret; - } - } - if (pac_attributes) { - ret = _krb5_pac_get_attributes_info(context, pac, pac_attributes); - if (ret && ret != ENOENT) { - krb5_pac_free(context, pac); - return ret; - } - if (ret == ENOENT) - *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY; - } - /* Verify the KDC signatures. */ ret = _kdc_pac_verify(r, client_principal, delegated_proxy_principal, - client, server, krbtgt, &pac); + client, server, krbtgt, pac, &is_trusted); if (ret == 0) { - if (pac == NULL) { - /* the plugin may indicate no PAC should be generated */ - *pac_attributes = 0; + if (is_trusted) { + krb5_pac_set_trusted(pac, true); } + + if (pac_canon_name) { + ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name); + if (ret && ret != ENOENT) { + krb5_pac_free(context, pac); + return ret; + } + } + if (pac_attributes && + _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0) + *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY; } else if (ret == KRB5_PLUGIN_NO_HANDLE) { /* * We can't verify the KDC signatures if the ticket was issued by @@ -163,6 +157,17 @@ _kdc_check_pac(astgs_request_t r, } } + if (pac_canon_name) { + ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name); + if (ret && ret != ENOENT) { + krb5_pac_free(context, pac); + return ret; + } + } + if (pac_attributes && + _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0) + *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY; + /* Discard the PAC if the plugin didn't handle it */ krb5_pac_free(context, pac); ret = krb5_pac_init(context, &pac); @@ -1051,8 +1056,9 @@ next_kvno: } else if (ret) { char *str = NULL, *p = NULL; - krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str); - krb5_unparse_name(r->context, princ, &p); + /* We should implement the MIT `trace_format()' concept */ + (void) krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str); + (void) krb5_unparse_name(r->context, princ, &p); kdc_log(r->context, config, 4, "No server key with enctype %s found for %s", str ? str : "<unknown enctype>", @@ -1327,6 +1333,7 @@ _kdc_db_fetch_client(krb5_context context, krb5_error_code ret; hdb_entry *client = NULL; + *clientdb = NULL; *client_out = NULL; ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags, @@ -1382,7 +1389,7 @@ tgs_build_reply(astgs_request_t priv, char *user2user_name = NULL; HDB *user2user_krbtgtdb; hdb_entry *user2user_krbtgt = NULL; - HDB *clientdb; + HDB *clientdb = NULL; HDB *serverdb = NULL; krb5_realm ref_realm = NULL; EncTicketPart *tgt = &priv->ticket->ticket; @@ -1896,9 +1903,16 @@ server_lookup: cpn, our_realm, &clientdb, &priv->client); if (ret) goto out; - flags &= ~HDB_F_SYNTHETIC_OK; + /* flags &= ~HDB_F_SYNTHETIC_OK; */ /* `flags' is not used again below */ priv->clientdb = clientdb; + /* Validate armor TGT before potentially including device claims */ + if (priv->armor_ticket) { + ret = _kdc_fast_check_armor_pac(priv); + if (ret) + goto out; + } + ret = _kdc_check_pac(priv, priv->client_princ, NULL, priv->client, priv->server, priv->krbtgt, priv->krbtgt, @@ -1915,6 +1929,29 @@ server_lookup: goto out; } + if (priv->pac != NULL) { + ret = _kdc_pac_update(priv, priv->client_princ, NULL, + priv->client, priv->server, priv->krbtgt, + &priv->pac); + if (ret == KRB5_PLUGIN_NO_HANDLE) { + ret = 0; + } + if (ret) { + const char *msg = krb5_get_error_message(context, ret); + kdc_audit_addreason((kdc_request_t)priv, "PAC update failed"); + kdc_log(context, config, 4, + "Update PAC failed for %s (%s) from %s with %s", + spn, cpn, from, msg); + krb5_free_error_message(context, msg); + goto out; + } + + if (priv->pac == NULL) { + /* the plugin may indicate no PAC should be generated */ + priv->pac_attributes = 0; + } + } + /* * Process request */ @@ -2011,13 +2048,6 @@ server_lookup: if (kdc_issued && !krb5_principal_is_krbtgt(context, priv->server->principal)) { - /* Validate armor TGT before potentially including device claims */ - if (priv->armor_ticket) { - ret = _kdc_fast_check_armor_pac(priv); - if (ret) - goto out; - } - add_ticket_sig = TRUE; } diff --git a/third_party/heimdal/kdc/misc.c b/third_party/heimdal/kdc/misc.c index eab8107935f..477e4fabfb2 100644 --- a/third_party/heimdal/kdc/misc.c +++ b/third_party/heimdal/kdc/misc.c @@ -139,6 +139,8 @@ _kdc_db_fetch(krb5_context context, krb5_const_principal princ; *h = NULL; + if (db) + *db = NULL; if (!name_type_ok(context, config, principal)) return HDB_ERR_NOENTRY; diff --git a/third_party/heimdal/kdc/mit_dump.c b/third_party/heimdal/kdc/mit_dump.c index 32cf5dc65ce..af380bbe378 100644 --- a/third_party/heimdal/kdc/mit_dump.c +++ b/third_party/heimdal/kdc/mit_dump.c @@ -104,7 +104,7 @@ my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp) size_t len; size_t sz = *szp; char *buf = *bufp; - char *p, *n; + char *n; if (!buf) { buf = malloc(sz ? sz : 8192); @@ -115,7 +115,7 @@ my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp) } len = 0; - while ((p = fgets(&buf[len], sz-len, f)) != NULL) { + while (fgets(&buf[len], sz-len, f) != NULL) { len += strlen(&buf[len]); if (buf[len-1] == '\n') break; diff --git a/third_party/heimdal/kdc/mssfu.c b/third_party/heimdal/kdc/mssfu.c index fda5a37b1c6..a88de097a9b 100644 --- a/third_party/heimdal/kdc/mssfu.c +++ b/third_party/heimdal/kdc/mssfu.c @@ -501,6 +501,26 @@ validate_constrained_delegation(astgs_request_t r) goto out; } + heim_assert(s4u_pac != NULL, "ad_kdc_issued implies the PAC is non-NULL"); + + ret = _kdc_pac_update(r, s4u_client_name, s4u_server_name, + s4u_client, r->server, r->krbtgt, + &s4u_pac); + if (ret == KRB5_PLUGIN_NO_HANDLE) { + ret = 0; + } + if (ret) { + const char *msg = krb5_get_error_message(r->context, ret); + kdc_audit_addreason((kdc_request_t)r, + "Constrained delegation ticket PAC update failed"); + kdc_log(r->context, r->config, 4, + "Update delegated PAC failed to %s for client" + "%s (%s) as %s from %s with %s", + r->sname, r->cname, s4usname, s4ucname, r->from, msg); + krb5_free_error_message(r->context, msg); + goto out; + } + /* * If the evidence ticket PAC didn't include PAC_UPN_DNS_INFO with * the canonical client name, but the user is local to our KDC, we diff --git a/third_party/heimdal/kdc/pkinit-ec.c b/third_party/heimdal/kdc/pkinit-ec.c index c718aa79962..31a5fe7dec5 100644 --- a/third_party/heimdal/kdc/pkinit-ec.c +++ b/third_party/heimdal/kdc/pkinit-ec.c @@ -52,10 +52,16 @@ */ #ifdef HAVE_HCRYPTO_W_OPENSSL -#include <openssl/ec.h> -#include <openssl/ecdh.h> #include <openssl/evp.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/rsa.h> #include <openssl/bn.h> +#include <openssl/dh.h> +#include <openssl/objects.h> +#ifdef HAVE_OPENSSL_30 +#include <openssl/core_names.h> +#endif #define HEIM_NO_CRYPTO_HDRS #endif /* HAVE_HCRYPTO_W_OPENSSL */ @@ -69,37 +75,101 @@ #include <pkinit_asn1.h> #include <hx509.h> - -#ifdef HAVE_HCRYPTO_W_OPENSSL -static void -free_client_ec_param(krb5_context context, - EC_KEY *ec_key_pk, - EC_KEY *ec_key_key) -{ - if (ec_key_pk != NULL) - EC_KEY_free(ec_key_pk); - if (ec_key_key != NULL) - EC_KEY_free(ec_key_key); -} -#endif +#include "../lib/hx509/hx_locl.h" +#include <hx509-private.h> void _kdc_pk_free_client_ec_param(krb5_context context, - void *ec_key_pk, - void *ec_key_key) + void *k0, + void *k1) { #ifdef HAVE_HCRYPTO_W_OPENSSL - free_client_ec_param(context, ec_key_pk, ec_key_key); +#ifdef HAVE_OPENSSL_30 + EVP_PKEY_free(k0); + EVP_PKEY_free(k1); +#else + EC_KEY_free(k0); + EC_KEY_free(k1); +#endif #endif } #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 +static krb5_error_code +generate_ecdh_keyblock_ossl30(krb5_context context, + EVP_PKEY *ec_key_pub, /* the client's public key */ + EVP_PKEY **ec_key_priv, /* the KDC's ephemeral private */ + unsigned char **dh_gen_key, /* shared secret */ + size_t *dh_gen_keylen) +{ + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *ephemeral = NULL; + krb5_error_code ret = 0; + unsigned char *p = NULL; + size_t size = 0; + + if (ec_key_pub == NULL) + /* XXX This seems like an internal error that should be impossible */ + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Missing client ECDH key agreement public key"); + if (ret == 0 && + (ephemeral = + EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1))) == NULL) + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Could not generate an ECDH key agreement private key"); + if (ret == 0 && + (pctx = EVP_PKEY_CTX_new(ephemeral, NULL)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1) + ret = krb5_enomem(context); + if (ret == 0 && + EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1) + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Could not generate an ECDH key agreement private key " + "(EVP_PKEY_CTX_set_dh_kdf_type)"); + if (ret == 0 && + EVP_PKEY_derive_set_peer_ex(pctx, ec_key_pub, 1) != 1) + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Could not generate an ECDH key agreement private key " + "(EVP_PKEY_derive_set_peer_ex)"); + if (ret == 0 && + (EVP_PKEY_derive(pctx, NULL, &size) != 1 || size == 0)) + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Could not generate an ECDH key agreement private key " + "(EVP_PKEY_derive)"); + if (ret == 0 && (p = malloc(size)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && + (EVP_PKEY_derive(pctx, p, &size) != 1 || size == 0)) + krb5_set_error_message(context, ret = KRB5KRB_ERR_GENERIC, + "Could not generate an ECDH key agreement private key " + "(EVP_PKEY_derive)"); + + if (ret) { + EVP_PKEY_free(ephemeral); + ephemeral = NULL; + free(p); + p = NULL; + size = 0; + } + + *ec_key_priv = ephemeral; + *dh_gen_keylen = size; + *dh_gen_key = p; + + EVP_PKEY_CTX_free(pctx); + return ret; +} +#else + +/* The empty line above is intentional to work around an mkproto bug */ static krb5_error_code -generate_ecdh_keyblock(krb5_context context, - EC_KEY *ec_key_pk, /* the client's public key */ - EC_KEY **ec_key_key, /* the KDC's ephemeral private */ - unsigned char **dh_gen_key, /* shared secret */ - size_t *dh_gen_keylen) +generate_ecdh_keyblock_ossl11(krb5_context context, + EC_KEY *ec_key_pk, /* the client's public key */ + EC_KEY **ec_key_key, /* the KDC's ephemeral private */ + unsigned char **dh_gen_key, /* shared secret */ + size_t *dh_gen_keylen) { const EC_GROUP *group; EC_KEY *ephemeral; @@ -136,7 +206,7 @@ generate_ecdh_keyblock(krb5_context context, EC_KEY_set_group(ephemeral, group); if (EC_KEY_generate_key(ephemeral) != 1) { - EC_KEY_free(ephemeral); + EC_KEY_free(ephemeral); return krb5_enomem(context); } @@ -165,6 +235,7 @@ generate_ecdh_keyblock(krb5_context context, return 0; } +#endif #endif /* HAVE_HCRYPTO_W_OPENSSL */ krb5_error_code @@ -175,20 +246,128 @@ _kdc_generate_ecdh_keyblock(krb5_context context, size_t *dh_gen_keylen) { #ifdef HAVE_HCRYPTO_W_OPENSSL - return generate_ecdh_keyblock(context, ec_key_pk, - (EC_KEY **)ec_key_key, - dh_gen_key, dh_gen_keylen); +#ifdef HAVE_OPENSSL_30 + return generate_ecdh_keyblock_ossl30(context, ec_key_pk, + (EVP_PKEY **)ec_key_key, + dh_gen_key, dh_gen_keylen); +#else + return generate_ecdh_keyblock_ossl11(context, ec_key_pk, + (EC_KEY **)ec_key_key, + dh_gen_key, dh_gen_keylen); +#endif #else return ENOTSUP; #endif /* HAVE_HCRYPTO_W_OPENSSL */ } #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 static krb5_error_code -get_ecdh_param(krb5_context context, - krb5_kdc_configuration *config, - SubjectPublicKeyInfo *dh_key_info, - EC_KEY **out) +get_ecdh_param_ossl30(krb5_context context, + krb5_kdc_configuration *config, + SubjectPublicKeyInfo *dh_key_info, + EVP_PKEY **out) +{ + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *template = NULL; + EVP_PKEY *public = NULL; + OSSL_PARAM params[2]; + krb5_error_code ret = 0; + ECParameters ecp; + const unsigned char *p; + const char *curve_sn = NULL; + size_t len; + char *curve_sn_dup = NULL; + int groupnid = NID_undef; + + /* XXX Algorithm agility; XXX KRB5_BADMSGTYPE?? */ + + /* + * In order for d2i_PublicKey() to work we need to create a template key + * that has the curve parameters for the subjectPublicKey. + * + * Or maybe we could learn to use the OSSL_DECODER(3) API. But this works, + * at least until OpenSSL deprecates d2i_PublicKey() and forces us to use + * OSSL_DECODER(3). + */ + + memset(&ecp, 0, sizeof(ecp)); + + if (dh_key_info->algorithm.parameters == NULL) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "PKINIT missing algorithm parameter " + "in clientPublicValue"); + if (ret == 0) + ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, + dh_key_info->algorithm.parameters->length, + &ecp, &len); + if (ret == 0 && ecp.element != choice_ECParameters_namedCurve) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "PKINIT client used an unnamed curve"); + if (ret == 0 && + (groupnid = _hx509_ossl_oid2nid(&ecp.u.namedCurve)) == NID_undef) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "PKINIT client used an unsupported curve"); + if (ret == 0 && (curve_sn = OBJ_nid2sn(groupnid)) == NULL) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "Could not resolve curve NID %d to its short name", + groupnid); + if (ret == 0 && (curve_sn_dup = strdup(curve_sn)) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) != 0) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "PKINIT client used an unsupported curve"); + } + if (ret == 0) { + /* + * Apparently there's no error checking to be done here? Why does + * OSSL_PARAM_construct_utf8_string() want a non-const for the value? + * Is that a bug in OpenSSL? + */ + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + curve_sn_dup, 0); + params[1] = OSSL_PARAM_construct_end(); + + if ((pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) + ret = krb5_enomem(context); + } + if (ret == 0 && EVP_PKEY_fromdata_init(pctx) != 1) + ret = krb5_enomem(context); + if (ret == 0 && + EVP_PKEY_fromdata(pctx, &template, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + params) != 1) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "Could not set up to parse key for curve %s", + curve_sn); + + p = dh_key_info->subjectPublicKey.data; + len = dh_key_info->subjectPublicKey.length / 8; + if (ret == 0 && + (public = d2i_PublicKey(EVP_PKEY_EC, &template, &p, len)) == NULL) + krb5_set_error_message(context, ret = KRB5_BADMSGTYPE, + "Could not decode PKINIT client ECDH key"); + + if (ret) { + EVP_PKEY_free(public); + public = NULL; + } + + *out = public; + + /* FYI the EVP_PKEY_CTX takes ownership of the `template' key */ + EVP_PKEY_CTX_free(pctx); + free_ECParameters(&ecp); + free(curve_sn_dup); + return ret; +} +#else + +static krb5_error_code +get_ecdh_param_ossl11(krb5_context context, + krb5_kdc_configuration *config, + SubjectPublicKeyInfo *dh_key_info, + EC_KEY **out) { ECParameters ecp; EC_KEY *public = NULL; @@ -198,30 +377,31 @@ get_ecdh_param(krb5_context context, int nid; if (dh_key_info->algorithm.parameters == NULL) { - krb5_set_error_message(context, KRB5_BADMSGTYPE, - "PKINIT missing algorithm parameter " - "in clientPublicValue"); - return KRB5_BADMSGTYPE; + krb5_set_error_message(context, KRB5_BADMSGTYPE, + "PKINIT missing algorithm parameter " + "in clientPublicValue"); + return KRB5_BADMSGTYPE; } + /* XXX Algorithm agility; XXX KRB5_BADMSGTYPE?? */ memset(&ecp, 0, sizeof(ecp)); ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, - dh_key_info->algorithm.parameters->length, &ecp, &len); + dh_key_info->algorithm.parameters->length, &ecp, &len); if (ret) - goto out; + goto out; if (ecp.element != choice_ECParameters_namedCurve) { - ret = KRB5_BADMSGTYPE; - goto out; + ret = KRB5_BADMSGTYPE; + goto out; } if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0) - nid = NID_X9_62_prime256v1; + nid = NID_X9_62_prime256v1; else { - ret = KRB5_BADMSGTYPE; - goto out; - } + ret = KRB5_BADMSGTYPE; + goto out; + } /* XXX verify group is ok */ @@ -230,20 +410,21 @@ get_ecdh_param(krb5_context context, p = dh_key_info->subjectPublicKey.data; len = dh_key_info->subjectPublicKey.length / 8; if (o2i_ECPublicKey(&public, &p, len) == NULL) { - ret = KRB5_BADMSGTYPE; - krb5_set_error_message(context, ret, - "PKINIT failed to decode ECDH key"); - goto out; + ret = KRB5_BADMSGTYPE; + krb5_set_error_message(context, ret, + "PKINIT failed to decode ECDH key"); + goto out; } *out = public; public = NULL; out: if (public) - EC_KEY_free(public); + EC_KEY_free(public); free_ECParameters(&ecp); return ret; } +#endif #endif /* HAVE_HCRYPTO_W_OPENSSL */ krb5_error_code @@ -253,7 +434,11 @@ _kdc_get_ecdh_param(krb5_context context, void **out) { #ifdef HAVE_HCRYPTO_W_OPENSSL - return get_ecdh_param(context, config, dh_key_info, (EC_KEY **)out); +#ifdef HAVE_OPENSSL_30 + return get_ecdh_param_ossl30(context, config, dh_key_info, (EVP_PKEY **)out); +#else + return get_ecdh_param_ossl11(context, config, dh_key_info, (EC_KEY **)out); +#endif #else return ENOTSUP; #endif /* HAVE_HCRYPTO_W_OPENSSL */ @@ -265,13 +450,51 @@ _kdc_get_ecdh_param(krb5_context context, */ #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 static krb5_error_code -serialize_ecdh_key(krb5_context context, - EC_KEY *key, - unsigned char **out, - size_t *out_len) +serialize_ecdh_key_ossl30(krb5_context context, + EVP_PKEY *key, + unsigned char **out, + size_t *out_len) +{ + unsigned char *p; + int len; + + *out = NULL; + *out_len = 0; + + len = i2d_PublicKey(key, NULL); + if (len <= 0) { + krb5_set_error_message(context, EOVERFLOW, + "PKINIT failed to encode ECDH key"); + return EOVERFLOW; + } + + *out = malloc(len); + if (*out == NULL) + return krb5_enomem(context); + + p = *out; + len = i2d_PublicKey(key, &p); + if (len <= 0) { + free(*out); + *out = NULL; + krb5_set_error_message(context, EINVAL /* XXX Better error please */, + "PKINIT failed to encode ECDH key"); + return EINVAL; + } + + *out_len = len * 8; + return 0; +} +#else + +static krb5_error_code +serialize_ecdh_key_ossl11(krb5_context context, + EC_KEY *key, + unsigned char **out, + size_t *out_len) { - krb5_error_code ret = 0; unsigned char *p; int len; @@ -279,8 +502,11 @@ serialize_ecdh_key(krb5_context context, *out_len = 0; len = i2o_ECPublicKey(key, NULL); - if (len <= 0) + if (len <= 0) { + krb5_set_error_message(context, EOVERFLOW, + "PKINIT failed to encode ECDH key"); return EOVERFLOW; + } *out = malloc(len); if (*out == NULL) @@ -291,16 +517,16 @@ serialize_ecdh_key(krb5_context context, if (len <= 0) { free(*out); *out = NULL; - ret = EINVAL; /* XXX Better error please */ - krb5_set_error_message(context, ret, + krb5_set_error_message(context, EINVAL /* XXX Better error please */, "PKINIT failed to encode ECDH key"); - return ret; + return EINVAL; } *out_len = len * 8; - return ret; + return 0; } #endif +#endif krb5_error_code _kdc_serialize_ecdh_key(krb5_context context, @@ -309,7 +535,11 @@ _kdc_serialize_ecdh_key(krb5_context context, size_t *out_len) { #ifdef HAVE_HCRYPTO_W_OPENSSL - return serialize_ecdh_key(context, key, out, out_len); +#ifdef HAVE_OPENSSL_30 + return serialize_ecdh_key_ossl30(context, key, out, out_len); +#else + return serialize_ecdh_key_ossl11(context, key, out, out_len); +#endif #else return ENOTSUP; #endif diff --git a/third_party/heimdal/kdc/simple_csr_authorizer.c b/third_party/heimdal/kdc/simple_csr_authorizer.c deleted file mode 100644 index b46a8931ad3..00000000000 --- a/third_party/heimdal/kdc/simple_csr_authorizer.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2019 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * This plugin authorizes requested certificate SANs and EKUs by checking for - * existence of files of the form: - * - * - * /<path>/<princ>/<ext>-<value> - * - * where <path> is the value of: - * - * [kdc] simple_csr_authorizer_directory = PATH - * - * <princ> is a requesting client principal name with all characters other than - * alphanumeric, '-', '_', and non-leading '.' URL-encoded. - * - * <ext> is one of: - * - * - pkinit (SAN) - * - xmpp (SAN) - * - email (SAN) - * - ms-upn (SAN) - * - dnsname (SAN) - * - eku (EKU OID) - * - * and <value> is a display form of the SAN or EKU OID, with SANs URL-encoded - * just like principal names (see above). - * - * OIDs are of the form "1.2.3.4.5". - * - * Only digitalSignature and nonRepudiation key usage values are permitted. - */ -#define _GNU_SOURCE 1 - -#include <sys/types.h> -#include <sys/stat.h> -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include <roken.h> -#include <krb5.h> -#include <hx509.h> -#include <kdc.h> -#include <common_plugin.h> -#include <csr_authorizer_plugin.h> - -/* - * string_encode_sz() and string_encode() encode a string to be safe for use as - * a file name. They function very much like URL encoders, but '~' also gets - * encoded, and '@', '-', '_', and non-leading '.' do not. - * - * A corresponding decoder is not needed. - */ -static size_t -string_encode_sz(const char *in) -{ - size_t sz = strlen(in); - int first = 1; - - while (*in) { - char c = *(in++); - - switch (c) { - case '@': - case '-': - case '_': - break; - case '.': - if (first) - sz += 2; - break; - default: - if (!isalnum(c)) - sz += 2; - } - first = 0; - } - return sz; -} - -static char * -string_encode(const char *in) -{ - size_t len = strlen(in); - size_t sz = string_encode_sz(in); - size_t i, k; - char *s; - int first = 1; - - if ((s = malloc(sz + 1)) == NULL) - return NULL; - s[sz] = '\0'; - - for (i = k = 0; i < len; i++, first = 0) { - unsigned char c = ((const unsigned char *)in)[i]; - - switch (c) { - case '@': - case '-': - case '_': - s[k++] = c; - break; - case '.': - if (first) { - s[k++] = '%'; - s[k++] = "0123456789abcdef"[(c&0xff)>>4]; - s[k++] = "0123456789abcdef"[(c&0x0f)]; - } else { - s[k++] = c; - } - break; - default: - if (isalnum(c)) { - s[k++] = c; - } else { - s[k++] = '%'; - s[k++] = "0123456789abcdef"[(c&0xff)>>4]; - s[k++] = "0123456789abcdef"[(c&0x0f)]; - } - } - } - return s; -} - -static void -frees(char **s) -{ - free(*s); - *s = NULL; -} - -static KRB5_LIB_CALL krb5_error_code -authorize(void *ctx, - krb5_context context, - const char *app, - hx509_request csr, - krb5_const_principal client, - krb5_boolean *result) -{ - krb5_error_code ret; - hx509_context hx509ctx = NULL; - KeyUsage ku; - const char *d; - size_t i; - char *princ = NULL; - char *s = NULL; - - if ((d = krb5_config_get_string(context, NULL, app ? app : "kdc", - "simple_csr_authorizer_directory", - NULL)) == NULL) - return KRB5_PLUGIN_NO_HANDLE; - - if ((ret = hx509_context_init(&hx509ctx))) - return ret; - - if ((ret = krb5_unparse_name(context, client, &princ))) - goto out; - - s = string_encode(princ); - free(princ); - princ = NULL; - if (s == NULL) - goto enomem; - - princ = s; - s = NULL; - - for (i = 0; ret == 0; i++) { - hx509_san_type san_type; - struct stat st; - const char *prefix; - char *san; - char *p; - - ret = hx509_request_get_san(csr, i, &san_type, &s); - if (ret) - break; - switch (san_type) { - case HX509_SAN_TYPE_EMAIL: - prefix = "email"; - break; - case HX509_SAN_TYPE_DNSNAME: - prefix = "dnsname"; - break; - case HX509_SAN_TYPE_XMPP: - prefix = "xmpp"; - break; - case HX509_SAN_TYPE_PKINIT: - prefix = "pkinit"; - break; - case HX509_SAN_TYPE_MS_UPN: - prefix = "ms-upn"; - break; - default: - ret = ENOTSUP; - break; - } - if (ret) - break; - - if ((san = string_encode(s)) == NULL || - asprintf(&p, "%s/%s/%s-%s", d, princ, prefix, san) == -1 || - p == NULL) { - free(san); - goto enomem; - } - ret = stat(p, &st) == -1 ? errno : 0; - free(san); - free(p); - frees(&s); - if (ret) - goto skip; - ret = hx509_request_authorize_san(csr, i); - } - frees(&s); - if (ret == HX509_NO_ITEM) - ret = 0; - if (ret) - goto out; - - for (i = 0; ret == 0; i++) { - struct stat st; - char *p; - - ret = hx509_request_get_eku(csr, i, &s); - if (ret) - break; - if (asprintf(&p, "%s/%s/eku-%s", d, princ, s) == -1 || p == NULL) - goto enomem; - ret = stat(p, &st) == -1 ? errno : 0; - free(p); - frees(&s); - if (ret) - goto skip; - ret = hx509_request_authorize_eku(csr, i); - } - if (ret == HX509_NO_ITEM) - ret = 0; - if (ret) - goto out; - - ku = int2KeyUsage(0); - ku.digitalSignature = 1; - ku.nonRepudiation = 1; - hx509_request_authorize_ku(csr, ku); - - *result = TRUE; - ret = 0; - goto out; - -skip: - /* Allow another plugin to get a crack at this */ - ret = KRB5_PLUGIN_NO_HANDLE; - goto out; - -enomem: - ret = krb5_enomem(context); - goto out; - -out: - hx509_context_free(&hx509ctx); - free(princ); - free(s); - return ret; -} - -static KRB5_LIB_CALL krb5_error_code -simple_csr_authorizer_init(krb5_context context, void **c) -{ - *c = NULL; - return 0; -} - -static KRB5_LIB_CALL void -simple_csr_authorizer_fini(void *c) -{ -} - -static krb5plugin_csr_authorizer_ftable plug_desc = - { 1, simple_csr_authorizer_init, simple_csr_authorizer_fini, authorize }; - -static krb5plugin_csr_authorizer_ftable *plugs[] = { &plug_desc }; - -static uintptr_t -simple_csr_authorizer_get_instance(const char *libname) -{ - if (strcmp(libname, "krb5") == 0) - return krb5_get_instance(libname); - if (strcmp(libname, "kdc") == 0) - return kdc_get_instance(libname); - if (strcmp(libname, "hx509") == 0) - return hx509_get_instance(libname); - return 0; -} - -krb5_plugin_load_ft kdc_csr_authorizer_plugin_load; - -krb5_error_code KRB5_CALLCONV -kdc_csr_authorizer_plugin_load(heim_pcontext context, - krb5_get_instance_func_t *get_instance, - size_t *num_plugins, - krb5_plugin_common_ftable_cp **plugins) -{ - *get_instance = simple_csr_authorizer_get_instance; - *num_plugins = sizeof(plugs) / sizeof(plugs[0]); - *plugins = (krb5_plugin_common_ftable_cp *)plugs; - return 0; -} diff --git a/third_party/heimdal/kdc/test_csr_authorizer.c b/third_party/heimdal/kdc/test_csr_authorizer.c index 1d526f77bb6..dbf4c421237 100644 --- a/third_party/heimdal/kdc/test_csr_authorizer.c +++ b/third_party/heimdal/kdc/test_csr_authorizer.c @@ -1,8 +1,84 @@ +/* + * Copyright (c) 2022 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ #include "kdc_locl.h" +#include <heim-ipc.h> + +/* + * This program implements two things: + * + * - a utility for testing the `kdc_authorize_csr()' function and the plugins + * that uses, + * + * and + * + * - a server for the IPC authorizer. + * + * For the latter, requested certificate SANs and EKUs are authorized by + * checking for existence of files of the form: + * + * /<path>/<princ>/<ext>-<value> + * + * where <path> is given as an option. + * + * <princ> is a requesting client principal name with all characters other than + * alphanumeric, '-', '_', and non-leading '.' URL-encoded. + * + * <ext> is one of: + * + * - pkinit (SAN) + * - xmpp (SAN) + * - email (SAN) + * - ms-upn (SAN) + * - dnsname (SAN) + * - eku (EKU OID) + * + * and <value> is a display form of the SAN or EKU OID, with SANs URL-encoded + * just like principal names (see above). + * + * OIDs are of the form "1.2.3.4.5". + * + * Only digitalSignature and nonRepudiation key usage values are permitted. + */ static int help_flag; static int version_flag; +static int daemon_flag; +static int daemon_child_flag = -1; +static int ignore_flag = 0; +static int server_flag = 0; static const char *app_string = "kdc"; +static const char *socket_dir; +static const char *authz_dir; struct getargs args[] = { { "help", 'h', arg_flag, &help_flag, @@ -11,6 +87,18 @@ struct getargs args[] = { "Print version", NULL }, { "app", 'a', arg_string, &app_string, "App to test (kdc or bx509); default: kdc", "APPNAME" }, + { "socket-dir", 'S', arg_string, &socket_dir, + "IPC socket directory", "DIR" }, + { "authorization-dir", 'A', arg_string, &authz_dir, + "authorization directory", "DIR" }, + { "server", '\0', arg_flag, &server_flag, + "Server mode", NULL }, + { "ignore", 'I', arg_flag, &ignore_flag, + "ignore requests", NULL }, + { "daemon", 'd', arg_flag, &daemon_flag, + "daemonize", NULL }, + { "daemon-child", '\0', arg_flag, &daemon_child_flag, + "internal-use-only option", NULL }, }; size_t num_args = sizeof(args) / sizeof(args[0]); @@ -19,9 +107,23 @@ usage(int e) { arg_printusage(args, num_args, NULL, "PATH-TO-DER-CSR PRINCIPAL"); fprintf(stderr, - "\n\tExercise CSR authorization plugins for a given CSR for a\n" - "\tgiven principal.\n" - "\n\tExample: %s PKCS10:/tmp/csr.der foo@TEST.H5L.SE\n", + "\tExercise CSR authorization plugins for a given CSR for a\n" + "\tgiven principal.\n\n" + "\tServer-mode (--server) looks for files in the \n" + "\t--authorization-dir DIR directory named:\n" + "\n" + "\t\teku=OID\n" + "\t\tsan_pkinit=PRINCIPAL\n" + "\t\tsan_ms_upn=PRINCIPAL\n" + "\t\tsan_dnsname=DOMAINNAME\n" + "\t\tsan_xmpp=JABBER-ID\n" + "\t\tsan_email=EMAIL\n" + "\n" + "\tClient-mode positional arguments are:\n\n" + "\t\tPATH-TO-DER-CSR PRETEND-CLIENT-PRINCIPAL [...]\n\n" + "\twhere {...} are requested features that must be granted\n" + "\tif the request is only partially authorized.\n\n" + "\tClient example:\n\t\t%s PKCS10:/tmp/csr.der foo@TEST.H5L.SE\n", getprogname()); exit(e); return e; @@ -58,6 +160,177 @@ load_plugins(krb5_context context) #endif } +static char *string_encode(const char *); +static int stat_authz(const char *, const char *); + +static krb5_error_code +authorize(const char *subject, const char *thing) +{ + krb5_error_code ret; + char *s = NULL; + + s = string_encode(subject); + if (s == NULL) + return ENOMEM; + + ret = stat_authz(s, thing); + if (ret == ENOENT) + ret = stat_authz(s, "all"); + if (ret == ENOENT) + ret = EACCES; + free(s); + return ret; +} + +static void +service(void *ctx, + const heim_octet_string *req, + const heim_icred cred, + heim_ipc_complete complete_cb, + heim_sipc_call complete_cb_data) +{ + krb5_error_code ret = 0; + struct rk_strpool *result = NULL; + krb5_data rep; + const char *subject; + char *cmd; + char *next = NULL; + char *res = NULL; + char *tok; + char *s; + int none_granted = 1; + int all_granted = 1; + int first = 1; + + /* + * A krb5_context and log facility for logging would be nice, but this is + * all just for testing. + */ + + (void)ctx; + + cmd = strndup(req->data, req->length); + if (cmd == NULL) + errx(1, "Out of memory"); + + if (strncmp(cmd, "check ", sizeof("check ") - 1) != 0) { + rep.data = "Invalid request command (must be \"check ...\")"; + rep.length = sizeof("Invalid request command (must be \"check ...\")") - 1; + (*complete_cb)(complete_cb_data, EINVAL, &rep); + free(cmd); + return; + } + + s = cmd + sizeof("check ") - 1; + subject = strtok_r(s, " ", &next); + s = NULL; + + while ((tok = strtok_r(s, " ", &next))) { + int ret2; + + ret2 = authorize(subject, tok); + result = rk_strpoolprintf(result, "%s%s:%s", + first ? "" : ",", + tok, + ret2 == 0 ? "granted" : "denied"); + if (ret2 == 0) + none_granted = 0; + else + all_granted = 0; + + if (ret2 != 0 && ret == 0) + ret = ret2; + + first = 0; + } + free(cmd); + + if (ret == 0 && all_granted) { + rk_strpoolfree(result); + + rep.data = "granted"; + rep.length = sizeof("granted") - 1; + (*complete_cb)(complete_cb_data, 0, &rep); + return; + } + + if (none_granted && ignore_flag) { + rk_strpoolfree(result); + + rep.data = "ignore"; + rep.length = sizeof("ignore") - 1; + (*complete_cb)(complete_cb_data, KRB5_PLUGIN_NO_HANDLE, &rep); + return; + } + + s = rk_strpoolcollect(result); /* frees `result' */ + if (s == NULL) { + rep.data = "denied out-of-memory"; + rep.length = sizeof("denied out-of-memory") - 1; + (*complete_cb)(complete_cb_data, KRB5_PLUGIN_NO_HANDLE, &rep); + return; + } + + if (asprintf(&res, "denied %s", s) == -1) + errx(1, "Out of memory"); + if (res == NULL) + errx(1, "Out of memory"); + + rep.data = res; + rep.length = strlen(res); + + (*complete_cb)(complete_cb_data, ret, &rep); + free(res); + free(s); +} + +static char * +make_feature_argument(const char *kind, + hx509_san_type san_type, + const char *value) +{ + const char *san_type_str = NULL; + char *s = NULL; + + if (strcmp(kind, "san") != 0) { + if (asprintf(&s, "%s=%s", kind, value) == -1 || s == NULL) + errx(1, "Out of memory"); + return s; + } + + switch (san_type) { + case HX509_SAN_TYPE_EMAIL: + san_type_str = "email"; + break; + case HX509_SAN_TYPE_DNSNAME: + san_type_str = "dnsname"; + break; + case HX509_SAN_TYPE_DN: + san_type_str = "dn"; + break; + case HX509_SAN_TYPE_REGISTERED_ID: + san_type_str = "registered_id"; + break; + case HX509_SAN_TYPE_XMPP: + san_type_str = "xmpp"; + break; + case HX509_SAN_TYPE_PKINIT: + case HX509_SAN_TYPE_MS_UPN: + san_type_str = "pkinit"; + break; + case HX509_SAN_TYPE_DNSSRV: + san_type_str = "dnssrv"; + break; + default: + warnx("SAN type not supported"); + return ""; + } + + if (asprintf(&s, "san_%s=%s", san_type_str, value) == -1 || s == NULL) + errx(1, "Out of memory"); + return s; +} + int main(int argc, char **argv) { @@ -79,24 +352,143 @@ main(int argc, char **argv) return 0; } - argc -= optidx; - argv += optidx; - - if (argc != 2) - usage(1); - if ((errno = krb5_init_context(&context))) err(1, "Could not initialize krb5_context"); if ((ret = krb5_initlog(context, argv0, &logf)) || (ret = krb5_addlog_dest(context, logf, "0-5/STDERR"))) krb5_err(context, 1, ret, "Could not set up logging to stderr"); load_plugins(context); + + if (server_flag && daemon_flag) + daemon_child_flag = roken_detach_prep(argc, argv, "--daemon-child"); + + argc -= optidx; + argv += optidx; + + if (socket_dir) + setenv("HEIM_IPC_DIR", socket_dir, 1); + + if (server_flag) { + const char *svc; + heim_sipc un; + + rk_pidfile(NULL); + + svc = krb5_config_get_string(context, NULL, + app_string ? app_string : "kdc", + "ipc_csr_authorizer", "service", NULL); + if (svc == NULL) + svc = "org.h5l.csr_authorizer"; + + /* `service' is our request handler; `argv' is its callback data */ + ret = heim_sipc_service_unix(svc, service, NULL, &un); + if (ret) + krb5_err(context, 1, ret, + "Could not setup service on Unix domain socket " + "%s/.heim_%s-socket", socket_dir, svc); + + roken_detach_finish(NULL, daemon_child_flag); + + /* Enter the IPC event loop */ + heim_ipc_main(); + return 0; + } + + /* Client mode */ + if (argc < 2) + usage(1); + + /* Parse the given CSR */ if ((ret = hx509_request_parse(context->hx509ctx, argv[0], &csr))) krb5_err(context, 1, ret, "Could not parse PKCS#10 CSR from %s", argv[0]); + + /* + * Parse the client principal that we'll pretend is an authenticated client + * principal. + */ if ((ret = krb5_parse_name(context, argv[1], &princ))) krb5_err(context, 1, ret, "Could not parse principal %s", argv[1]); - if ((ret = kdc_authorize_csr(context, app_string, csr, princ))) - krb5_err(context, 1, ret, "Authorization failed"); + + /* Call the authorizer */ + ret = kdc_authorize_csr(context, app_string, csr, princ); + + if (ret) { + unsigned n = hx509_request_count_unauthorized(csr); + size_t i, k; + int ret2 = 0; + int good = -1; + + /* + * Check partial approval of SANs. + * + * Iterate over the SANs in the request, and for each check if a) it + * was granted, b) it's on the remainder of our argv[]. + */ + for (i = 0; ret2 == 0; i++) { + hx509_san_type san_type; + char *feature = NULL; + char *san = NULL; + int granted; + + ret2 = hx509_request_get_san(csr, i, &san_type, &san); + if (ret2) + break; + + feature = make_feature_argument("san", san_type, san); + + granted = hx509_request_san_authorized_p(csr, i); + for (k = 2; k < argc; k++) { + if (strcmp(feature, argv[k]) != 0) + continue; + + /* The SAN is on our command line */ + if (granted && good == -1) + good = 1; + else if (!granted) + good = 0; + break; + } + + hx509_xfree(san); + } + + /* Check partial approval of EKUs */ + for (i = 0; ret2 == 0; i++) { + char *feature = NULL; + char *eku = NULL; + int granted; + + ret2 = hx509_request_get_eku(csr, i, &eku); + if (ret2) + break; + + feature = make_feature_argument("eku", 0, eku); + + granted = hx509_request_eku_authorized_p(csr, i); + for (k = 2; k < argc; k++) { + if (strcmp(feature, argv[k]) != 0) + continue; + + /* The SAN is on our command line */ + if (granted && good == -1) + good = 1; + else if (!granted) + good = 0; + break; + } + + hx509_xfree(eku); + } + + if (good != 1) { + krb5_free_principal(context, princ); + _krb5_unload_plugins(context, "kdc"); + hx509_request_free(&csr); + krb5_err(context, 1, ret, + "Authorization failed with %u rejected features", n); + } + } + printf("Authorized!\n"); krb5_free_principal(context, princ); _krb5_unload_plugins(context, "kdc"); @@ -104,3 +496,102 @@ main(int argc, char **argv) hx509_request_free(&csr); return 0; } + +/* + * string_encode_sz() and string_encode() encode a string to be safe for use as + * a file name. They function very much like URL encoders, but '~' also gets + * encoded, and '@', '-', '_', and non-leading '.' do not. + * + * A corresponding decoder is not needed. + */ +static size_t +string_encode_sz(const char *in) +{ + size_t sz = strlen(in); + int first = 1; + + while (*in) { + char c = *(in++); + + switch (c) { + case '@': + case '-': + case '_': + break; + case '.': + if (first) + sz += 2; + break; + default: + if (!isalnum(c)) + sz += 2; + } + first = 0; + } + return sz; +} + +static char * +string_encode(const char *in) +{ + size_t len = strlen(in); + size_t sz = string_encode_sz(in); + size_t i, k; + char *s; + int first = 1; + + if ((s = malloc(sz + 1)) == NULL) + return NULL; + s[sz] = '\0'; + + for (i = k = 0; i < len; i++, first = 0) { + unsigned char c = ((const unsigned char *)in)[i]; + + switch (c) { + case '@': + case '-': + case '_': + s[k++] = c; + break; + case '.': + if (first) { + s[k++] = '%'; + s[k++] = "0123456789abcdef"[(c&0xff)>>4]; + s[k++] = "0123456789abcdef"[(c&0x0f)]; + } else { + s[k++] = c; + } + break; + default: + if (isalnum(c)) { + s[k++] = c; + } else { + s[k++] = '%'; + s[k++] = "0123456789abcdef"[(c&0xff)>>4]; + s[k++] = "0123456789abcdef"[(c&0x0f)]; + } + } + } + return s; +} + +static int +stat_authz(const char *subject, + const char *thing) +{ + struct stat st; + char *p = NULL; + int ret; + + if (authz_dir == NULL) + return KRB5_PLUGIN_NO_HANDLE; + if (thing) + ret = asprintf(&p, "%s/%s/%s", authz_dir, subject, thing); + else + ret = asprintf(&p, "%s/%s", authz_dir, subject); + if (ret == -1 || p == NULL) + return ENOMEM; + ret = stat(p, &st); + free(p); + return ret == 0 ? 0 : errno; +} diff --git a/third_party/heimdal/kdc/test_token_validator.c b/third_party/heimdal/kdc/test_token_validator.c index 10ea35aa242..2e4e9dca3dd 100644 --- a/third_party/heimdal/kdc/test_token_validator.c +++ b/third_party/heimdal/kdc/test_token_validator.c @@ -88,7 +88,7 @@ main(int argc, char **argv) if (argc != 2) usage(1); - if ((ret = krb5_init_context(&context))) + if (krb5_init_context(&context)) err(1, "Could not initialize krb5_context"); load_plugins(context); diff --git a/third_party/heimdal/kpasswd/kpasswdd.c b/third_party/heimdal/kpasswd/kpasswdd.c index e04eebe46e1..43a7335898a 100644 --- a/third_party/heimdal/kpasswd/kpasswdd.c +++ b/third_party/heimdal/kpasswd/kpasswdd.c @@ -463,18 +463,27 @@ verify (krb5_auth_context *auth_context, * either an invalid request or an error packet. An error packet may be * the result of a ping-pong attacker pointing us at another kpasswdd. */ + if (len < 6) { + krb5_warnx(context, "Message too short: %llu", + (unsigned long long)len); + return 1; + } pkt_len = (msg[0] << 8) | (msg[1]); pkt_ver = (msg[2] << 8) | (msg[3]); ap_req_len = (msg[4] << 8) | (msg[5]); if (pkt_len != len) { - krb5_warnx (context, "Strange len: %ld != %ld", - (long)pkt_len, (long)len); + krb5_warnx(context, "Bad packet length: %u != %llu", pkt_len, + (unsigned long long)len); return 1; } if (ap_req_len == 0) { krb5_warnx (context, "Request is error packet (ap_req_len == 0)"); return 1; } + if (ap_req_len + 6 > len) { + krb5_warnx(context, "Bad AP-REQ length: %u", ap_req_len); + return 1; + } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW && pkt_ver != KRB5_KPASSWD_VERS_SETPW) { krb5_warnx (context, "Bad version (%d)", pkt_ver); diff --git a/third_party/heimdal/krb5.conf b/third_party/heimdal/krb5.conf index 103ea8c22d8..7d1a5472f16 100644 --- a/third_party/heimdal/krb5.conf +++ b/third_party/heimdal/krb5.conf @@ -1,3 +1,8 @@ +# The krb5.conf file can include other files using either of the following directives at the beginning of the line +# see also krb5.conf(5) +# include /etc/krb5-sssd.conf +# includedir /etc/krb5.conf.d + [libdefaults] default_realm = MY.REALM clockskew = 300 diff --git a/third_party/heimdal/kuser/NTMakefile b/third_party/heimdal/kuser/NTMakefile index 2538744dbc5..4b97b7084e7 100644 --- a/third_party/heimdal/kuser/NTMakefile +++ b/third_party/heimdal/kuser/NTMakefile @@ -55,6 +55,7 @@ NOINSTPROGRAMS=\ BINLIBS=\ + $(LIBHEIMBASE) \ $(LIBGSS_PREAUTH) \ $(LIBGSSAPI) \ $(LIBHEIMDAL) \ diff --git a/third_party/heimdal/lib/asn1/asn1parse.y b/third_party/heimdal/lib/asn1/asn1parse.y index f6f6ec0e5c4..d9e3fba2d37 100644 --- a/third_party/heimdal/lib/asn1/asn1parse.y +++ b/third_party/heimdal/lib/asn1/asn1parse.y @@ -1432,8 +1432,10 @@ TaggedType : Tag tagenv Type * IMPLICIT tags of CHOICE types are EXPLICIT * instead. */ - if (t->type == TChoice) + if (t->type == TChoice) { + $$->implicit_choice = 1; $$->tag.tagenv = TE_EXPLICIT; + } if($3->type == TTag && $2 == TE_IMPLICIT) { $$->subtype = $3->subtype; free($3); diff --git a/third_party/heimdal/lib/asn1/check-der.c b/third_party/heimdal/lib/asn1/check-der.c index a8956a74bd2..58c1446fd3a 100644 --- a/third_party/heimdal/lib/asn1/check-der.c +++ b/third_party/heimdal/lib/asn1/check-der.c @@ -67,11 +67,12 @@ test_integer (void) {NULL, 1, "\xff", NULL }, {NULL, 2, "\xff\x01", NULL }, {NULL, 2, "\x00\xff", NULL }, + {NULL, 2, "\xfe\x01", NULL }, {NULL, 4, "\x7f\xff\xff\xff", NULL } }; int values[] = {0, 127, 128, 256, -128, -129, -1, -255, 255, - 0x7fffffff}; + -511, 0x7fffffff}; int i, ret; int ntests = sizeof(tests) / sizeof(*tests); @@ -153,7 +154,7 @@ test_one_int(int val) static int test_integer_more (void) { - int i, n1, n2, n3, n4, n5, n6; + int64_t i, n1, n2, n3, n4, n5, n6; n2 = 0; for (i = 0; i < (sizeof(int) * 8); i++) { @@ -522,23 +523,27 @@ static int test_heim_integer (void) { struct test_case tests[] = { + {NULL, 1, "\xff", NULL }, + {NULL, 2, "\xff\x01", NULL }, {NULL, 2, "\xfe\x01", NULL }, {NULL, 2, "\xef\x01", NULL }, {NULL, 3, "\xff\x00\xff", NULL }, {NULL, 3, "\xff\x01\x00", NULL }, {NULL, 1, "\x00", NULL }, {NULL, 1, "\x01", NULL }, - {NULL, 2, "\x00\x80", NULL } + {NULL, 2, "\x00\x80", NULL }, }; heim_integer values[] = { + { 1, "\x01", 1 }, + { 1, "\xff", 1 }, { 2, "\x01\xff", 1 }, { 2, "\x10\xff", 1 }, { 2, "\xff\x01", 1 }, { 2, "\xff\x00", 1 }, { 0, "", 0 }, { 1, "\x01", 0 }, - { 1, "\x80", 0 } + { 1, "\x80", 0 }, }; int i, ret; int ntests = sizeof(tests) / sizeof(tests[0]); diff --git a/third_party/heimdal/lib/asn1/check-gen.c b/third_party/heimdal/lib/asn1/check-gen.c index 6b5c71c39f5..bba5b9db255 100644 --- a/third_party/heimdal/lib/asn1/check-gen.c +++ b/third_party/heimdal/lib/asn1/check-gen.c @@ -655,6 +655,12 @@ test_cert(void) size_t i; int ret; + memset(&c, 0, sizeof(c)); + ret = copy_Certificate(&c, &c2); + if (ret) + return ret; + free_Certificate(&c2); + for (i = 0; i < sizeof(certs)/sizeof(certs[0]); i++) { ret = decode_Certificate((unsigned char *)certs[i].cert, @@ -1115,6 +1121,57 @@ test_decorated(void) } static int +test_extensible_choice(void) +{ + PA_FX_FAST_REQUEST r, r2; + size_t len, size; + void *ptr; + int ret; + + memset(&r, 0, sizeof(r)); + + ret = copy_PA_FX_FAST_REQUEST(&r, &r2); + if (ret) + return ret; + free_PA_FX_FAST_REQUEST(&r2); + + r.element = 0; + r.u.asn1_ellipsis.data = "hello"; + r.u.asn1_ellipsis.length = sizeof("hello") - 1; + ret = copy_PA_FX_FAST_REQUEST(&r, &r2); + if (ret) + errx(1, "Out of memory"); + if (r2.element != 0) + errx(1, "Extensible CHOICE copy failure to set discriminant to 0"); + if (r2.u.asn1_ellipsis.length != r.u.asn1_ellipsis.length) + errx(1, "Extensible CHOICE copy failure to copy extension"); + if (memcmp(r.u.asn1_ellipsis.data, r2.u.asn1_ellipsis.data, + r.u.asn1_ellipsis.length) != 0) + errx(1, "Extensible CHOICE copy failure to copy extension (2)"); + free_PA_FX_FAST_REQUEST(&r2); + + ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, ptr, len, &r, &size, ret); + if (ret || len != size) + errx(1, "Extensible CHOICE encoding failure"); + + ret = decode_PA_FX_FAST_REQUEST(ptr, len, &r2, &size); + if (ret || len != size) + errx(1, "Extensible CHOICE decoding failure"); + + if (r2.element != 0) + errx(1, "Extensible CHOICE decode failure to set discriminant to 0"); + if (r2.u.asn1_ellipsis.length != r.u.asn1_ellipsis.length) + errx(1, "Extensible CHOICE decode failure to copy extension"); + if (memcmp(r.u.asn1_ellipsis.data, r2.u.asn1_ellipsis.data, + r.u.asn1_ellipsis.length) != 0) + errx(1, "Extensible CHOICE decode failure to copy extension (2)"); + + free_PA_FX_FAST_REQUEST(&r2); + free(ptr); + return 0; +} + +static int test_decorated_choice(void) { TESTNotDecoratedChoice tndc; @@ -2674,6 +2731,8 @@ main(int argc, char **argv) DO_ONE(test_default); + DO_ONE(test_extensible_choice); + DO_ONE(test_decorated_choice); DO_ONE(test_decorated); diff --git a/third_party/heimdal/lib/asn1/der.h b/third_party/heimdal/lib/asn1/der.h index f7323486454..ec5603ef6c1 100644 --- a/third_party/heimdal/lib/asn1/der.h +++ b/third_party/heimdal/lib/asn1/der.h @@ -67,9 +67,9 @@ enum { UT_IA5String = 22, UT_UTCTime = 23, UT_GeneralizedTime = 24, - UT_UniversalString = 25, UT_VisibleString = 26, UT_GeneralString = 27, + UT_UniversalString = 28, UT_BMPString = 30, /* unsupported types */ UT_ObjectDescriptor = 7, diff --git a/third_party/heimdal/lib/asn1/der_cmp.c b/third_party/heimdal/lib/asn1/der_cmp.c index ccaf6fd0f58..f745270f3cd 100644 --- a/third_party/heimdal/lib/asn1/der_cmp.c +++ b/third_party/heimdal/lib/asn1/der_cmp.c @@ -38,22 +38,25 @@ der_heim_oid_cmp(const heim_oid *p, const heim_oid *q) { int c; - if (p->length == q->length) + if (p->length == q->length) { + if (p->length == 0) + return 0; return memcmp(p->components, q->components, p->length * sizeof(*p->components)); + } if (p->length < q->length) { - c = memcmp(p->components, - q->components, - p->length * sizeof(*p->components)); - if (c == 0) + if (p->length == 0 || + (c = memcmp(p->components, + q->components, + p->length * sizeof(*p->components))) == 0) return -1; return c; } - c = memcmp(p->components, - q->components, - q->length * sizeof(*p->components)); - if (c == 0) + if (q->length == 0 || + (c = memcmp(p->components, + q->components, + q->length * sizeof(*p->components))) == 0) return 1; return c; } @@ -64,14 +67,19 @@ der_heim_octet_string_cmp(const heim_octet_string *p, { int c; - if (p->length == q->length) + if (p->length == q->length) { + if (p->length == 0) + return 0; return memcmp(p->data, q->data, p->length); + } if (p->length < q->length) { - if ((c = memcmp(p->data, q->data, p->length)) == 0) + if (p->length == 0 || + (c = memcmp(p->data, q->data, p->length)) == 0) return -1; return c; } - if ((c = memcmp(p->data, q->data, q->length)) == 0) + if (q->length == 0 || + (c = memcmp(p->data, q->data, q->length)) == 0) return 1; return c; } @@ -94,22 +102,92 @@ int ASN1CALL der_heim_bit_string_cmp(const heim_bit_string *p, const heim_bit_string *q) { - int r1, r2; - size_t i; - if (p->length != q->length) - return (int)(p->length - q->length); - i = memcmp(p->data, q->data, p->length / 8); - if (i) - return (int)i; - if ((p->length % 8) == 0) - return 0; - i = (p->length / 8); - r1 = ((unsigned char *)p->data)[i]; - r2 = ((unsigned char *)q->data)[i]; - i = 8 - (p->length % 8); - r1 = r1 >> i; - r2 = r2 >> i; - return r1 - r2; + unsigned char pc, qc; + size_t bits; + int c = 0; + + /* Compare prefix */ + if (p->length == 0 && q->length == 0) + return 0; + if (p->length > 7 && q->length > 7) { + if (p->length < q->length) + c = memcmp(p->data, q->data, p->length / 8); + else + c = memcmp(p->data, q->data, q->length / 8); + } + if (c) + return c; + + /* Prefixes are equal, c == 0 */ + + if (p->length == q->length && p->length % 8 == 0) + return 0; + if (p->length == 0 && q->length) + return -1; /* No trailing bits of p to compare to corresponding bits of q */ + if (q->length == 0 && p->length) + return 1; /* No trailing bits of q to compare to corresponding bits of p */ + + /* c == 0, lengths are not equal, both are at least 1 bit */ + pc = ((unsigned char *)p->data)[p->length / 8]; + qc = ((unsigned char *)q->data)[q->length / 8]; + if (p->length < q->length) + bits = p->length % 8; + else + bits = q->length % 8; + + if (bits > 0) { + if ((pc & 0x80) == 0 && (qc & 0x80) != 0) + return -1; + if ((pc & 0x80) != 0 && (qc & 0x80) == 0) + return 1; + } + if (bits > 1) { + if ((pc & 0x40) == 0 && (qc & 0x40) != 0) + return -1; + if ((pc & 0x40) != 0 && (qc & 0x40) == 0) + return 1; + } + if (bits > 2) { + if ((pc & 0x20) == 0 && (qc & 0x20) != 0) + return -1; + if ((pc & 0x20) != 0 && (qc & 0x20) == 0) + return 1; + } + if (bits > 3) { + if ((pc & 0x10) == 0 && (qc & 0x10) != 0) + return -1; + if ((pc & 0x10) != 0 && (qc & 0x10) == 0) + return 1; + } + if (bits > 4) { + if ((pc & 0x08) == 0 && (qc & 0x08) != 0) + return -1; + if ((pc & 0x08) != 0 && (qc & 0x08) == 0) + return 1; + } + if (bits > 5) { + if ((pc & 0x04) == 0 && (qc & 0x04) != 0) + return -1; + if ((pc & 0x04) != 0 && (qc & 0x04) == 0) + return 1; + } + if (bits > 6) { + if ((pc & 0x02) == 0 && (qc & 0x02) != 0) + return -1; + if ((pc & 0x02) != 0 && (qc & 0x02) == 0) + return 1; + } + /* + * `bits' can't be 8. + * + * All leading `bits' bits of the tail of the shorter of `p' or `q' are + * equal. + */ + if (p->length < q->length) + return -1; + if (q->length < p->length) + return 1; + return 0; } int ASN1CALL @@ -128,14 +206,19 @@ der_heim_bmp_string_cmp(const heim_bmp_string *p, const heim_bmp_string *q) { int c; - if (p->length == q->length) + if (p->length == q->length) { + if (p->length == 0) + return 0; return memcmp(p->data, q->data, p->length * sizeof(q->data[0])); + } if (p->length < q->length) { - if ((c = memcmp(p->data, q->data, p->length * sizeof(q->data[0]))) == 0) + if (p->length == 0 || + (c = memcmp(p->data, q->data, p->length * sizeof(q->data[0]))) == 0) return -1; return c; } - if ((c = memcmp(p->data, q->data, q->length * sizeof(q->data[0]))) == 0) + if (q->length == 0 || + (c = memcmp(p->data, q->data, q->length * sizeof(q->data[0]))) == 0) return 1; return c; } @@ -146,14 +229,19 @@ der_heim_universal_string_cmp(const heim_universal_string *p, { int c; - if (p->length == q->length) + if (p->length == q->length) { + if (p->length == 0) + return 0; return memcmp(p->data, q->data, p->length * sizeof(q->data[0])); + } if (p->length < q->length) { - if ((c = memcmp(p->data, q->data, p->length * sizeof(q->data[0]))) == 0) + if (p->length == 0 || + (c = memcmp(p->data, q->data, p->length * sizeof(q->data[0]))) == 0) return -1; return c; } - if ((c = memcmp(p->data, q->data, q->length * sizeof(q->data[0]))) == 0) + if (q->length == 0 || + (c = memcmp(p->data, q->data, q->length * sizeof(q->data[0]))) == 0) return 1; return c; } diff --git a/third_party/heimdal/lib/asn1/der_copy.c b/third_party/heimdal/lib/asn1/der_copy.c index 2084cef5f08..f67fff69d6b 100644 --- a/third_party/heimdal/lib/asn1/der_copy.c +++ b/third_party/heimdal/lib/asn1/der_copy.c @@ -167,9 +167,13 @@ int ASN1CALL der_copy_octet_string (const heim_octet_string *from, heim_octet_string *to) { assert(from->length == 0 || (from->length > 0 && from->data != NULL)); - if (from->length == 0) + if (from->length == 0) { + if (from->data == NULL) { + *to = *from; + return 0; + } to->data = calloc(1, 1); - else + } else to->data = malloc(from->length); if (to->data == NULL) { to->length = 0; diff --git a/third_party/heimdal/lib/asn1/der_get.c b/third_party/heimdal/lib/asn1/der_get.c index c12f8170025..06564b7cad2 100644 --- a/third_party/heimdal/lib/asn1/der_get.c +++ b/third_party/heimdal/lib/asn1/der_get.c @@ -86,9 +86,12 @@ der_get_integer (const unsigned char *p, size_t len, int val = 0; size_t oldlen = len; - if (len > sizeof(val)) + if (len == sizeof(val) + 1 && (p[0] == 0 || p[0] == 0xff)) + ; + else if (len > sizeof(val)) return ASN1_OVERRUN; + /* We assume we're on a twos-complement platform */ if (len > 0) { val = (signed char)*p++; while (--len) @@ -109,6 +112,7 @@ der_get_integer64 (const unsigned char *p, size_t len, if (len > sizeof(val)) return ASN1_OVERRUN; + /* We assume we're on a twos-complement platform */ if (len > 0) { val = (signed char)*p++; while (--len) @@ -456,13 +460,45 @@ der_get_heim_integer (const unsigned char *p, size_t len, if (p[0] & 0x80) { unsigned char *q; int carry = 1; - data->negative = 1; + + /* + * A negative number. It's going to be a twos complement byte array. + * We're going to leave the positive value in `data->data', but set the + * `data->negative' flag. That means we need to negate the + * twos-complement integer received. + */ + data->negative = 1; data->length = len; if (p[0] == 0xff) { + if (data->length == 1) { + /* One byte of all ones == -1 */ + q = data->data = malloc(1); + *q = 1; + data->length = 1; + if (size) + *size = 1; + return 0; + } + p++; data->length--; + + /* + * We could check if the next byte's high bit is set, which would + * be an error ("illegal padding" in OpenSSL). However, this would + * mean failing to accept certificates made by certain CAs that + * would read 8 bytes of RNG into a buffer, slap on length 8, then + * slap on the tag [UNIVERSAL INTEGER], and make that the + * serialNumber field's encoding, which then fails to parse in + * around 1 in 256 certificates. + * + * So let's not. + * + * if (p[0] & 0x80) + * return ASN1_PARSE_ERROR; // or a new error code + */ } data->data = malloc(data->length); if (data->data == NULL) { @@ -471,9 +507,17 @@ der_get_heim_integer (const unsigned char *p, size_t len, *size = 0; return ENOMEM; } + + /* + * Note that if `data->length' were zero, this would be UB because we + * underflow if data->length is zero even though we wouldn't actually + * dereference the byte before data->data. Thus we check above for + * that. + */ q = &((unsigned char*)data->data)[data->length - 1]; p += data->length - 1; while (q >= (unsigned char*)data->data) { + /* *p XOR 0xff -> ~*p; we're dealing with twos complement */ *q = *p ^ 0xff; if (carry) carry = !++*q; diff --git a/third_party/heimdal/lib/asn1/der_length.c b/third_party/heimdal/lib/asn1/der_length.c index 9a9913311d6..cd50df84622 100644 --- a/third_party/heimdal/lib/asn1/der_length.c +++ b/third_party/heimdal/lib/asn1/der_length.c @@ -256,7 +256,9 @@ der_length_heim_integer (const heim_integer *k) { if (k->length == 0) return 1; - if (k->negative) + if (k->negative && k->length == 1 && ((unsigned char *)k->data)[0] == 1) + return 1; + else if (k->negative) return k->length + (((~(((unsigned char *)k->data)[0])) & 0x80) ? 0 : 1); else return k->length + ((((unsigned char *)k->data)[0] & 0x80) ? 1 : 0); diff --git a/third_party/heimdal/lib/asn1/der_print.c b/third_party/heimdal/lib/asn1/der_print.c index 1d459856a61..dada747a9a7 100644 --- a/third_party/heimdal/lib/asn1/der_print.c +++ b/third_party/heimdal/lib/asn1/der_print.c @@ -223,7 +223,7 @@ der_print_bit_string(const heim_bit_string *k, int flags) (void) hex_encode(k->data, k->length / 8, &s); if (asprintf(&s2, "%llu:%s", (unsigned long long)k->length, s) == -1 || !s2) - return NULL; + s2 = NULL; free(s); return s2; } diff --git a/third_party/heimdal/lib/asn1/der_put.c b/third_party/heimdal/lib/asn1/der_put.c index 8fbd6f3da1c..106d456026c 100644 --- a/third_party/heimdal/lib/asn1/der_put.c +++ b/third_party/heimdal/lib/asn1/der_put.c @@ -343,7 +343,8 @@ der_put_octet_string (unsigned char *p, size_t len, if (len < data->length) return ASN1_OVERFLOW; p -= data->length; - memcpy (p+1, data->data, data->length); + if (data->length) + memcpy(p+1, data->data, data->length); *size = data->length; return 0; } @@ -378,19 +379,30 @@ der_put_heim_integer (unsigned char *p, size_t len, if (data->negative) { ssize_t i; int carry; - for (i = data->length - 1, carry = 1; i >= 0; i--) { - *p = buf[i] ^ 0xff; - if (carry) - carry = !++*p; - p--; - } - if (p[1] < 128) { - if (len < 1) - return ASN1_OVERFLOW; - *p-- = 0xff; - len--; - hibitset = 1; - } + + /* + * We represent the parsed integer as a positive value with a + * negativity flag. But we need to put it on the wire as the shortest + * twos-complement byte sequence possible. So we're going to negate + * the number as go. + */ + if (data->length == 1 && *(unsigned char *)data->data == 1) { + *(p--) = 0xff; + } else { + for (i = data->length - 1, carry = 1; i >= 0; i--) { + *p = buf[i] ^ 0xff; + if (carry) + carry = !++*p; + p--; + } + if (p[1] < 128) { + if (len < 1) + return ASN1_OVERFLOW; + *p-- = 0xff; + len--; + hibitset = 1; + } + } } else { p -= data->length; memcpy(p + 1, buf, data->length); diff --git a/third_party/heimdal/lib/asn1/gen.c b/third_party/heimdal/lib/asn1/gen.c index 10153c60379..06dc6bb701b 100644 --- a/third_party/heimdal/lib/asn1/gen.c +++ b/third_party/heimdal/lib/asn1/gen.c @@ -1118,6 +1118,7 @@ define_open_type(int level, const char *newbasename, const char *name, const cha /* Iterate objects in the object set, gen enum labels */ fprintf(headerfile, "enum { choice_%s_iosnumunknown = 0,\n", newbasename); + fprintf(jsonfile, "\"opentypeids\":["); for (i = 0; i < nobjs; i++) { HEIM_TAILQ_FOREACH(of, objects[i]->objfields, objfields) { if (strcmp(of->name, typeidfield->name) != 0) @@ -1127,8 +1128,11 @@ define_open_type(int level, const char *newbasename, const char *name, const cha of->name, objects[i]->symbol->name); fprintf(headerfile, "choice_%s_iosnum_%s,\n", newbasename, of->value->s->gen_name); + fprintf(jsonfile, "\"%s\"", of->value->s->gen_name); + fprintf(jsonfile, "%s", (i + 1) < nobjs ? "," : ""); } } + fprintf(jsonfile, "],\n"); fprintf(headerfile, "} element;\n"); if (is_array_of_open_type) @@ -1404,16 +1408,45 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t HEIM_TAILQ_FOREACH(m, t->members, members) { if (m->ellipsis) { ; - } else if (m->optional) { - char *n = NULL; + } else if (m->optional || m->defval) { + char *n = NULL, *defval = NULL; + const char *namep, *defvalp; + + if (m->defval) { + switch (m->defval->type) { + case stringvalue: + if (asprintf(&defval, "\"%s\"", m->defval->u.stringvalue) < 0 || defval == NULL) + errx(1, "malloc"); + defvalp = defval; + break; + case integervalue: + if (asprintf(&defval, "%lld", (long long)m->defval->u.integervalue) < 0 || defval == NULL) + errx(1, "malloc"); + defvalp = defval; + break; + case booleanvalue: + defvalp = m->defval->u.booleanvalue ? "true" : "false"; + break; + default: + abort(); + } + } else + defvalp = "null"; + + if (m->optional) { + if (asprintf(&n, "*%s", m->gen_name) < 0 || n == NULL) + errx(1, "malloc"); + namep = n; + } else + namep = m->gen_name; - if (asprintf(&n, "*%s", m->gen_name) < 0 || n == NULL) - errx(1, "malloc"); fprintf(jsonfile, "{\"name\":\"%s\",\"gen_name\":\"%s\"," - "\"optional\":true,\"type\":", m->name, m->gen_name); - define_type(level + 1, n, newbasename, t, m->type, FALSE, FALSE); + "\"optional\":%s,\"defval\":%s,\"type\":", + m->name, m->gen_name, m->optional ? "true" : "false", defvalp); + define_type(level + 1, namep, newbasename, t, m->type, FALSE, FALSE); fprintf(jsonfile, "}%s", last_member_p(m)); free (n); + free (defval); } else { fprintf(jsonfile, "{\"name\":\"%s\",\"gen_name\":\"%s\"," "\"optional\":false,\"type\":", m->name, m->gen_name); @@ -1498,6 +1531,9 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t fprintf(jsonfile, "\"ttype\":\"TeletexString\",\"ctype\":\"heim_general_string\""); break; case TTag: + if (t->implicit_choice) { + fprintf(jsonfile, "\"desired_tagenv\":\"IMPLICIT\","); + } fprintf(jsonfile, "\"tagclass\":\"%s\",\"tagvalue\":%d,\"tagenv\":\"%s\",\n", tagclassnames[t->tag.tagclass], t->tag.tagvalue, t->tag.tagenv == TE_EXPLICIT ? "EXPLICIT" : "IMPLICIT"); diff --git a/third_party/heimdal/lib/asn1/gen_free.c b/third_party/heimdal/lib/asn1/gen_free.c index 14e6529046c..ff80063ecec 100644 --- a/third_party/heimdal/lib/asn1/gen_free.c +++ b/third_party/heimdal/lib/asn1/gen_free.c @@ -56,7 +56,7 @@ free_type (const char *name, const Type *t, int preserve) free_primitive ("heim_integer", name); break; } - /* HEIM_FALLTHROUGH; */ + HEIM_FALLTHROUGH; case TBoolean: case TEnumerated : case TNull: @@ -237,4 +237,3 @@ generate_type_free (const Symbol *s) } fprintf (codefile, "}\n\n"); } - diff --git a/third_party/heimdal/lib/asn1/gen_template.c b/third_party/heimdal/lib/asn1/gen_template.c index ad25fcfb29d..67d18ecfff5 100644 --- a/third_party/heimdal/lib/asn1/gen_template.c +++ b/third_party/heimdal/lib/asn1/gen_template.c @@ -1238,7 +1238,7 @@ template_members(struct templatehead *temp, is_primitive_type(t->subtype); if (t->tag.tagenv == TE_IMPLICIT) { - Type *t2 = t->subtype ? t->subtype : t->symbol->type; + Type *t2 = t->subtype; while (t2->type == TType && (t2->subtype || t2->symbol->type)) t2 = t2->subtype ? t2->subtype : t2->symbol->type; diff --git a/third_party/heimdal/lib/asn1/main.c b/third_party/heimdal/lib/asn1/main.c index 569b4782b4d..babcc009c29 100644 --- a/third_party/heimdal/lib/asn1/main.c +++ b/third_party/heimdal/lib/asn1/main.c @@ -415,7 +415,7 @@ main(int argc, char **argv) buflen = strlen(buf); if ((ws = strspn(buf, " \t"))) - memmove(buf, buf + ws, buflen -= ws); + memmove(buf, buf + ws, buflen - ws); if (buf[0] == '\0' || buf[0] == '#') continue; diff --git a/third_party/heimdal/lib/asn1/symbol.h b/third_party/heimdal/lib/asn1/symbol.h index 8a24e251565..bce2e1fe421 100644 --- a/third_party/heimdal/lib/asn1/symbol.h +++ b/third_party/heimdal/lib/asn1/symbol.h @@ -200,6 +200,7 @@ struct type { struct range *range; struct constraint_spec *constraint; unsigned long id; + unsigned int implicit_choice:1; }; typedef struct type Type; diff --git a/third_party/heimdal/lib/asn1/template.c b/third_party/heimdal/lib/asn1/template.c index 31eb66004ec..ed215d88874 100644 --- a/third_party/heimdal/lib/asn1/template.c +++ b/third_party/heimdal/lib/asn1/template.c @@ -1176,6 +1176,13 @@ _asn1_decode(const struct asn1_template *t, unsigned flags, return ret; } if (i >= A1_HEADER_LEN(choice) + 1 || !choice[i].tt) { + /* + * If this is an extensible CHOICE, then choice->tt will be the + * offset to u.ellipsis. If it's not, then this "extension" is + * an error and must stop parsing it. (We could be permissive + * and throw away the extension, though one might as well just + * mark such a CHOICE as extensible.) + */ if (choice->tt == 0) return ASN1_BAD_ID; @@ -1786,16 +1793,20 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const } if (*element == 0) { - ret += der_put_octet_string(p, len, - DPOC(data, choice->tt), &datalen); + if (choice->tt) { + /* This is an extensible CHOICE */ + ret += der_put_octet_string(p, len, + DPOC(data, choice->tt), &datalen); + len -= datalen; p -= datalen; + } /* else this is really an error -- XXX what to do? */ } else { choice += *element; el = DPOC(data, choice->offset); ret = _asn1_encode(choice->ptr, p, len, el, &datalen); if (ret) return ret; + len -= datalen; p -= datalen; } - len -= datalen; p -= datalen; break; } @@ -2175,7 +2186,8 @@ _asn1_length(const struct asn1_template *t, const void *data) break; if (*element == 0) { - ret += der_length_octet_string(DPOC(data, choice->tt)); + if (choice->tt) + ret += der_length_octet_string(DPOC(data, choice->tt)); } else { choice += *element; ret += _asn1_length(choice->ptr, DPOC(data, choice->offset)); @@ -2350,7 +2362,16 @@ _asn1_free(const struct asn1_template *t, void *data) break; if (*element == 0) { - der_free_octet_string(DPO(data, choice->tt)); + /* + * If choice->tt != 0 then this is an extensible choice, and + * the offset choice->tt is the offset to u.ellipsis. + */ + if (choice->tt != 0) + der_free_octet_string(DPO(data, choice->tt)); + /* + * Else this was a not-fully initialized CHOICE. We could + * stand to memset clear the rest of it though... + */ } else { choice += *element; _asn1_free(choice->ptr, DPO(data, choice->offset)); @@ -2727,6 +2748,7 @@ _asn1_print(const struct asn1_template *t, if (*element > A1_HEADER_LEN(choice)) { r = rk_strpoolprintf(r, "null"); } else if (*element == 0) { + /* XXX If choice->tt then we should print the u.ellipsis */ r = rk_strpoolprintf(r, "null"); } else { choice += *element; @@ -2993,7 +3015,7 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to) unsigned int i; tel->val = calloc(fel->len, ellen); - if (tel->val == NULL) + if (tel->val == NULL && fel->len > 0) return ENOMEM; tel->len = fel->len; @@ -3024,7 +3046,12 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to) *telement = *felement; if (*felement == 0) { - ret = der_copy_octet_string(DPOC(from, choice->tt), DPO(to, choice->tt)); + if (choice->tt) + ret = der_copy_octet_string(DPOC(from, choice->tt), DPO(to, choice->tt)); + /* + * Else we should really memset clear the rest of this choice, + * but we don't really know its size. + */ } else { choice += *felement; ret = _asn1_copy(choice->ptr, diff --git a/third_party/heimdal/lib/base/Makefile.am b/third_party/heimdal/lib/base/Makefile.am index 18300b3cff5..606e00a975c 100644 --- a/third_party/heimdal/lib/base/Makefile.am +++ b/third_party/heimdal/lib/base/Makefile.am @@ -16,6 +16,8 @@ AM_CPPFLAGS += $(ROKEN_RENAME) -I../com_err -I$(srcdir)/../com_err lib_LTLIBRARIES = libheimbase.la check_PROGRAMS = test_base +test_base_CFLAGS = -Wno-string-concatenation + libheimbase_la_LDFLAGS = -version-info 1:0:0 if FRAMEWORK_COREFOUNDATION diff --git a/third_party/heimdal/lib/base/context.c b/third_party/heimdal/lib/base/context.c index fb9b442ce1e..f22ce9459f4 100644 --- a/third_party/heimdal/lib/base/context.c +++ b/third_party/heimdal/lib/base/context.c @@ -159,7 +159,7 @@ heim_get_debug_dest(heim_context context) heim_error_code heim_set_log_dest(heim_context context, heim_log_facility *fac) { - context->log_dest = fac; + context->log_dest = heim_log_ref(fac); return 0; } diff --git a/third_party/heimdal/lib/base/db.c b/third_party/heimdal/lib/base/db.c index b206ff6d766..e6f6af41a20 100644 --- a/third_party/heimdal/lib/base/db.c +++ b/third_party/heimdal/lib/base/db.c @@ -47,6 +47,8 @@ * memory-based rollback log is used). */ +#include "config.h" + #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -1505,11 +1507,12 @@ json_db_sync(void *db, heim_error_t *error) json = heim_json_copy_serialize(jsondb->dict, 0, &e); if (json == NULL) { + ret = heim_error_get_code(e); if (error) *error = e; else heim_release(e); - return heim_error_get_code(e); + return ret; } json_text = heim_string_get_utf8(json); diff --git a/third_party/heimdal/lib/base/dll.c b/third_party/heimdal/lib/base/dll.c index 59c39137b72..53f1f63bcdd 100644 --- a/third_party/heimdal/lib/base/dll.c +++ b/third_party/heimdal/lib/base/dll.c @@ -83,8 +83,8 @@ struct tls_values { static HEIMDAL_THREAD_LOCAL struct tls_values values; -static char dead_key; -#define DEAD_KEY ((void *)&dead_key) +static char dead_key[1]; +static void no_dtor(void *d) { (void)d; } void heim_w32_service_thread_detach(void *unused) @@ -112,7 +112,7 @@ heim_w32_service_thread_detach(void *unused) assert(i < key_defs->keys_start_idx + key_defs->keys_num); } dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx]; - if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY) + if (values.values[i] != NULL && dtor != NULL && dtor != no_dtor) dtor(values.values[i]); values.values[i] = NULL; } @@ -151,7 +151,7 @@ heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *)) #if !defined(WIN32) (void) pthread_once(&pt_once, create_pt_key); - (void) pthread_setspecific(pt_key, DEAD_KEY); + (void) pthread_setspecific(pt_key, dead_key); #endif HEIMDAL_MUTEX_lock(&tls_key_defs_lock); @@ -267,7 +267,7 @@ heim_w32_delete_key(HEIM_PRIV_thread_key key) key_lookup(key, &key_defs, &dtor_idx, NULL); if (key_defs == NULL) return EINVAL; - key_defs->keys_dtors[dtor_idx] = DEAD_KEY; + key_defs->keys_dtors[dtor_idx] = no_dtor; return 0; } @@ -280,7 +280,7 @@ heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value) size_t i; #if !defined(WIN32) - (void) pthread_setspecific(pt_key, DEAD_KEY); + (void) pthread_setspecific(pt_key, dead_key); #endif key_lookup(key, NULL, NULL, &dtor); @@ -305,7 +305,7 @@ heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value) assert(key < values.values_num); - if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY) + if (values.values[key] != NULL && dtor != NULL && dtor != no_dtor) dtor(values.values[key]); values.values[key] = value; diff --git a/third_party/heimdal/lib/base/error.c b/third_party/heimdal/lib/base/error.c index 6ba3bea412d..bc289d3d910 100644 --- a/third_party/heimdal/lib/base/error.c +++ b/third_party/heimdal/lib/base/error.c @@ -86,6 +86,7 @@ heim_error_create_enomem(void) void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4)) { if (error) { va_list ap; @@ -97,6 +98,7 @@ heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) heim_error_t heim_error_create(int error_code, const char *fmt, ...) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3)) { heim_error_t e; va_list ap; @@ -110,6 +112,7 @@ heim_error_create(int error_code, const char *fmt, ...) heim_error_t heim_error_createv(int error_code, const char *fmt, va_list ap) + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0)) { heim_error_t e; char *str; diff --git a/third_party/heimdal/lib/base/heimbase-atomics.h b/third_party/heimdal/lib/base/heimbase-atomics.h index 58b72ac4bc7..e468a36bda9 100644 --- a/third_party/heimdal/lib/base/heimbase-atomics.h +++ b/third_party/heimdal/lib/base/heimbase-atomics.h @@ -36,13 +36,15 @@ #ifndef HEIM_BASE_ATOMICS_H #define HEIM_BASE_ATOMICS_H 1 -#include "config.h" +#include <stdint.h> /* * Atomic operations + * + * (#define HEIM_BASE_ATOMICS_FALLBACK to test fallbacks.) */ -#if defined(HAVE_STDATOMIC_H) +#if !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(HAVE_STDATOMIC_H) #include <stdatomic.h> @@ -52,23 +54,55 @@ #define heim_base_atomic(T) _Atomic(T) -#define heim_base_atomic_inc(x) (atomic_fetch_add((x), 1) + 1) -#define heim_base_atomic_dec(x) (atomic_fetch_sub((x), 1) - 1) -#define heim_base_atomic_integer_type heim_base_atomic(unsigned int) -#define heim_base_atomic_integer_max UINT_MAX +#define heim_base_atomic_inc_32(x) (atomic_fetch_add((x), 1) + 1) +#define heim_base_atomic_dec_32(x) (atomic_fetch_sub((x), 1) - 1) +#define heim_base_atomic_inc_64(x) (atomic_fetch_add((x), 1) + 1) +#define heim_base_atomic_dec_64(x) (atomic_fetch_sub((x), 1) - 1) #define heim_base_exchange_pointer(t,v) atomic_exchange((t), (v)) #define heim_base_exchange_32(t,v) atomic_exchange((t), (v)) #define heim_base_exchange_64(t,v) atomic_exchange((t), (v)) -#elif defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) +/* + * <stdatomic.h>'s and AIX's CAS functions take a pointer to an expected value + * and return a boolean, setting the pointed-to variable to the old value of + * the target. + * + * Other CAS functions, like GCC's, Solaris'/Illumos', and Windows', return the + * old value and don't take a pointer to an expected value. + * + * We implement the latter semantics. + */ +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return atomic_compare_exchange_strong(t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH) #define heim_base_atomic_barrier() __sync_synchronize() -#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1) -#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1) -#define heim_base_atomic_integer_type unsigned int -#define heim_base_atomic_integer_max UINT_MAX +#define heim_base_atomic_inc_32(x) __sync_add_and_fetch((x), 1) +#define heim_base_atomic_dec_32(x) __sync_sub_and_fetch((x), 1) +#define heim_base_atomic_inc_64(x) __sync_add_and_fetch((x), 1) +#define heim_base_atomic_dec_64(x) __sync_sub_and_fetch((x), 1) #ifndef __has_builtin #define __has_builtin(x) 0 @@ -84,31 +118,47 @@ #define heim_base_exchange_32(t,v) heim_base_exchange_pointer((t), (v)) #define heim_base_exchange_64(t,v) heim_base_exchange_pointer((t), (v)) -#elif defined(__sun) +#define heim_base_cas_pointer(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_32(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) +#define heim_base_cas_64(t,e,d) __sync_val_compare_and_swap((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(__sun) #include <sys/atomic.h> +#include <mbarrier.h> -#define heim_base_atomic_barrier() __machine_rw_barrier() +static inline void __heim_base_atomic_barrier(void) +{ + __machine_rw_barrier(); +} + +#define heim_base_atomic_barrier() __heim_base_atomic_barrier() + +#define heim_base_atomic(T) volatile T + +#define heim_base_atomic_inc_32(x) atomic_inc_32_nv((x)) +#define heim_base_atomic_dec_32(x) atomic_dec_32_nv((x)) +#define heim_base_atomic_inc_64(x) atomic_inc_64_nv((x)) +#define heim_base_atomic_dec_64(x) atomic_dec_64_nv((x)) -#define heim_base_atomic_inc(x) atomic_inc_uint_nv((volatile uint_t *)(x)) -#define heim_base_atomic_dec(x) atomic_dec_uint_nv((volatile uint_t *)(x)) -#define heim_base_atomic_integer_type uint_t -#define heim_base_atomic_integer_max UINT_MAX +#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((t), (void *)(v)) +#define heim_base_exchange_32(t,v) atomic_swap_32((t), (v)) +#define heim_base_exchange_64(t,v) atomic_swap_64((t), (v)) -#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((volatile void *)(t), (void *)(v)) -#define heim_base_exchange_32(t,v) atomic_swap_32((volatile uint32_t *)(t), (v)) -#define heim_base_exchange_64(t,v) atomic_swap_64((volatile uint64_t *)(t), (v)) +#define heim_base_cas_pointer(t,e,d) atomic_cas_ptr((t), (e), (d)) +#define heim_base_cas_32(t,e,d) atomic_cas_32((t), (e), (d)) +#define heim_base_cas_64(t,e,d) atomic_cas_64((t), (e), (d)) -#elif defined(_AIX) +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_AIX) #include <sys/atomic_op.h> #define heim_base_atomic_barrier() __isync() -#define heim_base_atomic_inc(x) (fetch_and_add((atomic_p)(x)) + 1) -#define heim_base_atomic_dec(x) (fetch_and_add((atomic_p)(x)) - 1) -#define heim_base_atomic_integer_type unsigned int -#define heim_base_atomic_integer_max UINT_MAX +#define heim_base_atomic_inc_32(x) (fetch_and_add((atomic_p)(x), 1) + 1) +#define heim_base_atomic_dec_32(x) (fetch_and_add((atomic_p)(x), -1) - 1) +#define heim_base_atomic_inc_64(x) (fetch_and_addlp((atomic_l)(x), 1) + 1) +#define heim_base_atomic_dec_64(x) (fetch_and_addlp((atomic_l)(x), -1) - 1) static inline void * heim_base_exchange_pointer(void *p, void *newval) @@ -143,79 +193,160 @@ heim_base_exchange_64(uint64_t *p, uint64_t newval) return val; } -#elif defined(_WIN32) +static inline void * +heim_base_cas_pointer_(heim_base_atomic(void *)*t, void *e, void *d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +static inline uint32_t +heim_base_cas_32_(heim_base_atomic(uint32_t)*t, uint32_t e, uint32_t d) +{ + return compare_and_swap((atomic_p)t, &e, d), e; +} + +static inline uint64_t +heim_base_cas_64_(heim_base_atomic(uint64_t)*t, uint64_t e, uint64_t d) +{ + return compare_and_swaplp((atomic_l)t, &e, d), e; +} + +#define heim_base_cas_pointer(t,e,d) heim_base_cas_pointer_((t), (e), (d)) +#define heim_base_cas_32(t,e,d) heim_base_cas_32_((t), (e), (d)) +#define heim_base_cas_64(t,e,d) heim_base_cas_64_((t), (e), (d)) + +#elif !defined(HEIM_BASE_ATOMICS_FALLBACK) && defined(_WIN32) #define heim_base_atomic_barrier() MemoryBarrier() -#define heim_base_atomic_inc(x) InterlockedIncrement(x) -#define heim_base_atomic_dec(x) InterlockedDecrement(x) -#define heim_base_atomic_integer_type LONG -#define heim_base_atomic_integer_max MAXLONG +#define heim_base_atomic_inc_32(x) InterlockedIncrement(x) +#define heim_base_atomic_dec_32(x) InterlockedDecrement(x) +#define heim_base_atomic_inc_64(x) InterlockedIncrement64(x) +#define heim_base_atomic_dec_64(x) InterlockedDecrement64(x) #define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((PVOID volatile *)(t), (PVOID)(v)) #define heim_base_exchange_32(t,v) ((ULONG)InterlockedExchange((LONG volatile *)(t), (LONG)(v))) -#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((LONG64 violatile *)(t), (LONG64)(v))) +#define heim_base_exchange_64(t,v) ((ULONG64)InterlockedExchange64((ULONG64 volatile *)(t), (LONG64)(v))) + +#define heim_base_cas_pointer(t,e,d) InterlockedCompareExchangePointer((PVOID volatile *)(t), (d), (e)) +#define heim_base_cas_32(t,e,d) InterlockedCompareExchange ((LONG volatile *)(t), (d), (e)) +#define heim_base_cas_64(t,e,d) InterlockedCompareExchange64((ULONG64 volatile *)(t), (d), (e)) #else +#define heim_base_atomic(T) volatile T +#define heim_base_atomic_barrier() +#define heim_base_atomic_load(x) (*(x)) +#define heim_base_atomic_init(t, v) do { (*(t) = (v)); } while (0) +#define heim_base_atomic_store(t, v) do { (*(t) = (v)); } while (0) + #include <heim_threads.h> #define HEIM_BASE_NEED_ATOMIC_MUTEX 1 -extern HEIMDAL_MUTEX _heim_base_mutex; -#define heim_base_atomic_integer_type unsigned int -#define heim_base_atomic_integer_max UINT_MAX +static inline uint32_t +heim_base_atomic_inc_32(heim_base_atomic(uint32_t) *x) +{ + uint32_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = ++(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} -static inline heim_base_atomic_integer_type -heim_base_atomic_inc(heim_base_atomic_integer_type *x) +static inline uint32_t +heim_base_atomic_dec_32(heim_base_atomic(uint32_t) *x) { - heim_base_atomic_integer_type t; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); + uint32_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + t = --(*x); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return t; +} + +static inline uint64_t +heim_base_atomic_inc_64(heim_base_atomic(uint64_t) *x) +{ + uint64_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); t = ++(*x); - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); return t; } -static inline heim_base_atomic_integer_type -heim_base_atomic_dec(heim_base_atomic_integer_type *x) +static inline uint64_t +heim_base_atomic_dec_64(heim_base_atomic(uint64_t) *x) { - heim_base_atomic_integer_type t; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); + uint64_t t; + HEIMDAL_MUTEX_lock(heim_base_mutex()); t = --(*x); - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); return t; } static inline void * -heim_base_exchange_pointer(void *target, void *value) +heim_base_exchange_pointer(heim_base_atomic(void *)target, void *value) { void *old; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); + HEIMDAL_MUTEX_lock(heim_base_mutex()); old = *(void **)target; *(void **)target = value; - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + HEIMDAL_MUTEX_unlock(heim_base_mutex()); return old; } static inline uint32_t -heim_base_exchange_32(uint32_t *p, uint32_t newval) +heim_base_exchange_32(heim_base_atomic(uint32_t) *target, uint32_t newval) { uint32_t old; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); - old = *p; - *p = newval; - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + HEIMDAL_MUTEX_lock(heim_base_mutex()); + old = *target; + *target = newval; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); return old; } static inline uint64_t -heim_base_exchange_64(uint64_t *p, uint64_t newval) +heim_base_exchange_64(heim_base_atomic(uint64_t) *target, uint64_t newval) { uint64_t old; - HEIMDAL_MUTEX_lock(&_heim_base_mutex); - old = *p; - *p = newval; - HEIMDAL_MUTEX_unlock(&_heim_base_mutex); + HEIMDAL_MUTEX_lock(heim_base_mutex()); + old = *target; + *target = newval; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline void * +heim_base_cas_pointer(heim_base_atomic(void *)target, void *expected, void *desired) +{ + void *old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(void **)target) == expected) + *(void **)target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint32_t +heim_base_cas_32(heim_base_atomic(uint32_t) *target, uint32_t expected, uint32_t desired) +{ + uint32_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(uint32_t *)target) == expected) + *target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); + return old; +} + +static inline uint64_t +heim_base_cas_64(heim_base_atomic(uint64_t) *target, uint64_t expected,uint64_t desired) +{ + uint64_t old; + HEIMDAL_MUTEX_lock(heim_base_mutex()); + if ((old = *(uint64_t *)target) == expected) + *target = desired; + HEIMDAL_MUTEX_unlock(heim_base_mutex()); return old; } @@ -226,7 +357,7 @@ heim_base_exchange_64(uint64_t *p, uint64_t newval) #endif #ifndef heim_base_atomic_barrier -#define heim_base_atomic_barrier() +static inline void heim_base_atomic_barrier(void) { return; } #endif #ifndef heim_base_atomic_load diff --git a/third_party/heimdal/lib/base/heimbase.c b/third_party/heimdal/lib/base/heimbase.c index 1e6805a25e7..d9b15768136 100644 --- a/third_party/heimdal/lib/base/heimbase.c +++ b/third_party/heimdal/lib/base/heimbase.c @@ -34,13 +34,14 @@ */ #include "baselocl.h" +#include "heimbase-atomics.h" #include <syslog.h> -static heim_base_atomic_integer_type tidglobal = HEIM_TID_USER; +static heim_base_atomic(uint32_t) tidglobal = HEIM_TID_USER; struct heim_base { heim_type_t isa; - heim_base_atomic_integer_type ref_cnt; + heim_base_atomic(uint32_t) ref_cnt; HEIM_TAILQ_ENTRY(heim_base) autorel; heim_auto_release_t autorelpool; uintptr_t isaextra[3]; @@ -49,7 +50,7 @@ struct heim_base { /* specialized version of base */ struct heim_base_mem { heim_type_t isa; - heim_base_atomic_integer_type ref_cnt; + heim_base_atomic(uint32_t) ref_cnt; HEIM_TAILQ_ENTRY(heim_base) autorel; heim_auto_release_t autorelpool; const char *name; @@ -60,9 +61,12 @@ struct heim_base_mem { #define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1) #define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1)) -#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX -HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; -#endif +HEIMDAL_MUTEX * HEIM_CALLCONV +heim_base_mutex(void) +{ + static HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; + return &_heim_base_mutex; +} /* * Auto release structure @@ -93,10 +97,10 @@ heim_retain(heim_object_t ptr) p = PTR2BASE(ptr); - if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max) + if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX) return ptr; - if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0) + if ((heim_base_atomic_inc_32(&p->ref_cnt) - 1) == 0) heim_abort("resurection"); return ptr; } @@ -110,7 +114,7 @@ heim_retain(heim_object_t ptr) void heim_release(void *ptr) { - heim_base_atomic_integer_type old; + heim_base_atomic(uint32_t) old; struct heim_base *p; if (ptr == NULL || heim_base_is_tagged(ptr)) @@ -118,10 +122,10 @@ heim_release(void *ptr) p = PTR2BASE(ptr); - if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max) + if (heim_base_atomic_load(&p->ref_cnt) == UINT32_MAX) return; - old = heim_base_atomic_dec(&p->ref_cnt) + 1; + old = heim_base_atomic_dec_32(&p->ref_cnt) + 1; if (old > 1) return; @@ -160,7 +164,7 @@ void _heim_make_permanent(heim_object_t ptr) { struct heim_base *p = PTR2BASE(ptr); - heim_base_atomic_store(&p->ref_cnt, heim_base_atomic_integer_max); + heim_base_atomic_store(&p->ref_cnt, UINT32_MAX); } @@ -260,9 +264,12 @@ heim_cmp(heim_object_t a, heim_object_t b) static void HEIM_CALLCONV memory_dealloc(void *ptr) { - struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr); - if (p->dealloc) - p->dealloc(ptr); + if (ptr) { + struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr); + + if (p->dealloc) + p->dealloc(ptr); + } } struct heim_type_data memory_object = { @@ -318,7 +325,7 @@ _heim_create_type(const char *name, if (type == NULL) return NULL; - type->tid = heim_base_atomic_inc(&tidglobal); + type->tid = heim_base_atomic_inc_32(&tidglobal); type->name = name; type->init = init; type->dealloc = dealloc; @@ -500,6 +507,8 @@ heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *)) void heim_abort(const char *fmt, ...) + HEIMDAL_NORETURN_ATTRIBUTE + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2)) { va_list ap; va_start(ap, fmt); @@ -513,6 +522,8 @@ heim_abort(const char *fmt, ...) void heim_abortv(const char *fmt, va_list ap) + HEIMDAL_NORETURN_ATTRIBUTE + HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0)) { static char str[1024]; @@ -672,13 +683,14 @@ heim_object_t heim_auto_release(heim_object_t ptr) { struct heim_base *p; - struct ar_tls *tls = autorel_tls(); + struct ar_tls *tls; heim_auto_release_t ar; if (ptr == NULL || heim_base_is_tagged(ptr)) return ptr; p = PTR2BASE(ptr); + tls = autorel_tls(); /* drop from old pool */ if ((ar = p->autorelpool) != NULL) { @@ -913,14 +925,14 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, heim_abort("heim_path_vcreate() does not create root nodes"); while (path_element != NULL) { + int idx = -1; + next_path_element = va_arg(ap, heim_object_t); node_type = heim_get_tid(node); if (node_type == HEIM_TID_DICT) { next_node = heim_dict_get_value(node, path_element); } else if (node_type == HEIM_TID_ARRAY) { - int idx = -1; - if (heim_get_tid(path_element) == HEIM_TID_NUMBER) idx = heim_number_get_int(path_element); if (idx < 0) { @@ -931,10 +943,16 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, "and positive"); return EINVAL; } - if (idx < heim_array_get_length(node)) + if (idx < heim_array_get_length(node)) { next_node = heim_array_get_value(node, idx); - else + } else if (idx == heim_array_get_length(node)) { next_node = NULL; + } else { + if (error) + *error = heim_error_create(EINVAL, + "Index for array in path is too large"); + return EINVAL; + } } else if (node_type == HEIM_TID_DB && next_path_element != NULL) { if (error) *error = heim_error_create(EINVAL, "Interior node is a DB"); @@ -946,26 +964,31 @@ heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, /* Create missing interior node */ if (next_node == NULL) { - next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */ - if (next_node == NULL) { + heim_dict_t new_node; + + new_node = heim_dict_create(size); /* no arrays or DBs, just dicts */ + if (new_node == NULL) { ret = ENOMEM; goto err; } if (node_type == HEIM_TID_DICT) { - ret = heim_dict_set_value(node, path_element, next_node); + ret = heim_dict_set_value(node, path_element, new_node); + next_node = heim_dict_get_value(node, path_element); } else if (node_type == HEIM_TID_ARRAY && heim_number_get_int(path_element) <= heim_array_get_length(node)) { ret = heim_array_insert_value(node, heim_number_get_int(path_element), - next_node); + new_node); + next_node = heim_array_get_value(node, idx); } else { ret = EINVAL; if (error) *error = heim_error_create(ret, "Node in path not a " "container"); } - heim_release(next_node); + + heim_release(new_node); if (ret) goto err; } diff --git a/third_party/heimdal/lib/base/heimbase.h b/third_party/heimdal/lib/base/heimbase.h index 5d5c4e3b75e..d2c011414c7 100644 --- a/third_party/heimdal/lib/base/heimbase.h +++ b/third_party/heimdal/lib/base/heimbase.h @@ -36,8 +36,6 @@ #ifndef HEIM_BASE_H #define HEIM_BASE_H 1 -#include "config.h" - #include <sys/types.h> #ifndef _WIN32 #include <sys/socket.h> @@ -190,38 +188,8 @@ typedef long heim_base_once_t; /* XXX arch dependant */ #endif -heim_object_t heim_retain(heim_object_t); -void heim_release(heim_object_t); - -void heim_show(heim_object_t); - typedef void (HEIM_CALLCONV *heim_type_dealloc)(void *); -void * -heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc); - -heim_tid_t -heim_get_tid(heim_object_t object); - -int -heim_cmp(heim_object_t a, heim_object_t b); - -uintptr_t -heim_get_hash(heim_object_t ptr); - -void -heim_base_once_f(heim_base_once_t *, void *, void (*)(void *)); - -void -heim_abort(const char *fmt, ...) - HEIMDAL_NORETURN_ATTRIBUTE - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2)); - -void -heim_abortv(const char *fmt, va_list ap) - HEIMDAL_NORETURN_ATTRIBUTE - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0)); - #define heim_assert(e,t) \ (heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0) @@ -229,70 +197,23 @@ heim_abortv(const char *fmt, va_list ap) * */ -heim_null_t -heim_null_create(void); - -heim_bool_t -heim_bool_create(int); - -int -heim_bool_val(heim_bool_t); - /* * Array */ typedef struct heim_array_data *heim_array_t; -heim_array_t heim_array_create(void); -heim_tid_t heim_array_get_type_id(void); - typedef void (*heim_array_iterator_f_t)(heim_object_t, void *, int *); typedef int (*heim_array_filter_f_t)(heim_object_t, void *); -int heim_array_append_value(heim_array_t, heim_object_t); -int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t); -void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t); -void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t); -#ifdef __BLOCKS__ -void heim_array_iterate(heim_array_t, void (^)(heim_object_t, int *)); -void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t, int *)); -#endif -size_t heim_array_get_length(heim_array_t); -heim_object_t - heim_array_get_value(heim_array_t, size_t); -heim_object_t - heim_array_copy_value(heim_array_t, size_t); -void heim_array_set_value(heim_array_t, size_t, heim_object_t); -void heim_array_delete_value(heim_array_t, size_t); -void heim_array_filter_f(heim_array_t, void *, heim_array_filter_f_t); -#ifdef __BLOCKS__ -void heim_array_filter(heim_array_t, int (^)(heim_object_t)); -#endif - /* * Dict */ typedef struct heim_dict_data *heim_dict_t; -heim_dict_t heim_dict_create(size_t size); -heim_tid_t heim_dict_get_type_id(void); - typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *); -int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t); -void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t); -#ifdef __BLOCKS__ -void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t)); -#endif - -heim_object_t - heim_dict_get_value(heim_dict_t, heim_object_t); -heim_object_t - heim_dict_copy_value(heim_dict_t, heim_object_t); -void heim_dict_delete_key(heim_dict_t, heim_object_t); - /* * String */ @@ -300,15 +221,6 @@ void heim_dict_delete_key(heim_dict_t, heim_object_t); typedef struct heim_string_data *heim_string_t; typedef void (*heim_string_free_f_t)(void *); -heim_string_t heim_string_create(const char *); -heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t); -heim_string_t heim_string_create_with_bytes(const void *, size_t); -heim_string_t heim_string_ref_create_with_bytes(const void *, size_t, - heim_string_free_f_t); -heim_string_t heim_string_create_with_format(const char *, ...); -heim_tid_t heim_string_get_type_id(void); -const char * heim_string_get_utf8(heim_string_t); - #define HSTR(_str) (__heim_string_constant("" _str "")) heim_string_t __heim_string_constant(const char *); @@ -318,41 +230,10 @@ heim_string_t __heim_string_constant(const char *); typedef struct heim_error * heim_error_t; -heim_error_t heim_error_create_enomem(void); - -heim_error_t heim_error_create(int, const char *, ...) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3)); - -void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4)); - -heim_error_t heim_error_createv(int, const char *, va_list) - HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0)); - -heim_string_t heim_error_copy_string(heim_error_t); -int heim_error_get_code(heim_error_t); - -heim_error_t heim_error_append(heim_error_t, heim_error_t); - /* * Path */ -heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...); -heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...); -heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error, - va_list ap); -heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error, - va_list ap); - -int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf, - heim_error_t *error, va_list ap); -int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf, - heim_error_t *error, ...); - -void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap); -void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...); - /* * Data (octet strings) */ @@ -369,14 +250,6 @@ typedef struct heim_base_data heim_octet_string; typedef struct heim_base_data * heim_data_t; typedef void (*heim_data_free_f_t)(void *); -heim_data_t heim_data_create(const void *, size_t); -heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t); -heim_tid_t heim_data_get_type_id(void); -const heim_octet_string * - heim_data_get_data(heim_data_t); -const void * heim_data_get_ptr(heim_data_t); -size_t heim_data_get_length(heim_data_t); - /* * DB */ @@ -426,53 +299,18 @@ extern struct heim_db_type heim_sorted_text_file_dbtype; #define HEIM_DB_TYPE_VERSION_01 1 -int heim_db_register(const char *dbtype, - void *data, - struct heim_db_type *plugin); - -heim_db_t heim_db_create(const char *dbtype, const char *dbname, - heim_dict_t options, heim_error_t *error); -heim_db_t heim_db_clone(heim_db_t, heim_error_t *); -int heim_db_begin(heim_db_t, int, heim_error_t *); -int heim_db_commit(heim_db_t, heim_error_t *); -int heim_db_rollback(heim_db_t, heim_error_t *); -heim_tid_t heim_db_get_type_id(void); - -int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t, - heim_error_t *); -heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t, - heim_error_t *); -int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t, - heim_error_t *); -void heim_db_iterate_f(heim_db_t, heim_string_t, void *, - heim_db_iterator_f_t, heim_error_t *); -#ifdef __BLOCKS__ -void heim_db_iterate(heim_db_t, heim_string_t, - void (^)(heim_data_t, heim_data_t), heim_error_t *); -#endif - - /* * Number */ typedef struct heim_number_data *heim_number_t; -heim_number_t heim_number_create(int64_t); -heim_tid_t heim_number_get_type_id(void); -int heim_number_get_int(heim_number_t); -int64_t heim_number_get_long(heim_number_t); - /* - * + * Autorelease */ typedef struct heim_auto_release * heim_auto_release_t; -heim_auto_release_t heim_auto_release_create(void); -void heim_auto_release_drain(heim_auto_release_t); -heim_object_t heim_auto_release(heim_object_t); - /* * JSON */ @@ -494,22 +332,10 @@ typedef enum heim_json_flags { HEIM_JSON_F_INDENT8 = 4096, } heim_json_flags_t; -heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t, - heim_error_t *); -heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t, - heim_json_flags_t, - heim_error_t *); -heim_string_t heim_json_copy_serialize(heim_object_t, heim_json_flags_t, - heim_error_t *); - - /* * Debug */ -heim_string_t -heim_description(heim_object_t ptr); - /* * Binary search. * @@ -530,14 +356,7 @@ void _bsearch_file_close(bsearch_file_handle *bfh); * Thread-specific keys */ -int heim_w32_key_create(unsigned long *, void (*)(void *)); -int heim_w32_delete_key(unsigned long); -int heim_w32_setspecific(unsigned long, void *); -void *heim_w32_getspecific(unsigned long); -void heim_w32_service_thread_detach(void *); - #include <heim_threads.h> -#include "heimbase-atomics.h" #include <com_err.h> /* diff --git a/third_party/heimdal/lib/base/json.c b/third_party/heimdal/lib/base/json.c index c7ef0658850..4fa0f2d5aff 100644 --- a/third_party/heimdal/lib/base/json.c +++ b/third_party/heimdal/lib/base/json.c @@ -893,7 +893,7 @@ parse_string(struct parse_ctx *ctx) continue; /* This will cause p0 to be realloc'ed */ } p_save = ctx->p; - ctop = cbot = -3; + cbot = -3; ctop = unescape_unicode(ctx); if (ctop == -1 && strict) return parse_string_error(ctx, p0, "Invalid escaped Unicode"); diff --git a/third_party/heimdal/lib/base/log.c b/third_party/heimdal/lib/base/log.c index 1d79c7e45b9..9a97276e153 100644 --- a/third_party/heimdal/lib/base/log.c +++ b/third_party/heimdal/lib/base/log.c @@ -35,6 +35,7 @@ #include "baselocl.h" #include "heim_threads.h" +#include "heimbase-atomics.h" #include "heimbase.h" #include "heimbase-svc.h" #include <assert.h> @@ -52,7 +53,7 @@ struct heim_log_facility_internal { struct heim_log_facility_s { char *program; - size_t refs; + heim_base_atomic(uint32_t) refs; size_t len; struct heim_log_facility_internal *val; }; @@ -156,7 +157,7 @@ heim_log_facility * heim_log_ref(heim_log_facility *fac) { if (fac) - fac->refs++; + (void) heim_base_atomic_inc_32(&fac->refs); return fac; } @@ -226,6 +227,8 @@ open_syslog(heim_context context, close_syslog, sd); if (ret) free(sd); + else + sd = NULL; return ret; } @@ -347,6 +350,7 @@ open_file(heim_context context, heim_log_facility *fac, int min, int max, free(fd); } else if (disp & FILEDISP_KEEPOPEN) { log_file(context, NULL, NULL, fd); + fd = NULL; } return ret; } @@ -463,7 +467,7 @@ heim_closelog(heim_context context, heim_log_facility *fac) { int i; - if (!fac || --(fac->refs)) + if (!fac || heim_base_atomic_dec_32(&fac->refs)) return; for (i = 0; i < fac->len; i++) (*fac->val[i].close_func)(fac->val[i].data); diff --git a/third_party/heimdal/lib/base/test_base.c b/third_party/heimdal/lib/base/test_base.c index 6d0668663af..cc875d7f8b6 100644 --- a/third_party/heimdal/lib/base/test_base.c +++ b/third_party/heimdal/lib/base/test_base.c @@ -64,6 +64,7 @@ #include <fcntl.h> #include "baselocl.h" +#include "heimbase-atomics.h" static void HEIM_CALLCONV memory_free(heim_object_t obj) @@ -1273,6 +1274,84 @@ test_array() return 0; } +/* This function tests only that heimbase-atomics.h compiles */ +static int +test_atomics(void) +{ + heim_base_atomic(void *) tptr; + heim_base_atomic(uint32_t) tu32; + heim_base_atomic(uint64_t) tu64; + + heim_base_atomic_init(&tptr, NULL); + heim_base_atomic_init(&tu32, 0); + heim_base_atomic_init(&tu64, 0); + + if (heim_base_atomic_load(&tptr)) + return 1; + if (heim_base_atomic_load(&tu32)) + return 1; + if (heim_base_atomic_load(&tu64)) + return 1; + + heim_base_atomic_store(&tptr, &tptr); + heim_base_atomic_store(&tu32, 1); + heim_base_atomic_store(&tu64, 1); + + if (heim_base_atomic_load(&tptr) != &tptr) + return 1; + if (heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_load(&tu64) != 1) + return 1; + + if (heim_base_atomic_inc_32(&tu32) != 2 || + heim_base_atomic_load(&tu32) != 2) + return 1; + if (heim_base_atomic_inc_64(&tu64) != 2 || + heim_base_atomic_load(&tu64) != 2) + return 1; + + if (heim_base_atomic_dec_32(&tu32) != 1 || + heim_base_atomic_load(&tu32) != 1) + return 1; + if (heim_base_atomic_dec_64(&tu64) != 1 || + heim_base_atomic_load(&tu64) != 1) + return 1; + + heim_base_exchange_pointer(&tptr, (void *)&tu32); + if (heim_base_atomic_load(&tptr) != &tu32) + return 1; + heim_base_exchange_32(&tu32, 32); + if (heim_base_atomic_load(&tu32) != 32) + return 1; + heim_base_exchange_64(&tu64, 64); + if (heim_base_atomic_load(&tu64) != 64) + return 1; + + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tu64) != &tu32) + return 1; + if (heim_base_cas_pointer(&tptr, (void *)&tu32, (void *)&tptr) != &tu64) + return 1; + if (heim_base_atomic_load(&tptr) != (void *)&tu64) + return 1; + + if (heim_base_cas_32(&tu32, 32, 4) != 32) + return 1; + if (heim_base_cas_32(&tu32, 32, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu32) != 4) + return 1; + + if (heim_base_cas_64(&tu64, 64, 4) != 64) + return 1; + if (heim_base_cas_64(&tu64, 64, 4) != 4) + return 1; + if (heim_base_atomic_load(&tu64) != 4) + return 1; + + return 0; +} + int main(int argc, char **argv) { @@ -1295,6 +1374,7 @@ main(int argc, char **argv) res |= test_db(NULL, NULL); res |= test_db("json", argc > 1 ? argv[1] : "test_db.json"); res |= test_array(); + res |= test_atomics(); return res ? 1 : 0; } diff --git a/third_party/heimdal/lib/base/version-script.map b/third_party/heimdal/lib/base/version-script.map index 9493ee69236..1ba88992985 100644 --- a/third_party/heimdal/lib/base/version-script.map +++ b/third_party/heimdal/lib/base/version-script.map @@ -44,6 +44,7 @@ HEIMDAL_BASE_1.0 { heim_auto_release_create; heim_auto_release_drain; heim_base_once_f; + heim_base_mutex; heim_bool_create; heim_bool_val; heim_clear_error_message; diff --git a/third_party/heimdal/lib/gssapi/gss-token.c b/third_party/heimdal/lib/gssapi/gss-token.c index f3f90521c3b..a4c02967cf3 100644 --- a/third_party/heimdal/lib/gssapi/gss-token.c +++ b/third_party/heimdal/lib/gssapi/gss-token.c @@ -472,8 +472,13 @@ accept_one(gss_name_t service, const char *ccname, int negotiate) NULL, NULL, &deleg_creds); ret = write_and_free_token(&out, negotiate); - if (ret) + if (ret) { + OM_uint32 junk; + + (void) gss_delete_sec_context(&junk, &ctx, + GSS_C_NO_BUFFER); return ret; + } GBAIL("gss_accept_sec_context", maj, min); } while (maj & GSS_S_CONTINUE_NEEDED); @@ -491,6 +496,7 @@ accept_one(gss_name_t service, const char *ccname, int negotiate) (char *)dname.value); (void) gss_release_buffer(&min, &dname); (void) gss_release_name(&min, &client); + (void) gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER); if (ccname) { #ifdef HAVE_GSS_STORE_CRED_INTO diff --git a/third_party/heimdal/lib/gssapi/krb5/8003.c b/third_party/heimdal/lib/gssapi/krb5/8003.c index bf7da11c754..0b91cf83b82 100644 --- a/third_party/heimdal/lib/gssapi/krb5/8003.c +++ b/third_party/heimdal/lib/gssapi/krb5/8003.c @@ -33,48 +33,6 @@ #include "gsskrb5_locl.h" -krb5_error_code -_gsskrb5_encode_om_uint32(OM_uint32 n, u_char *p) -{ - p[0] = (n >> 0) & 0xFF; - p[1] = (n >> 8) & 0xFF; - p[2] = (n >> 16) & 0xFF; - p[3] = (n >> 24) & 0xFF; - return 0; -} - -krb5_error_code -_gsskrb5_encode_be_om_uint32(OM_uint32 n, u_char *p) -{ - p[0] = (n >> 24) & 0xFF; - p[1] = (n >> 16) & 0xFF; - p[2] = (n >> 8) & 0xFF; - p[3] = (n >> 0) & 0xFF; - return 0; -} - -krb5_error_code -_gsskrb5_decode_om_uint32(const void *ptr, OM_uint32 *n) -{ - const u_char *p = ptr; - *n = ((uint32_t)p[0]) - | ((uint32_t)p[1] << 8) - | ((uint32_t)p[2] << 16) - | ((uint32_t)p[3] << 24); - return 0; -} - -krb5_error_code -_gsskrb5_decode_be_om_uint32(const void *ptr, OM_uint32 *n) -{ - const u_char *p = ptr; - *n = ((uint32_t)p[0] <<24) - | ((uint32_t)p[1] << 16) - | ((uint32_t)p[2] << 8) - | ((uint32_t)p[3]); - return 0; -} - static krb5_error_code hash_input_chan_bindings (const gss_channel_bindings_t b, u_char *p) @@ -85,23 +43,23 @@ hash_input_chan_bindings (const gss_channel_bindings_t b, ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); - _gsskrb5_encode_om_uint32 (b->initiator_addrtype, num); + _gss_mg_encode_le_uint32 (b->initiator_addrtype, num); EVP_DigestUpdate(ctx, num, sizeof(num)); - _gsskrb5_encode_om_uint32 (b->initiator_address.length, num); + _gss_mg_encode_le_uint32 (b->initiator_address.length, num); EVP_DigestUpdate(ctx, num, sizeof(num)); if (b->initiator_address.length) EVP_DigestUpdate(ctx, b->initiator_address.value, b->initiator_address.length); - _gsskrb5_encode_om_uint32 (b->acceptor_addrtype, num); + _gss_mg_encode_le_uint32 (b->acceptor_addrtype, num); EVP_DigestUpdate(ctx, num, sizeof(num)); - _gsskrb5_encode_om_uint32 (b->acceptor_address.length, num); + _gss_mg_encode_le_uint32 (b->acceptor_address.length, num); EVP_DigestUpdate(ctx, num, sizeof(num)); if (b->acceptor_address.length) EVP_DigestUpdate(ctx, b->acceptor_address.value, b->acceptor_address.length); - _gsskrb5_encode_om_uint32 (b->application_data.length, num); + _gss_mg_encode_le_uint32 (b->application_data.length, num); EVP_DigestUpdate(ctx, num, sizeof(num)); if (b->application_data.length) EVP_DigestUpdate(ctx, @@ -144,7 +102,7 @@ _gsskrb5_create_8003_checksum ( } p = result->checksum.data; - _gsskrb5_encode_om_uint32 (16, p); + _gss_mg_encode_le_uint32 (16, p); p += 4; if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS) { memset (p, 0, 16); @@ -152,7 +110,7 @@ _gsskrb5_create_8003_checksum ( hash_input_chan_bindings (input_chan_bindings, p); } p += 16; - _gsskrb5_encode_om_uint32 (flags, p); + _gss_mg_encode_le_uint32 (flags, p); p += 4; if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) { @@ -244,7 +202,7 @@ _gsskrb5_verify_8003_checksum( } p = cksum->checksum.data; - _gsskrb5_decode_om_uint32(p, &length); + _gss_mg_decode_le_uint32(p, &length); if(length != sizeof(hash)) { *minor_status = 0; return GSS_S_BAD_BINDINGS; @@ -273,7 +231,7 @@ _gsskrb5_verify_8003_checksum( p += sizeof(hash); - _gsskrb5_decode_om_uint32(p, flags); + _gss_mg_decode_le_uint32(p, flags); p += 4; if (cksum->checksum.length > 24 && (*flags & GSS_C_DELEG_FLAG)) { diff --git a/third_party/heimdal/lib/gssapi/krb5/arcfour.c b/third_party/heimdal/lib/gssapi/krb5/arcfour.c index 5c754bc6d52..787a8d3d2d6 100644 --- a/third_party/heimdal/lib/gssapi/krb5/arcfour.c +++ b/third_party/heimdal/lib/gssapi/krb5/arcfour.c @@ -293,7 +293,7 @@ _gssapi_get_mic_arcfour(OM_uint32 * minor_status, context_handle->auth_context, &seq_number); p = p0 + 8; /* SND_SEQ */ - _gsskrb5_encode_be_om_uint32(seq_number, p); + _gss_mg_encode_be_uint32(seq_number, p); krb5_auth_con_setlocalseqnumber (context, context_handle->auth_context, @@ -385,7 +385,7 @@ _gssapi_verify_mic_arcfour(OM_uint32 * minor_status, memset(k6_data, 0, sizeof(k6_data)); } - _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number); + _gss_mg_decode_be_uint32(SND_SEQ, &seq_number); if (context_handle->more_flags & LOCAL) cmp = (ct_memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4) != 0); @@ -473,7 +473,7 @@ _gssapi_wrap_arcfour(OM_uint32 * minor_status, context_handle->auth_context, &seq_number); - _gsskrb5_encode_be_om_uint32(seq_number, p0 + 8); + _gss_mg_encode_be_uint32(seq_number, p0 + 8); krb5_auth_con_setlocalseqnumber (context, context_handle->auth_context, @@ -656,7 +656,7 @@ OM_uint32 _gssapi_unwrap_arcfour(OM_uint32 *minor_status, memset_s(k6_data, sizeof(k6_data), 0, sizeof(k6_data)); } - _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number); + _gss_mg_decode_be_uint32(SND_SEQ, &seq_number); if (context_handle->more_flags & LOCAL) cmp = (ct_memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4) != 0); @@ -1042,7 +1042,7 @@ _gssapi_wrap_iov_arcfour(OM_uint32 *minor_status, krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_number); - _gsskrb5_encode_be_om_uint32(seq_number, p0 + 8); + _gss_mg_encode_be_uint32(seq_number, p0 + 8); krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, @@ -1279,7 +1279,7 @@ _gssapi_unwrap_iov_arcfour(OM_uint32 *minor_status, memset(k6_data, 0, sizeof(k6_data)); } - _gsskrb5_decode_be_om_uint32(snd_seq, &seq_number); + _gss_mg_decode_be_uint32(snd_seq, &seq_number); if (ctx->more_flags & LOCAL) { cmp = (ct_memcmp(&snd_seq[4], "\xff\xff\xff\xff", 4) != 0); diff --git a/third_party/heimdal/lib/gssapi/krb5/cfx.c b/third_party/heimdal/lib/gssapi/krb5/cfx.c index 8d806f9fbf1..cb9ea773bbb 100644 --- a/third_party/heimdal/lib/gssapi/krb5/cfx.c +++ b/third_party/heimdal/lib/gssapi/krb5/cfx.c @@ -276,11 +276,7 @@ _gk_verify_buffers(OM_uint32 *minor_status, /* * In DCE style mode we reject having a padding or trailer buffer */ - if (padding) { - *minor_status = EINVAL; - return GSS_S_FAILURE; - } - if (trailer) { + if (padding || trailer) { *minor_status = EINVAL; return GSS_S_FAILURE; } @@ -505,8 +501,8 @@ _gssapi_wrap_cfx_iov(OM_uint32 *minor_status, krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_number); - _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]); - _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]); + _gss_mg_encode_be_uint32(0, &token->SND_SEQ[0]); + _gss_mg_encode_be_uint32(seq_number, &token->SND_SEQ[4]); krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, ++seq_number); @@ -817,8 +813,8 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status, /* * Check sequence number */ - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi); - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo); + _gss_mg_decode_be_uint32(&token->SND_SEQ[0], &seq_number_hi); + _gss_mg_decode_be_uint32(&token->SND_SEQ[4], &seq_number_lo); if (seq_number_hi) { /* no support for 64-bit sequence numbers */ *minor_status = ERANGE; @@ -1271,8 +1267,8 @@ OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status, krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_number); - _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]); - _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]); + _gss_mg_encode_be_uint32(0, &token->SND_SEQ[0]); + _gss_mg_encode_be_uint32(seq_number, &token->SND_SEQ[4]); krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, ++seq_number); @@ -1458,8 +1454,8 @@ OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status, /* * Check sequence number */ - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi); - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo); + _gss_mg_decode_be_uint32(&token->SND_SEQ[0], &seq_number_hi); + _gss_mg_decode_be_uint32(&token->SND_SEQ[4], &seq_number_lo); if (seq_number_hi) { /* no support for 64-bit sequence numbers */ *minor_status = ERANGE; @@ -1623,7 +1619,10 @@ OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status, return GSS_S_FAILURE; } - memcpy(buf, message_buffer->value, message_buffer->length); + if (message_buffer->length) + memcpy(buf, message_buffer->value, message_buffer->length); + else + memset(buf, 0, len); token = (gss_cfx_mic_token)(buf + message_buffer->length); token->TOK_ID[0] = 0x04; @@ -1639,8 +1638,8 @@ OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status, krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_number); - _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]); - _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]); + _gss_mg_encode_be_uint32(0, &token->SND_SEQ[0]); + _gss_mg_encode_be_uint32(seq_number, &token->SND_SEQ[4]); krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, ++seq_number); @@ -1733,8 +1732,8 @@ OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status, /* * Check sequence number */ - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi); - _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo); + _gss_mg_decode_be_uint32(&token->SND_SEQ[0], &seq_number_hi); + _gss_mg_decode_be_uint32(&token->SND_SEQ[4], &seq_number_lo); if (seq_number_hi) { *minor_status = ERANGE; return GSS_S_UNSEQ_TOKEN; @@ -1773,7 +1772,8 @@ OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status, *minor_status = ENOMEM; return GSS_S_FAILURE; } - memcpy(buf, message_buffer->value, message_buffer->length); + if (message_buffer->length) + memcpy(buf, message_buffer->value, message_buffer->length); memcpy(buf + message_buffer->length, token, sizeof(*token)); ret = krb5_verify_checksum(context, ctx->crypto, diff --git a/third_party/heimdal/lib/gssapi/krb5/creds.c b/third_party/heimdal/lib/gssapi/krb5/creds.c index efb0a23ad92..036d9fa072a 100644 --- a/third_party/heimdal/lib/gssapi/krb5/creds.c +++ b/third_party/heimdal/lib/gssapi/krb5/creds.c @@ -263,10 +263,15 @@ _gsskrb5_import_cred(OM_uint32 * minor_status, *minor_status = ENOMEM; return GSS_S_FAILURE; } + *minor_status = krb5_cc_get_principal(context, id, &handle->principal); + if (*minor_status) { + free(handle); + krb5_cc_close(context, id); + return GSS_S_FAILURE; + } handle->usage = GSS_C_INITIATE; handle->destination_realm = NULL; - krb5_cc_get_principal(context, id, &handle->principal); handle->ccache = id; handle->cred_flags = flags; diff --git a/third_party/heimdal/lib/gssapi/krb5/duplicate_cred.c b/third_party/heimdal/lib/gssapi/krb5/duplicate_cred.c index f5b34c5144b..a44ec3b239c 100644 --- a/third_party/heimdal/lib/gssapi/krb5/duplicate_cred.c +++ b/third_party/heimdal/lib/gssapi/krb5/duplicate_cred.c @@ -76,12 +76,6 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_duplicate_cred ( HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); dup->destination_realm = NULL; - if (cred->destination_realm && - (dup->destination_realm = strdup(cred->destination_realm)) == NULL) { - *minor_status = krb5_enomem(context); - free(dup); - return (GSS_S_FAILURE); - } dup->usage = cred->usage; dup->endtime = cred->endtime; dup->principal = NULL; @@ -92,6 +86,11 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_duplicate_cred ( major = GSS_S_FAILURE; HEIMDAL_MUTEX_init(&dup->cred_id_mutex); + if (cred->destination_realm && + (dup->destination_realm = strdup(cred->destination_realm)) == NULL) { + *minor_status = krb5_enomem(context); + goto fail; + } *minor_status = krb5_copy_principal(context, cred->principal, &dup->principal); if (*minor_status) diff --git a/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c b/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c index a705d03a875..7749fc6cadd 100644 --- a/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c +++ b/third_party/heimdal/lib/gssapi/krb5/init_sec_context.c @@ -742,6 +742,9 @@ repl_mutual output_token->length = 0; output_token->value = NULL; + if (input_token == GSS_C_NO_BUFFER) + return GSS_S_FAILURE; + if (actual_mech_type) *actual_mech_type = GSS_KRB5_MECHANISM; @@ -799,10 +802,10 @@ repl_mutual *minor_status = 0; if (time_rec) - _gsskrb5_lifetime_left(minor_status, - context, - ctx->endtime, - time_rec); + (void) _gsskrb5_lifetime_left(minor_status, + context, + ctx->endtime, + time_rec); if (ret_flags) *ret_flags = ctx->flags; diff --git a/third_party/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c b/third_party/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c index f57277422a2..49d86d11cd5 100644 --- a/third_party/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c +++ b/third_party/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c @@ -90,7 +90,7 @@ static OM_uint32 inquire_sec_context_tkt_flags tkt_flags = TicketFlags2int(context_handle->ticket->ticket.flags); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); - _gsskrb5_encode_om_uint32(tkt_flags, buf); + _gss_mg_encode_le_uint32(tkt_flags, buf); value.length = sizeof(buf); value.value = buf; @@ -430,8 +430,8 @@ get_authtime(OM_uint32 *minor_status, { gss_buffer_desc value; - unsigned char buf[4]; - OM_uint32 authtime; + unsigned char buf[SIZEOF_TIME_T]; + time_t authtime; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (ctx->ticket == NULL) { @@ -445,7 +445,13 @@ get_authtime(OM_uint32 *minor_status, HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); - _gsskrb5_encode_om_uint32(authtime, buf); +#if SIZEOF_TIME_T == 8 + _gss_mg_encode_le_uint64(authtime, buf); +#elif SIZEOF_TIME_T == 4 + _gss_mg_encode_le_uint32(authtime, buf); +#else +#error set SIZEOF_TIME_T for your platform +#endif value.length = sizeof(buf); value.value = buf; diff --git a/third_party/heimdal/lib/gssapi/krb5/prf.c b/third_party/heimdal/lib/gssapi/krb5/prf.c index 671ab2c6d98..941d21ac600 100644 --- a/third_party/heimdal/lib/gssapi/krb5/prf.c +++ b/third_party/heimdal/lib/gssapi/krb5/prf.c @@ -119,7 +119,7 @@ _gsskrb5_pseudo_random(OM_uint32 *minor_status, while(dol > 0) { size_t tsize; - _gsskrb5_encode_be_om_uint32(num, input.data); + _gss_mg_encode_be_uint32(num, input.data); ret = krb5_crypto_prf(context, crypto, &input, &output); if (ret) { diff --git a/third_party/heimdal/lib/gssapi/krb5/unwrap.c b/third_party/heimdal/lib/gssapi/krb5/unwrap.c index 64613698fa4..1eea68eace2 100644 --- a/third_party/heimdal/lib/gssapi/krb5/unwrap.c +++ b/third_party/heimdal/lib/gssapi/krb5/unwrap.c @@ -163,7 +163,7 @@ unwrap_des memset (&schedule, 0, sizeof(schedule)); seq = p; - _gsskrb5_decode_om_uint32(seq, &seq_number); + _gss_mg_decode_be_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); @@ -335,7 +335,7 @@ unwrap_des3 } seq = seq_data.data; - _gsskrb5_decode_om_uint32(seq, &seq_number); + _gss_mg_decode_be_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); diff --git a/third_party/heimdal/lib/gssapi/krb5/verify_mic.c b/third_party/heimdal/lib/gssapi/krb5/verify_mic.c index 7070f3de46b..4a776c8099b 100644 --- a/third_party/heimdal/lib/gssapi/krb5/verify_mic.c +++ b/third_party/heimdal/lib/gssapi/krb5/verify_mic.c @@ -109,7 +109,7 @@ verify_mic_des memset_s(&schedule, sizeof(schedule), 0, sizeof(schedule)); seq = p; - _gsskrb5_decode_om_uint32(seq, &seq_number); + _gss_mg_decode_be_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); @@ -211,7 +211,7 @@ retry: HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); seq = seq_data.data; - _gsskrb5_decode_om_uint32(seq, &seq_number); + _gss_mg_decode_be_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); diff --git a/third_party/heimdal/lib/gssapi/mech/gss_accept_sec_context.c b/third_party/heimdal/lib/gssapi/mech/gss_accept_sec_context.c index 1cb0b367aad..04d7ab538ac 100644 --- a/third_party/heimdal/lib/gssapi/mech/gss_accept_sec_context.c +++ b/third_party/heimdal/lib/gssapi/mech/gss_accept_sec_context.c @@ -172,7 +172,7 @@ choose_mech(struct _gss_context *ctx) if (len == 0) { /* * There is the a wierd mode of SPNEGO (in CIFS and - * SASL GSS-SPENGO where the first token is zero + * SASL GSS-SPENGO) where the first token is zero * length and the acceptor returns a mech_list, lets * hope that is what is happening now. * @@ -190,13 +190,17 @@ choose_mech(struct _gss_context *ctx) * Decode the OID for the mechanism. Simplify life by * assuming that the OID length is less than 128 bytes. */ - if (len < 2 || *p != 0x06) - goto bail; - if ((p[1] & 0x80) || p[1] > (len - 2)) - goto bail; + if (len < 2 || *p != 0x06) { + _gss_mg_log(10, "initial context token appears to be for non-standard mechanism"); + return GSS_S_COMPLETE; + } + len -= 2; + if ((p[1] & 0x80) || p[1] > len) { + _gss_mg_log(10, "mechanism oid in initial context token is too long"); + return GSS_S_COMPLETE; + } mech.length = p[1]; p += 2; - len -= 2; mech.elements = p; mech_oid = _gss_mg_support_mechanism(&mech); @@ -209,19 +213,13 @@ gss_get_mechanism: * and we have to try all mechs (that we have a cred element * for, if we have a cred). */ - if (mech_oid != GSS_C_NO_OID) { - log_oid("mech oid", mech_oid); - ctx->gc_mech = __gss_get_mechanism(mech_oid); - if (!ctx->gc_mech) { - _gss_mg_log(10, "mechanism client used is unknown"); - return (GSS_S_BAD_MECH); - } - _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name); - return GSS_S_COMPLETE; + log_oid("mech oid", mech_oid); + ctx->gc_mech = __gss_get_mechanism(mech_oid); + if (!ctx->gc_mech) { + _gss_mg_log(10, "mechanism client used is unknown"); + return (GSS_S_BAD_MECH); } - -bail: - _gss_mg_log(10, "no mech oid found"); + _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name); return GSS_S_COMPLETE; } diff --git a/third_party/heimdal/lib/gssapi/mech/gss_krb5.c b/third_party/heimdal/lib/gssapi/mech/gss_krb5.c index 78c305689f0..21bb2bffb00 100644 --- a/third_party/heimdal/lib/gssapi/mech/gss_krb5.c +++ b/third_party/heimdal/lib/gssapi/mech/gss_krb5.c @@ -564,17 +564,19 @@ gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, return GSS_S_FAILURE; } - if (data_set->elements[0].length != 4) { + if (data_set->elements[0].length != SIZEOF_TIME_T) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } - { - unsigned char *buf = data_set->elements[0].value; - *authtime = ((unsigned long)buf[3] <<24) | (buf[2] << 16) | - (buf[1] << 8) | (buf[0] << 0); - } +#if SIZEOF_TIME_T == 8 + _gss_mg_decode_le_uint64(data_set->elements[0].value, (uint64_t *)authtime); +#elif SIZEOF_TIME_T == 4 + _gss_mg_decode_le_uint32(data_set->elements[0].value, (uint32_t *)authtime); +#else +#error set SIZEOF_TIME_T for your platform +#endif gss_release_buffer_set(minor_status, &data_set); @@ -844,10 +846,7 @@ gss_krb5_get_tkt_flags(OM_uint32 *minor_status, return GSS_S_FAILURE; } - { - const u_char *p = data_set->elements[0].value; - *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); - } + _gss_mg_decode_le_uint32(data_set->elements[0].value, tkt_flags); gss_release_buffer_set(minor_status, &data_set); return GSS_S_COMPLETE; diff --git a/third_party/heimdal/lib/gssapi/mech/gss_mo.c b/third_party/heimdal/lib/gssapi/mech/gss_mo.c index b6ae282b13e..08cb43d37d3 100644 --- a/third_party/heimdal/lib/gssapi/mech/gss_mo.c +++ b/third_party/heimdal/lib/gssapi/mech/gss_mo.c @@ -453,7 +453,7 @@ gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, struct _gss_mech_switch *ms; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; - OM_uint32 major; + OM_uint32 major, tmp; major = gss_create_empty_oid_set(minor_status, mechs); if (GSS_ERROR(major)) @@ -464,7 +464,6 @@ gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) { gssapi_mech_interface mi = &ms->gm_mech; struct gss_mech_compat_desc_struct *gmc = mi->gm_compat; - OM_uint32 tmp; if (gmc && gmc->gmc_inquire_attrs_for_mech) { major = gmc->gmc_inquire_attrs_for_mech(minor_status, @@ -493,6 +492,9 @@ gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, break; } + if (major) + gss_release_oid_set(&tmp, mechs); + return major; } diff --git a/third_party/heimdal/lib/gssapi/mech/gss_utils.c b/third_party/heimdal/lib/gssapi/mech/gss_utils.c index bb7e619397f..62fa262af36 100644 --- a/third_party/heimdal/lib/gssapi/mech/gss_utils.c +++ b/third_party/heimdal/lib/gssapi/mech/gss_utils.c @@ -178,6 +178,60 @@ _gss_secure_release_buffer_set(OM_uint32 *minor_status, } void +_gss_mg_encode_le_uint64(uint64_t n, uint8_t *p) +{ + p[0] = (n >> 0 ) & 0xFF; + p[1] = (n >> 8 ) & 0xFF; + p[2] = (n >> 16) & 0xFF; + p[3] = (n >> 24) & 0xFF; + p[4] = (n >> 32) & 0xFF; + p[5] = (n >> 40) & 0xFF; + p[6] = (n >> 48) & 0xFF; + p[7] = (n >> 56) & 0xFF; +} + +void +_gss_mg_decode_le_uint64(const void *ptr, uint64_t *n) +{ + const uint8_t *p = ptr; + *n = ((uint64_t)p[0] << 0) + | ((uint64_t)p[1] << 8) + | ((uint64_t)p[2] << 16) + | ((uint64_t)p[3] << 24) + | ((uint64_t)p[4] << 32) + | ((uint64_t)p[5] << 40) + | ((uint64_t)p[6] << 48) + | ((uint64_t)p[7] << 56); +} + +void +_gss_mg_encode_be_uint64(uint64_t n, uint8_t *p) +{ + p[0] = (n >> 56) & 0xFF; + p[1] = (n >> 48) & 0xFF; + p[2] = (n >> 40) & 0xFF; + p[3] = (n >> 32) & 0xFF; + p[4] = (n >> 24) & 0xFF; + p[5] = (n >> 16) & 0xFF; + p[6] = (n >> 8 ) & 0xFF; + p[7] = (n >> 0 ) & 0xFF; +} + +void +_gss_mg_decode_be_uint64(const void *ptr, uint64_t *n) +{ + const uint8_t *p = ptr; + *n = ((uint64_t)p[0] << 56) + | ((uint64_t)p[1] << 48) + | ((uint64_t)p[2] << 40) + | ((uint64_t)p[3] << 32) + | ((uint64_t)p[4] << 24) + | ((uint64_t)p[5] << 16) + | ((uint64_t)p[6] << 8) + | ((uint64_t)p[7] << 0); +} + +void _gss_mg_encode_le_uint32(uint32_t n, uint8_t *p) { p[0] = (n >> 0 ) & 0xFF; @@ -190,7 +244,10 @@ void _gss_mg_decode_le_uint32(const void *ptr, uint32_t *n) { const uint8_t *p = ptr; - *n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + *n = ((uint32_t)p[0] << 0) + | ((uint32_t)p[1] << 8) + | ((uint32_t)p[2] << 16) + | ((uint32_t)p[3] << 24); } void diff --git a/third_party/heimdal/lib/gssapi/mech/mech_locl.h b/third_party/heimdal/lib/gssapi/mech/mech_locl.h index d451b87c4a7..f0cfcb5e638 100644 --- a/third_party/heimdal/lib/gssapi/mech/mech_locl.h +++ b/third_party/heimdal/lib/gssapi/mech/mech_locl.h @@ -42,6 +42,7 @@ #include <ctype.h> #include <heimbase.h> +#include "heimbase-atomics.h" #include <gssapi_asn1.h> #include <der.h> diff --git a/third_party/heimdal/lib/gssapi/mech/utils.h b/third_party/heimdal/lib/gssapi/mech/utils.h index 208a59cc85c..717ca49166a 100644 --- a/third_party/heimdal/lib/gssapi/mech/utils.h +++ b/third_party/heimdal/lib/gssapi/mech/utils.h @@ -36,6 +36,11 @@ OM_uint32 _gss_secure_release_buffer(OM_uint32 *minor_status, OM_uint32 _gss_secure_release_buffer_set(OM_uint32 *minor_status, gss_buffer_set_t *buffer_set); +void _gss_mg_encode_le_uint64(uint64_t n, uint8_t *p); +void _gss_mg_decode_le_uint64(const void *ptr, uint64_t *n); +void _gss_mg_encode_be_uint64(uint64_t n, uint8_t *p); +void _gss_mg_decode_be_uint64(const void *ptr, uint64_t *n); + void _gss_mg_encode_le_uint32(uint32_t n, uint8_t *p); void _gss_mg_decode_le_uint32(const void *ptr, uint32_t *n); void _gss_mg_encode_be_uint32(uint32_t n, uint8_t *p); diff --git a/third_party/heimdal/lib/gssapi/ntlm/crypto.c b/third_party/heimdal/lib/gssapi/ntlm/crypto.c index d1a115ff8e3..d2cfddf6d54 100644 --- a/third_party/heimdal/lib/gssapi/ntlm/crypto.c +++ b/third_party/heimdal/lib/gssapi/ntlm/crypto.c @@ -48,27 +48,6 @@ struct _krb5_key_type; * */ -static void -encode_le_uint32(uint32_t n, unsigned char *p) -{ - p[0] = (n >> 0) & 0xFF; - p[1] = (n >> 8) & 0xFF; - p[2] = (n >> 16) & 0xFF; - p[3] = (n >> 24) & 0xFF; -} - - -static void -decode_le_uint32(const void *ptr, uint32_t *n) -{ - const unsigned char *p = ptr; - *n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); -} - -/* - * - */ - const char a2i_signmagic[] = "session key to server-to-client signing key magic constant"; const char a2i_sealmagic[] = @@ -168,11 +147,11 @@ v1_sign_message(gss_buffer_t in, _krb5_crc_init_table(); crc = _krb5_crc_update(in->value, in->length, 0); - encode_le_uint32(0, &sigature[0]); - encode_le_uint32(crc, &sigature[4]); - encode_le_uint32(seq, &sigature[8]); + _gss_mg_encode_le_uint32(0, &sigature[0]); + _gss_mg_encode_le_uint32(crc, &sigature[4]); + _gss_mg_encode_le_uint32(seq, &sigature[8]); - encode_le_uint32(1, out); /* version */ + _gss_mg_encode_le_uint32(1, out); /* version */ RC4(signkey, sizeof(sigature), sigature, out + 4); if (RAND_bytes(out + 4, 4) != 1) @@ -199,13 +178,13 @@ v2_sign_message(gss_buffer_t in, return GSS_S_FAILURE; } - encode_le_uint32(seq, hmac); + _gss_mg_encode_le_uint32(seq, hmac); HMAC_Update(&c, hmac, 4); HMAC_Update(&c, in->value, in->length); HMAC_Final(&c, hmac, &hmaclen); HMAC_CTX_cleanup(&c); - encode_le_uint32(1, &out[0]); + _gss_mg_encode_le_uint32(1, &out[0]); if (sealkey) RC4(sealkey, 8, hmac, &out[4]); else @@ -365,10 +344,10 @@ _gss_ntlm_get_mic sigature = message_token->value; - encode_le_uint32(1, &sigature[0]); /* version */ - encode_le_uint32(0, &sigature[4]); - encode_le_uint32(0, &sigature[8]); - encode_le_uint32(0, &sigature[12]); + _gss_mg_encode_le_uint32(1, &sigature[0]); /* version */ + _gss_mg_encode_le_uint32(0, &sigature[4]); + _gss_mg_encode_le_uint32(0, &sigature[8]); + _gss_mg_encode_le_uint32(0, &sigature[12]); return GSS_S_COMPLETE; } @@ -422,7 +401,7 @@ _gss_ntlm_verify_mic if ((ctx->status & STATUS_SESSIONKEY) == 0) return GSS_S_UNAVAILABLE; - decode_le_uint32(token_buffer->value, &num); + _gss_mg_decode_le_uint32(token_buffer->value, &num); if (num != 1) return GSS_S_BAD_MIC; @@ -433,10 +412,10 @@ _gss_ntlm_verify_mic crc = _krb5_crc_update(message_buffer->value, message_buffer->length, 0); /* skip first 4 bytes in the encrypted checksum */ - decode_le_uint32(&sigature[4], &num); + _gss_mg_decode_le_uint32(&sigature[4], &num); if (num != crc) return GSS_S_BAD_MIC; - decode_le_uint32(&sigature[8], &num); + _gss_mg_decode_le_uint32(&sigature[8], &num); if (ctx->u.v1.crypto_recv.seq != num) return GSS_S_BAD_MIC; ctx->u.v1.crypto_recv.seq++; @@ -448,13 +427,13 @@ _gss_ntlm_verify_mic p = (unsigned char*)(token_buffer->value); - decode_le_uint32(&p[0], &num); /* version */ + _gss_mg_decode_le_uint32(&p[0], &num); /* version */ if (num != 1) return GSS_S_BAD_MIC; - decode_le_uint32(&p[4], &num); + _gss_mg_decode_le_uint32(&p[4], &num); if (num != 0) return GSS_S_BAD_MIC; - decode_le_uint32(&p[8], &num); + _gss_mg_decode_le_uint32(&p[8], &num); if (num != 0) return GSS_S_BAD_MIC; - decode_le_uint32(&p[12], &num); + _gss_mg_decode_le_uint32(&p[12], &num); if (num != 0) return GSS_S_BAD_MIC; return GSS_S_COMPLETE; diff --git a/third_party/heimdal/lib/gssapi/ntlm/kdc.c b/third_party/heimdal/lib/gssapi/ntlm/kdc.c index 1bce00fc5d6..10aa2b9803a 100644 --- a/third_party/heimdal/lib/gssapi/ntlm/kdc.c +++ b/third_party/heimdal/lib/gssapi/ntlm/kdc.c @@ -82,7 +82,7 @@ get_ccache(krb5_context context, int *destroy, krb5_ccache *id) ret = krb5_cc_cache_match(context, principal, id); if (ret == 0) - return 0; + goto out; /* did not find in default credcache, lets try default keytab */ ret = krb5_kt_default(context, &kt); diff --git a/third_party/heimdal/lib/gssapi/ntlm/ntlm.h b/third_party/heimdal/lib/gssapi/ntlm/ntlm.h index a0ad8158418..7b16aa21d91 100644 --- a/third_party/heimdal/lib/gssapi/ntlm/ntlm.h +++ b/third_party/heimdal/lib/gssapi/ntlm/ntlm.h @@ -50,6 +50,7 @@ #include <gssapi_ntlm.h> #include <gssapi_mech.h> #include <gssapi_oid.h> +#include <mech/utils.h> #include <krb5.h> #include <kcm.h> diff --git a/third_party/heimdal/lib/gssapi/sanon/export_cred.c b/third_party/heimdal/lib/gssapi/sanon/export_cred.c index 06c2458f722..359eefd372f 100644 --- a/third_party/heimdal/lib/gssapi/sanon/export_cred.c +++ b/third_party/heimdal/lib/gssapi/sanon/export_cred.c @@ -36,5 +36,43 @@ _gss_sanon_export_cred(OM_uint32 *minor, gss_cred_id_t input_cred, gss_buffer_t token) { - return _gss_sanon_export_name(minor, (gss_name_t)input_cred, token); + gss_buffer_desc buf; + krb5_storage *sp; + krb5_data data_out, data; + OM_uint32 major, junk; + + token->value = NULL; + token->length = 0; + + major = _gss_sanon_export_name(minor, (gss_name_t)input_cred, &buf); + if (major) + return major; + + sp = krb5_storage_emem(); + if (sp == NULL) { + gss_release_buffer(&junk, &buf); + *minor = ENOMEM; + return GSS_S_FAILURE; + } + + major = _gss_mg_store_oid(minor, sp, GSS_SANON_X25519_MECHANISM); + if (major) { + gss_release_buffer(&junk, &buf); + krb5_storage_free(sp); + return major; + } + data_out.length = 0; + data_out.data = NULL; + data.length = buf.length; + data.data = buf.value; + *minor = krb5_store_data(sp, data); + if (*minor == 0) + *minor = krb5_storage_to_data(sp, &data_out); + if (*minor == 0) { + token->value = data_out.data; + token->length = data_out.length; + } + gss_release_buffer(&junk, &buf); + krb5_storage_free(sp); + return major; } diff --git a/third_party/heimdal/lib/gssapi/spnego/context_storage.c b/third_party/heimdal/lib/gssapi/spnego/context_storage.c index 13e20d723e6..3924bd38395 100644 --- a/third_party/heimdal/lib/gssapi/spnego/context_storage.c +++ b/third_party/heimdal/lib/gssapi/spnego/context_storage.c @@ -55,6 +55,10 @@ ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp); static krb5_error_code store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech); +#ifdef sc_flags +#undef sc_flags +#endif + static uint16_t spnego_flags_to_int(struct spnego_flags flags); static struct spnego_flags @@ -207,7 +211,9 @@ ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp) struct negoex_auth_mech *mech; CHECK(ret, ret_negoex_auth_mech(sp, &mech)); - HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links); + /* `mech' will not be NULL here, but quiet scan-build */ + if (mech) + HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links); } } @@ -374,15 +380,15 @@ ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp) if (snc_flags & SNC_METADATA) CHECK(major, _gss_mg_ret_buffer(&minor, sp, &mech->metadata)); - *mechp = mech; - fail: if (ret == 0 && GSS_ERROR(major)) ret = minor ? minor : KRB5_BAD_MSIZE; if (ret) _gss_negoex_release_auth_mech(context, mech); - gss_release_buffer(&minor, &buf); + else + *mechp = mech; + gss_release_buffer(&minor, &buf); return ret; } diff --git a/third_party/heimdal/lib/gssapi/spnego/negoex_util.c b/third_party/heimdal/lib/gssapi/spnego/negoex_util.c index aac09d4483b..e6c9a139248 100644 --- a/third_party/heimdal/lib/gssapi/spnego/negoex_util.c +++ b/third_party/heimdal/lib/gssapi/spnego/negoex_util.c @@ -87,10 +87,15 @@ static void release_all_mechs(gssspnego_ctx ctx, krb5_context context) { struct negoex_auth_mech *mech, *next; + struct negoex_auth_mech *prev = NULL; HEIM_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next) { - _gss_negoex_release_auth_mech(context, mech); + if (prev) + _gss_negoex_release_auth_mech(context, prev); + prev = mech; } + if (prev) + _gss_negoex_release_auth_mech(context, mech); HEIM_TAILQ_INIT(&ctx->negoex_mechs); } @@ -349,7 +354,7 @@ storage_from_memory(OM_uint32 *minor, krb5_storage **sp) { *sp = krb5_storage_from_readonly_mem(data, length); - if (sp == NULL) { + if (*sp == NULL) { *minor = ENOMEM; return GSS_S_FAILURE; } diff --git a/third_party/heimdal/lib/gssapi/test_context.c b/third_party/heimdal/lib/gssapi/test_context.c index 7446d15e0df..907e9c4d208 100644 --- a/third_party/heimdal/lib/gssapi/test_context.c +++ b/third_party/heimdal/lib/gssapi/test_context.c @@ -734,17 +734,29 @@ wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid) token.data = emalloc(token.length); p = token.data; - memcpy(p, iov[0].buffer.value, iov[0].buffer.length); + + if (iov[0].buffer.length) + memcpy(p, iov[0].buffer.value, iov[0].buffer.length); p += iov[0].buffer.length; - memcpy(p, iov[1].buffer.value, iov[1].buffer.length); + + if (iov[1].buffer.length) + memcpy(p, iov[1].buffer.value, iov[1].buffer.length); p += iov[1].buffer.length; - memcpy(p, iov[2].buffer.value, iov[2].buffer.length); + + if (iov[2].buffer.length) + memcpy(p, iov[2].buffer.value, iov[2].buffer.length); p += iov[2].buffer.length; - memcpy(p, iov[3].buffer.value, iov[3].buffer.length); + + if (iov[3].buffer.length) + memcpy(p, iov[3].buffer.value, iov[3].buffer.length); p += iov[3].buffer.length; - memcpy(p, iov[4].buffer.value, iov[4].buffer.length); + + if (iov[4].buffer.length) + memcpy(p, iov[4].buffer.value, iov[4].buffer.length); p += iov[4].buffer.length; - memcpy(p, iov[5].buffer.value, iov[5].buffer.length); + + if (iov[5].buffer.length) + memcpy(p, iov[5].buffer.value, iov[5].buffer.length); p += iov[5].buffer.length; assert(p - ((unsigned char *)token.data) == token.length); @@ -1336,7 +1348,7 @@ main(int argc, char **argv) if (out1.length != out2.length) errx(1, "prf len mismatch"); - if (memcmp(out1.value, out2.value, out1.length) != 0) + if (out1.length && memcmp(out1.value, out2.value, out1.length) != 0) errx(1, "prf data mismatch"); gss_release_buffer(&min_stat, &out1); @@ -1346,7 +1358,7 @@ main(int argc, char **argv) if (out1.length != out2.length) errx(1, "prf len mismatch"); - if (memcmp(out1.value, out2.value, out1.length) != 0) + if (out1.length && memcmp(out1.value, out2.value, out1.length) != 0) errx(1, "prf data mismatch"); gss_release_buffer(&min_stat, &out1); diff --git a/third_party/heimdal/lib/hcrypto/Makefile.am b/third_party/heimdal/lib/hcrypto/Makefile.am index bb36f745190..878525c17d0 100644 --- a/third_party/heimdal/lib/hcrypto/Makefile.am +++ b/third_party/heimdal/lib/hcrypto/Makefile.am @@ -21,7 +21,7 @@ WFLAGS += $(WFLAGS_UNUSED_BUT_SET_VAR) lib_LTLIBRARIES = libhcrypto.la check_LTLIBRARIES = libhctest.la -libhcrypto_la_LDFLAGS = -version-info 5:0:1 +libhcrypto_la_LDFLAGS = -version-info 5:0:0 libhcrypto_la_LIBADD = \ $(top_builddir)/lib/asn1/libasn1.la \ $(LIB_dlopen) \ @@ -51,7 +51,6 @@ hcryptoinclude_HEADERS = \ evp-openssl.h \ evp-pkcs11.h \ hmac.h \ - md2.h \ md4.h \ md5.h \ pkcs12.h \ @@ -160,8 +159,6 @@ libhcrypto_la_SOURCES = \ hash.h \ hmac.c \ hmac.h \ - md2.c \ - md2.h \ md4.c \ md4.h \ md5.c \ diff --git a/third_party/heimdal/lib/hcrypto/NTMakefile b/third_party/heimdal/lib/hcrypto/NTMakefile index ec945009ba6..e3e50a7d5ba 100644 --- a/third_party/heimdal/lib/hcrypto/NTMakefile +++ b/third_party/heimdal/lib/hcrypto/NTMakefile @@ -76,7 +76,6 @@ INCFILES= \ $(HCRYPTOINCLUDEDIR)\evp-wincng.h \ $(HCRYPTOINCLUDEDIR)\evp-w32.h \ $(HCRYPTOINCLUDEDIR)\hmac.h \ - $(HCRYPTOINCLUDEDIR)\md2.h \ $(HCRYPTOINCLUDEDIR)\md4.h \ $(HCRYPTOINCLUDEDIR)\md5.h \ $(HCRYPTOINCLUDEDIR)\pkcs12.h \ @@ -124,7 +123,6 @@ libhcrypto_OBJs = \ $(OBJ)\evp-w32.obj \ $(OBJ)\engine.obj \ $(OBJ)\hmac.obj \ - $(OBJ)\md2.obj \ $(OBJ)\md4.obj \ $(OBJ)\md5.obj \ $(OBJ)\pkcs5.obj \ diff --git a/third_party/heimdal/lib/hcrypto/des.c b/third_party/heimdal/lib/hcrypto/des.c index 32f87b47137..cb9cac74b6c 100644 --- a/third_party/heimdal/lib/hcrypto/des.c +++ b/third_party/heimdal/lib/hcrypto/des.c @@ -344,14 +344,14 @@ load(const unsigned char *b, uint32_t v[2]) static void store(const uint32_t v[2], unsigned char *b) { - b[0] = (v[0] >> 24) & 0xff; - b[1] = (v[0] >> 16) & 0xff; - b[2] = (v[0] >> 8) & 0xff; - b[3] = (v[0] >> 0) & 0xff; - b[4] = (v[1] >> 24) & 0xff; - b[5] = (v[1] >> 16) & 0xff; - b[6] = (v[1] >> 8) & 0xff; - b[7] = (v[1] >> 0) & 0xff; + b[0] = (v[0] >> 24) & 0xffU; + b[1] = (v[0] >> 16) & 0xffU; + b[2] = (v[0] >> 8) & 0xffU; + b[3] = (v[0] >> 0) & 0xffU; + b[4] = (v[1] >> 24) & 0xffU; + b[5] = (v[1] >> 16) & 0xffU; + b[6] = (v[1] >> 8) & 0xffU; + b[7] = (v[1] >> 0) & 0xffU; } /** diff --git a/third_party/heimdal/lib/hcrypto/engine.h b/third_party/heimdal/lib/hcrypto/engine.h index e6fc5b92e41..27c6ab6affa 100644 --- a/third_party/heimdal/lib/hcrypto/engine.h +++ b/third_party/heimdal/lib/hcrypto/engine.h @@ -70,7 +70,7 @@ typedef struct hc_engine ENGINE; -#define NID_md2 0 +/*#define NID_md2 0 */ #define NID_md4 1 #define NID_md5 2 #define NID_sha1 4 diff --git a/third_party/heimdal/lib/hcrypto/evp-cc.c b/third_party/heimdal/lib/hcrypto/evp-cc.c index 4a377f1c3bb..7798519da18 100644 --- a/third_party/heimdal/lib/hcrypto/evp-cc.c +++ b/third_party/heimdal/lib/hcrypto/evp-cc.c @@ -605,32 +605,6 @@ EVP_cc_rc2_64_cbc(void) #endif } -/** - * The CommonCrypto md2 provider - * - * @ingroup hcrypto_evp - */ - -const EVP_MD * -EVP_cc_md2(void) -{ -#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H - static const struct hc_evp_md md2 = { - CC_MD2_DIGEST_LENGTH, - CC_MD2_BLOCK_BYTES, - sizeof(CC_MD2_CTX), - (hc_evp_md_init)CC_MD2_Init, - (hc_evp_md_update)CC_MD2_Update, - (hc_evp_md_final)CC_MD2_Final, - (hc_evp_md_cleanup)NULL - }; - return &md2; -#elif HCRYPTO_FALLBACK - return EVP_hcrypto_md2(); -#else - return NULL; -#endif -} /** * The CommonCrypto md4 provider diff --git a/third_party/heimdal/lib/hcrypto/evp-cc.h b/third_party/heimdal/lib/hcrypto/evp-cc.h index 4d131de01eb..f8576459a8f 100644 --- a/third_party/heimdal/lib/hcrypto/evp-cc.h +++ b/third_party/heimdal/lib/hcrypto/evp-cc.h @@ -37,7 +37,6 @@ #define HEIM_EVP_CC_H 1 /* symbol renaming */ -#define EVP_cc_md2 hc_EVP_cc_md2 #define EVP_cc_md4 hc_EVP_cc_md4 #define EVP_cc_md5 hc_EVP_cc_md5 #define EVP_cc_sha1 hc_EVP_cc_sha1 @@ -67,7 +66,6 @@ HC_CPP_BEGIN -const EVP_MD * EVP_cc_md2(void); const EVP_MD * EVP_cc_md4(void); const EVP_MD * EVP_cc_md5(void); const EVP_MD * EVP_cc_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp-hcrypto.c b/third_party/heimdal/lib/hcrypto/evp-hcrypto.c index 321efb08c52..ee43e105a32 100644 --- a/third_party/heimdal/lib/hcrypto/evp-hcrypto.c +++ b/third_party/heimdal/lib/hcrypto/evp-hcrypto.c @@ -51,7 +51,6 @@ #include <rc4.h> #include <sha.h> -#include <md2.h> #include <md4.h> #include <md5.h> @@ -401,28 +400,6 @@ EVP_hcrypto_md4(void) return &md4; } -/** - * The message digest MD2 - hcrypto - * - * @return the message digest type. - * - * @ingroup hcrypto_evp - */ - -const EVP_MD * -EVP_hcrypto_md2(void) -{ - static const struct hc_evp_md md2 = { - 16, - 16, - sizeof(MD2_CTX), - (hc_evp_md_init)MD2_Init, - (hc_evp_md_update)MD2_Update, - (hc_evp_md_final)MD2_Final, - NULL - }; - return &md2; -} /* * diff --git a/third_party/heimdal/lib/hcrypto/evp-hcrypto.h b/third_party/heimdal/lib/hcrypto/evp-hcrypto.h index b7876c67c8a..6d4f1c872a2 100644 --- a/third_party/heimdal/lib/hcrypto/evp-hcrypto.h +++ b/third_party/heimdal/lib/hcrypto/evp-hcrypto.h @@ -37,7 +37,6 @@ #define HEIM_EVP_HCRYPTO_H 1 /* symbol renaming */ -#define EVP_hcrypto_md2 hc_EVP_hcrypto_md2 #define EVP_hcrypto_md4 hc_EVP_hcrypto_md4 #define EVP_hcrypto_md5 hc_EVP_hcrypto_md5 #define EVP_hcrypto_sha1 hc_EVP_hcrypto_sha1 @@ -67,7 +66,6 @@ HC_CPP_BEGIN -const EVP_MD * EVP_hcrypto_md2(void); const EVP_MD * EVP_hcrypto_md4(void); const EVP_MD * EVP_hcrypto_md5(void); const EVP_MD * EVP_hcrypto_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp-openssl.c b/third_party/heimdal/lib/hcrypto/evp-openssl.c index ca02862bf68..f6cf687b98b 100644 --- a/third_party/heimdal/lib/hcrypto/evp-openssl.c +++ b/third_party/heimdal/lib/hcrypto/evp-openssl.c @@ -80,6 +80,24 @@ #define EVP_MD_CTX_free EVP_MD_CTX_destroy #endif +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) +int _heim_openssl_fips_enabled(void); +int +_heim_openssl_fips_enabled(void) +{ + static int fips_enabled_res = -1; + + if (fips_enabled_res != -1) + return fips_enabled_res; + +#ifdef HAVE_OPENSSL_30 + return fips_enabled_res = !!EVP_default_properties_is_fips_enabled(NULL); +#else + return fips_enabled_res = !!FIPS_mode(); +#endif +} +#endif + /* A HEIM_BASE_ONCE argument struct for per-EVP one-time initialization */ struct once_init_cipher_ctx { const hc_EVP_CIPHER **hc_memoizep; @@ -158,7 +176,7 @@ cipher_do_cipher(hc_EVP_CIPHER_CTX *ctx, unsigned char *out, struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data; assert(ossl_ctx != NULL); - return EVP_Cipher(ossl_ctx->ossl_cipher_ctx, out, in, len); + return EVP_Cipher(ossl_ctx->ossl_cipher_ctx, out, in, len) == 0 ? 0 : 1; } static int @@ -438,7 +456,9 @@ OSSL_CIPHER_ALGORITHM(des_ede3_cbc, hc_EVP_CIPH_CBC_MODE) * * @ingroup hcrypto_evp */ +#ifndef HAVE_OPENSSL_30 OSSL_CIPHER_ALGORITHM(des_cbc, hc_EVP_CIPH_CBC_MODE) +#endif /** * The AES-128 cipher type (OpenSSL provider) @@ -494,6 +514,7 @@ OSSL_CIPHER_ALGORITHM(aes_192_cfb8, hc_EVP_CIPH_CFB8_MODE) */ OSSL_CIPHER_ALGORITHM(aes_256_cfb8, hc_EVP_CIPH_CFB8_MODE) +#ifndef HAVE_OPENSSL_30 /* * RC2 is only needed for tests of PKCS#12 support, which currently uses * the RC2 PBE. So no RC2 -> tests fail. @@ -530,6 +551,7 @@ OSSL_CIPHER_ALGORITHM(rc2_40_cbc, OSSL_CIPHER_ALGORITHM(rc2_64_cbc, hc_EVP_CIPH_CBC_MODE | hc_EVP_CIPH_VARIABLE_LENGTH) +#endif /** * The Camellia-128 cipher type - OpenSSL @@ -558,6 +580,7 @@ OSSL_CIPHER_ALGORITHM(camellia_192_cbc, hc_EVP_CIPH_CBC_MODE) */ OSSL_CIPHER_ALGORITHM(camellia_256_cbc, hc_EVP_CIPH_CBC_MODE) +#ifndef HAVE_OPENSSL_30 /** * The RC4 cipher type (OpenSSL provider) * @@ -581,15 +604,6 @@ OSSL_CIPHER_ALGORITHM(rc4_40, hc_EVP_CIPH_VARIABLE_LENGTH) /** - * The MD2 hash algorithm (OpenSSL provider) - * - * @return the MD2 EVP_MD pointer. - * - * @ingroup hcrypto_evp - */ -OSSL_MD_ALGORITHM(md2) - -/** * The MD4 hash algorithm (OpenSSL provider) * * @return the MD4 EVP_MD pointer. @@ -597,6 +611,7 @@ OSSL_MD_ALGORITHM(md2) * @ingroup hcrypto_evp */ OSSL_MD_ALGORITHM(md4) +#endif /** * The MD5 hash algorithm (OpenSSL provider) diff --git a/third_party/heimdal/lib/hcrypto/evp-openssl.h b/third_party/heimdal/lib/hcrypto/evp-openssl.h index 225ff454a87..ff285adeee3 100644 --- a/third_party/heimdal/lib/hcrypto/evp-openssl.h +++ b/third_party/heimdal/lib/hcrypto/evp-openssl.h @@ -37,7 +37,6 @@ #define HEIM_EVP_OSSL_H 1 /* symbol renaming */ -#define EVP_ossl_md2 hc_EVP_ossl_md2 #define EVP_ossl_md4 hc_EVP_ossl_md4 #define EVP_ossl_md5 hc_EVP_ossl_md5 #define EVP_ossl_sha1 hc_EVP_ossl_sha1 @@ -67,7 +66,6 @@ HC_CPP_BEGIN -const hc_EVP_MD * hc_EVP_ossl_md2(void); const hc_EVP_MD * hc_EVP_ossl_md4(void); const hc_EVP_MD * hc_EVP_ossl_md5(void); const hc_EVP_MD * hc_EVP_ossl_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp-pkcs11.c b/third_party/heimdal/lib/hcrypto/evp-pkcs11.c index b44871fa4c4..90e2b91be4b 100644 --- a/third_party/heimdal/lib/hcrypto/evp-pkcs11.c +++ b/third_party/heimdal/lib/hcrypto/evp-pkcs11.c @@ -819,7 +819,6 @@ PKCS11_CIPHER_ALGORITHM(rc4_40, 0, EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH) -PKCS11_MD_ALGORITHM(md2, CKM_MD2, 16, 16) #ifdef CKM_MD4 /* non-standard extension */ PKCS11_MD_ALGORITHM(md4, CKM_MD4, 16, 64) #else diff --git a/third_party/heimdal/lib/hcrypto/evp-pkcs11.h b/third_party/heimdal/lib/hcrypto/evp-pkcs11.h index 6d8a21f528b..49d020a2f2c 100644 --- a/third_party/heimdal/lib/hcrypto/evp-pkcs11.h +++ b/third_party/heimdal/lib/hcrypto/evp-pkcs11.h @@ -35,7 +35,6 @@ /* symbol renaming */ -#define EVP_pkcs11_md2() hc_EVP_pkcs11_md2() #define EVP_pkcs11_md4() hc_EVP_pkcs11_md4() #define EVP_pkcs11_md5() hc_EVP_pkcs11_md5() #define EVP_pkcs11_sha1() hc_EVP_pkcs11_sha1() @@ -59,7 +58,6 @@ #define EVP_pkcs11_camellia_192_cbc() hc_EVP_pkcs11_camellia_192_cbc() #define EVP_pkcs11_camellia_256_cbc() hc_EVP_pkcs11_camellia_256_cbc() -#define EVP_pkcs11_hcrypto_md2() hc_EVP_pkcs11_hcrypto_md2() #define EVP_pkcs11_hcrypto_md4() hc_EVP_pkcs11_hcrypto_md4() #define EVP_pkcs11_hcrypto_md5() hc_EVP_pkcs11_hcrypto_md5() #define EVP_pkcs11_hcrypto_sha1() hc_EVP_pkcs11_hcrypto_sha1() @@ -89,7 +87,6 @@ HC_CPP_BEGIN * Strict PKCS#11 implementations (these will return NULL if the underlying * PKCS#11 implementation does not implement the cipher or hash). */ -const EVP_MD * hc_EVP_pkcs11_md2(void); const EVP_MD * hc_EVP_pkcs11_md4(void); const EVP_MD * hc_EVP_pkcs11_md5(void); const EVP_MD * hc_EVP_pkcs11_sha1(void); @@ -122,7 +119,6 @@ const EVP_CIPHER * hc_EVP_pkcs11_camellia_256_cbc(void); /* * PKCS#11 implementations with fallback to hcrypto. */ -const EVP_MD * hc_EVP_pkcs11_hcrypto_md2(void); const EVP_MD * hc_EVP_pkcs11_hcrypto_md4(void); const EVP_MD * hc_EVP_pkcs11_hcrypto_md5(void); const EVP_MD * hc_EVP_pkcs11_hcrypto_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp-w32.c b/third_party/heimdal/lib/hcrypto/evp-w32.c index 7d14d1f4f30..ac2c6a53e4b 100644 --- a/third_party/heimdal/lib/hcrypto/evp-w32.c +++ b/third_party/heimdal/lib/hcrypto/evp-w32.c @@ -109,7 +109,6 @@ _hc_w32crypto_DllMain(HINSTANCE hinstDLL, return hc_EVP_hcrypto_ ##name (); \ } -EVP_W32CRYPTO_PROVIDER(EVP_MD, md2) EVP_W32CRYPTO_PROVIDER(EVP_MD, md4) EVP_W32CRYPTO_PROVIDER(EVP_MD, md5) EVP_W32CRYPTO_PROVIDER(EVP_MD, sha1) diff --git a/third_party/heimdal/lib/hcrypto/evp-w32.h b/third_party/heimdal/lib/hcrypto/evp-w32.h index 89bfa4d0af3..6d3ba4596d3 100644 --- a/third_party/heimdal/lib/hcrypto/evp-w32.h +++ b/third_party/heimdal/lib/hcrypto/evp-w32.h @@ -34,7 +34,6 @@ #define HEIM_EVP_W32_H 1 /* symbol renaming */ -#define EVP_w32crypto_md2() hc_EVP_w32crypto_md2() #define EVP_w32crypto_md4() hc_EVP_w32crypto_md4() #define EVP_w32crypto_md5() hc_EVP_w32crypto_md5() #define EVP_w32crypto_sha1() hc_EVP_w32crypto_sha1() @@ -65,7 +64,6 @@ HC_CPP_BEGIN -const EVP_MD * hc_EVP_w32crypto_md2(void); const EVP_MD * hc_EVP_w32crypto_md4(void); const EVP_MD * hc_EVP_w32crypto_md5(void); const EVP_MD * hc_EVP_w32crypto_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp-wincng.c b/third_party/heimdal/lib/hcrypto/evp-wincng.c index 92974a91bb6..34ac3b32459 100644 --- a/third_party/heimdal/lib/hcrypto/evp-wincng.c +++ b/third_party/heimdal/lib/hcrypto/evp-wincng.c @@ -700,7 +700,6 @@ wincng_md_cleanup(EVP_MD_CTX *ctx) } \ } while (0) -WINCNG_MD_ALGORITHM(md2, BCRYPT_MD2_ALGORITHM); WINCNG_MD_ALGORITHM(md4, BCRYPT_MD4_ALGORITHM); WINCNG_MD_ALGORITHM(md5, BCRYPT_MD5_ALGORITHM); WINCNG_MD_ALGORITHM(sha1, BCRYPT_SHA1_ALGORITHM); @@ -711,7 +710,6 @@ WINCNG_MD_ALGORITHM(sha512, BCRYPT_SHA512_ALGORITHM); static void wincng_md_algorithm_cleanup(void) { - WINCNG_MD_ALGORITHM_CLEANUP(md2); WINCNG_MD_ALGORITHM_CLEANUP(md4); WINCNG_MD_ALGORITHM_CLEANUP(md5); WINCNG_MD_ALGORITHM_CLEANUP(sha1); diff --git a/third_party/heimdal/lib/hcrypto/evp-wincng.h b/third_party/heimdal/lib/hcrypto/evp-wincng.h index ed7037a1145..88d98303e84 100644 --- a/third_party/heimdal/lib/hcrypto/evp-wincng.h +++ b/third_party/heimdal/lib/hcrypto/evp-wincng.h @@ -34,7 +34,6 @@ #define HEIM_EVP_WINCNG_H 1 /* symbol renaming */ -#define EVP_wincng_md2() EVP_wincng_md2() #define EVP_wincng_md4() EVP_wincng_md4() #define EVP_wincng_md5() EVP_wincng_md5() #define EVP_wincng_sha1() EVP_wincng_sha1() @@ -64,7 +63,6 @@ HC_CPP_BEGIN -const EVP_MD * hc_EVP_wincng_md2(void); const EVP_MD * hc_EVP_wincng_md4(void); const EVP_MD * hc_EVP_wincng_md5(void); const EVP_MD * hc_EVP_wincng_sha1(void); diff --git a/third_party/heimdal/lib/hcrypto/evp.c b/third_party/heimdal/lib/hcrypto/evp.c index 320e85283f7..3874179c029 100644 --- a/third_party/heimdal/lib/hcrypto/evp.c +++ b/third_party/heimdal/lib/hcrypto/evp.c @@ -59,6 +59,7 @@ # define HCRYPTO_DEF_PROVIDER pkcs11_hcrypto # elif HAVE_HCRYPTO_W_OPENSSL # define HCRYPTO_DEF_PROVIDER ossl +# define HCRYPTO_DEF_PROVIDER_IS_OPENSSL # else # define HCRYPTO_DEF_PROVIDER hcrypto # endif @@ -69,6 +70,11 @@ #define EVP_DEF_OP(_prov,_op) HC_CONCAT4(EVP_,_prov,_,_op)() +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) +extern int _heim_openssl_fips_enabled(void); +#endif + + /** * @page page_evp EVP - generic crypto interface * @@ -463,24 +469,16 @@ const EVP_MD * EVP_md4(void) HC_DEPRECATED_CRYPTO { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, md4); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, md4); } -/** - * The message digest MD2 - * - * @return the message digest type. - * - * @ingroup hcrypto_evp - */ - -const EVP_MD * -EVP_md2(void) HC_DEPRECATED_CRYPTO -{ - hcrypto_validate(); - return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, md2); -} - /* * */ @@ -872,37 +870,34 @@ EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen, *outlen = 0; - /** - * If there in no spare bytes in the left from last Update and the - * input length is on the block boundery, the EVP_CipherUpdate() - * function can take a shortcut (and preformance gain) and - * directly encrypt the data, otherwise we hav to fix it up and - * store extra it the EVP_CIPHER_CTX. + /* + * If there in no bytes left over from the last Update and the + * input length is on a block boundary, then we can take a + * shortcut (and preformance gain) and directly encrypt the + * data. */ - if (ctx->buf_len == 0 && (inlen & ctx->block_mask) == 0) { + if (ctx->buf_len == 0 && inlen && (inlen & ctx->block_mask) == 0) { ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); if (ret == 1) *outlen = inlen; - else + else *outlen = 0; return ret; } - blocksize = EVP_CIPHER_CTX_block_size(ctx); left = blocksize - ctx->buf_len; assert(left > 0); if (ctx->buf_len) { - - /* if total buffer is smaller then input, store locally */ + /* If we can't fill one block in the buffer, save the input there */ if (inlen < left) { memcpy(ctx->buf + ctx->buf_len, in, inlen); ctx->buf_len += inlen; return 1; } - /* fill in local buffer and encrypt */ + /* Fill the buffer and encrypt */ memcpy(ctx->buf + ctx->buf_len, in, left); ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize); memset_s(ctx->buf, blocksize, 0, blocksize); @@ -920,12 +915,16 @@ EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen, ctx->buf_len = (inlen & ctx->block_mask); inlen &= ~ctx->block_mask; - ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); - if (ret != 1) - return ret; + if (inlen) { + /* Encrypt all the whole blocks of input that we have */ + ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen); + if (ret != 1) + return ret; + } *outlen += inlen; + /* Save the tail of the input, if any */ in = ((unsigned char *)in) + inlen; memcpy(ctx->buf, in, ctx->buf_len); } @@ -984,7 +983,7 @@ EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, void *out, int *outlen) * @param in in data to the operation. * @param size length of data. * - * @return 1 on success. + * @return bytes encrypted on success, zero on failure. */ int @@ -1063,6 +1062,13 @@ const EVP_CIPHER * EVP_rc2_cbc(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, rc2_cbc); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_cbc); } @@ -1078,6 +1084,13 @@ const EVP_CIPHER * EVP_rc2_40_cbc(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, rc2_40_cbc); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_40_cbc); } @@ -1093,6 +1106,13 @@ const EVP_CIPHER * EVP_rc2_64_cbc(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, rc2_64_cbc); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc2_64_cbc); } @@ -1108,6 +1128,13 @@ const EVP_CIPHER * EVP_rc4(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, rc4); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc4); } @@ -1123,6 +1150,13 @@ const EVP_CIPHER * EVP_rc4_40(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, rc4_40); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, rc4_40); } @@ -1138,6 +1172,13 @@ const EVP_CIPHER * EVP_des_cbc(void) { hcrypto_validate(); +#if defined(HCRYPTO_DEF_PROVIDER_IS_OPENSSL) && defined(HAVE_OPENSSL_30) +#if defined(HAVE_OPENSSL_FIPS_H) || defined(HAVE_OPENSSL_FIPS_MODE_SET_API) + if (_heim_openssl_fips_enabled()) + return NULL; +#endif + return EVP_DEF_OP(hcrypto, des_cbc); +#endif return EVP_DEF_OP(HCRYPTO_DEF_PROVIDER, des_cbc); } diff --git a/third_party/heimdal/lib/hcrypto/evp.h b/third_party/heimdal/lib/hcrypto/evp.h index 8893362db23..7019abafd6d 100644 --- a/third_party/heimdal/lib/hcrypto/evp.h +++ b/third_party/heimdal/lib/hcrypto/evp.h @@ -81,7 +81,6 @@ #define EVP_des_cbc hc_EVP_des_cbc #define EVP_des_ede3_cbc hc_EVP_des_ede3_cbc #define EVP_enc_null hc_EVP_enc_null -#define EVP_md2 hc_EVP_md2 #define EVP_md4 hc_EVP_md4 #define EVP_md5 hc_EVP_md5 #define EVP_md_null hc_EVP_md_null @@ -194,7 +193,7 @@ struct hc_CIPHER_CTX { unsigned long flags; void *cipher_data; int final_used; - int block_mask; + unsigned block_mask; unsigned char final[EVP_MAX_BLOCK_LENGTH]; }; @@ -250,7 +249,6 @@ HC_CPP_BEGIN */ const EVP_MD *EVP_md_null(void); -HC_DEPRECATED_CRYPTO const EVP_MD *EVP_md2(void); HC_DEPRECATED_CRYPTO const EVP_MD *EVP_md4(void); HC_DEPRECATED_CRYPTO const EVP_MD *EVP_md5(void); const EVP_MD *EVP_sha(void); diff --git a/third_party/heimdal/lib/hcrypto/hmac.c b/third_party/heimdal/lib/hcrypto/hmac.c index adccee76b26..b646d561221 100644 --- a/third_party/heimdal/lib/hcrypto/hmac.c +++ b/third_party/heimdal/lib/hcrypto/hmac.c @@ -113,9 +113,10 @@ HMAC_Init_ex(HMAC_CTX *ctx, ctx->ipad = malloc(blockSize); if (ctx->ipad) ctx->ctx = EVP_MD_CTX_create(); - if (!ctx->buf || !ctx->opad || !ctx->ipad || !ctx->ctx) - return 0; } + /* We do this check here to quiet scan-build */ + if (!ctx->buf || !ctx->opad || !ctx->ipad || !ctx->ctx) + return 0; #if 0 ctx->engine = engine; #endif diff --git a/third_party/heimdal/lib/hcrypto/libhcrypto-exports.def b/third_party/heimdal/lib/hcrypto/libhcrypto-exports.def index 9ce7082580e..b1e2d34b116 100644 --- a/third_party/heimdal/lib/hcrypto/libhcrypto-exports.def +++ b/third_party/heimdal/lib/hcrypto/libhcrypto-exports.def @@ -147,7 +147,6 @@ EXPORTS hc_EVP_camellia_256_cbc hc_EVP_enc_null hc_EVP_get_cipherbyname - hc_EVP_md2 hc_EVP_md4 hc_EVP_md5 hc_EVP_md_null @@ -162,7 +161,6 @@ EXPORTS hc_EVP_sha384 hc_EVP_sha512 -;! hc_EVP_cc_md2 ;! hc_EVP_cc_md4 ;! hc_EVP_cc_md5 ;! hc_EVP_cc_sha1 @@ -177,7 +175,6 @@ EXPORTS ;! hc_EVP_cc_aes_192_cfb8 ;! hc_EVP_cc_aes_256_cfb8 - hc_EVP_ossl_md2 hc_EVP_ossl_md4 hc_EVP_ossl_md5 hc_EVP_ossl_sha1 @@ -196,7 +193,6 @@ EXPORTS hc_EVP_ossl_rc4 hc_EVP_ossl_rc4_40 - hc_EVP_pkcs11_md2 hc_EVP_pkcs11_md4 hc_EVP_pkcs11_md5 hc_EVP_pkcs11_sha1 @@ -214,7 +210,6 @@ EXPORTS hc_EVP_pkcs11_rc4 hc_EVP_pkcs11_rc4_40 - hc_EVP_w32crypto_md2 ;! hc_EVP_w32crypto_md4 ;! hc_EVP_w32crypto_md5 ;! hc_EVP_w32crypto_sha1 ;! @@ -234,7 +229,6 @@ EXPORTS hc_EVP_w32crypto_aes_192_cfb8 ;! hc_EVP_w32crypto_aes_256_cfb8 ;! - hc_EVP_hcrypto_md2 hc_EVP_hcrypto_md4 hc_EVP_hcrypto_md5 hc_EVP_hcrypto_sha1 @@ -265,9 +259,6 @@ EXPORTS hc_HMAC_Init_ex hc_HMAC_Update hc_HMAC_size - hc_MD2_Final - hc_MD2_Init - hc_MD2_Update hc_MD4_Final hc_MD4_Init hc_MD4_Update diff --git a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_log_u32.c b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_log_u32.c index f7bca01ded3..c9cc157911e 100644 --- a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_log_u32.c +++ b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_log_u32.c @@ -91,13 +91,24 @@ mp_err mp_log_u32(const mp_int *a, uint32_t base, uint32_t *c) return MP_VAL; } + /* `base' is at least 2 */ + /* A small shortcut for bases that are powers of two. */ if ((base & (base - 1u)) == 0u) { int y, bit_count; + for (y=0; (y < 7) && ((base & 1u) == 0u); y++) { + /* We must go through this loop at least once */ base >>= 1; } bit_count = mp_count_bits(a) - 1; + /* + * `y' is necessarily at least 1 because `base' is a power of two and + * larger than 1, so we must have gone through the loop at least once, so + * we can't be dividing by zero. + * + * scan-build thinks we can be dividing by zero... WAT. + */ *c = (uint32_t)(bit_count/y); return MP_OKAY; } diff --git a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_prime_rand.c b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_prime_rand.c index 4530e9a5e1e..23a1c4f78dc 100644 --- a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_prime_rand.c +++ b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_prime_rand.c @@ -39,7 +39,7 @@ mp_err s_mp_prime_random_ex(mp_int *a, int t, int size, int flags, private_mp_pr bsize = (size>>3) + ((size&7)?1:0); /* we need a buffer of bsize bytes */ - tmp = (unsigned char *) MP_MALLOC((size_t)bsize); + tmp = (unsigned char *) MP_CALLOC(1, (size_t)bsize); if (tmp == NULL) { return MP_MEM; } diff --git a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_root_u32.c b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_root_u32.c index ba65549c645..97b1b2ea482 100644 --- a/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_root_u32.c +++ b/third_party/heimdal/lib/hcrypto/libtommath/bn_mp_root_u32.c @@ -23,6 +23,9 @@ mp_err mp_root_u32(const mp_int *a, uint32_t b, mp_int *c) if (((b & 1u) == 0u) && (a->sign == MP_NEG)) { return MP_VAL; } + if (b == 0) { + return MP_VAL; + } if ((err = mp_init_multi(&t1, &t2, &t3, NULL)) != MP_OKAY) { return err; diff --git a/third_party/heimdal/lib/hcrypto/md2.c b/third_party/heimdal/lib/hcrypto/md2.c deleted file mode 100644 index 0170d416a1c..00000000000 --- a/third_party/heimdal/lib/hcrypto/md2.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2006 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <config.h> -#include <roken.h> - -#include "hash.h" -#include "md2.h" - -static const unsigned char subst[256] = { - 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, - 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, - 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, - 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, - 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, - 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, - 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, - 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, - 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, - 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, - 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, - 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, - 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, - 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, - 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, - 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, - 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, - 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 -}; - -int -MD2_Init (struct md2 *m) -{ - memset(m, 0, sizeof(*m)); - return 1; -} - -static void -calc(struct md2 *m, const void *v) -{ - unsigned char x[48], L; - const unsigned char *p = v; - int i, j, t; - - L = m->checksum[15]; - for (i = 0; i < 16; i++) - L = m->checksum[i] ^= subst[p[i] ^ L]; - - for (i = 0; i < 16; i++) { - x[i] = m->state[i]; - x[i + 16] = p[i]; - x[i + 32] = x[i] ^ p[i]; - } - - t = 0; - for (i = 0; i < 18; i++) { - for (j = 0; j < 48; j++) - t = x[j] ^= subst[t]; - t = (t + i) & 0xff; - } - - memcpy(m->state, x, 16); - memset_s(x, sizeof(x), 0, sizeof(x)); -} - -int -MD2_Update (struct md2 *m, const void *v, size_t len) -{ - size_t idx = m->len & 0xf; - const unsigned char *p = v; - - m->len += len; - if (len + idx >= 16) { - if (idx) { - memcpy(m->data + idx, p, 16 - idx); - calc(m, m->data); - p += 16; - len -= 16 - idx; - } - while (len >= 16) { - calc(m, p); - p += 16; - len -= 16; - } - idx = 0; - } - - memcpy(m->data + idx, p, len); - return 1; -} - -int -MD2_Final (void *res, struct md2 *m) -{ - unsigned char pad[16]; - size_t padlen; - - padlen = 16 - (m->len % 16); - memset(pad, padlen, padlen); - - MD2_Update(m, pad, padlen); - memcpy(pad, m->checksum, 16); - MD2_Update(m, pad, 16); - - memcpy(res, m->state, MD2_DIGEST_LENGTH); - memset_s(m, sizeof(*m), 0, sizeof(*m)); - return 1; -} diff --git a/third_party/heimdal/lib/hcrypto/md2.h b/third_party/heimdal/lib/hcrypto/md2.h deleted file mode 100644 index d82334eac94..00000000000 --- a/third_party/heimdal/lib/hcrypto/md2.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2006 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* $Id$ */ - -#ifndef HEIM_MD2_H -#define HEIM_MD2_H 1 - -/* symbol renaming */ -#define MD2_Init hc_MD2_Init -#define MD2_Update hc_MD2_Update -#define MD2_Final hc_MD2_Final - -/* - * - */ - -#define MD2_DIGEST_LENGTH 16 - -struct md2 { - size_t len; - unsigned char data[16]; /* stored unalligned data between Update's */ - unsigned char checksum[16]; - unsigned char state[16]; /* lower 16 bytes of X */ -}; - -typedef struct md2 MD2_CTX; - -int MD2_Init (struct md2 *m); -int MD2_Update (struct md2 *m, const void *p, size_t len); -int MD2_Final (void *res, struct md2 *m); - -#endif /* HEIM_MD2_H */ diff --git a/third_party/heimdal/lib/hcrypto/mdtest.c b/third_party/heimdal/lib/hcrypto/mdtest.c index d301c5c4a42..7ad93e3560e 100644 --- a/third_party/heimdal/lib/hcrypto/mdtest.c +++ b/third_party/heimdal/lib/hcrypto/mdtest.c @@ -39,7 +39,6 @@ #ifdef KRB5 #include <krb5-types.h> #endif -#include <md2.h> #include <md4.h> #include <md5.h> #include <sha.h> @@ -51,68 +50,60 @@ struct hash_foo { const char *name; size_t psize; size_t hsize; - void (*init)(void*); - void (*update)(void*, const void*, size_t); - void (*final)(void*, void*); + int (*init)(void*); + int (*update)(void*, const void*, size_t); + int (*final)(void*, void*); const EVP_MD * (*evp)(void); -} md2 = { - "MD2", - sizeof(MD2_CTX), - 16, - (void (*)(void*))MD2_Init, - (void (*)(void*,const void*, size_t))MD2_Update, - (void (*)(void*, void*))MD2_Final, - EVP_md2 -}, md4 = { +} md4 = { "MD4", sizeof(MD4_CTX), 16, - (void (*)(void*))MD4_Init, - (void (*)(void*,const void*, size_t))MD4_Update, - (void (*)(void*, void*))MD4_Final, + (int (*)(void*))MD4_Init, + (int (*)(void*,const void*, size_t))MD4_Update, + (int (*)(void*, void*))MD4_Final, EVP_md4 }, md5 = { "MD5", sizeof(MD5_CTX), 16, - (void (*)(void*))MD5_Init, - (void (*)(void*,const void*, size_t))MD5_Update, - (void (*)(void*, void*))MD5_Final, + (int (*)(void*))MD5_Init, + (int (*)(void*,const void*, size_t))MD5_Update, + (int (*)(void*, void*))MD5_Final, EVP_md5 }, sha1 = { "SHA-1", sizeof(struct sha), 20, - (void (*)(void*))SHA1_Init, - (void (*)(void*,const void*, size_t))SHA1_Update, - (void (*)(void*, void*))SHA1_Final, + (int (*)(void*))SHA1_Init, + (int (*)(void*,const void*, size_t))SHA1_Update, + (int (*)(void*, void*))SHA1_Final, EVP_sha1 }; struct hash_foo sha256 = { "SHA-256", sizeof(SHA256_CTX), 32, - (void (*)(void*))SHA256_Init, - (void (*)(void*,const void*, size_t))SHA256_Update, - (void (*)(void*, void*))SHA256_Final, + (int (*)(void*))SHA256_Init, + (int (*)(void*,const void*, size_t))SHA256_Update, + (int (*)(void*, void*))SHA256_Final, EVP_sha256 }; struct hash_foo sha384 = { "SHA-384", sizeof(SHA384_CTX), 48, - (void (*)(void*))SHA384_Init, - (void (*)(void*,const void*, size_t))SHA384_Update, - (void (*)(void*, void*))SHA384_Final, + (int (*)(void*))SHA384_Init, + (int (*)(void*,const void*, size_t))SHA384_Update, + (int (*)(void*, void*))SHA384_Final, EVP_sha384 }; struct hash_foo sha512 = { "SHA-512", sizeof(SHA512_CTX), 64, - (void (*)(void*))SHA512_Init, - (void (*)(void*,const void*, size_t))SHA512_Update, - (void (*)(void*, void*))SHA512_Final, + (int (*)(void*))SHA512_Init, + (int (*)(void*,const void*, size_t))SHA512_Update, + (int (*)(void*, void*))SHA512_Final, EVP_sha512 }; @@ -121,24 +112,6 @@ struct test { unsigned char hash[64]; }; -struct test md2_tests[] = { - {"", - "\x83\x50\xe5\xa3\xe2\x4c\x15\x3d\xf2\x27\x5c\x9f\x80\x69\x27\x73" }, - {"a", - "\x32\xec\x01\xec\x4a\x6d\xac\x72\xc0\xab\x96\xfb\x34\xc0\xb5\xd1" }, - {"abc", - "\xda\x85\x3b\x0d\x3f\x88\xd9\x9b\x30\x28\x3a\x69\xe6\xde\xd6\xbb" }, - {"message digest", - "\xab\x4f\x49\x6b\xfb\x2a\x53\x0b\x21\x9f\xf3\x30\x31\xfe\x06\xb0" }, - {"abcdefghijklmnopqrstuvwxyz", - "\x4e\x8d\xdf\xf3\x65\x02\x92\xab\x5a\x41\x08\xc3\xaa\x47\x94\x0b" }, - {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - "\xda\x33\xde\xf2\xa4\x2d\xf1\x39\x75\x35\x28\x46\xc3\x03\x38\xcd" }, - {"12345678901234567890123456789012345678901234567890123456789012345678901234567890", - "\xd5\x97\x6f\x79\xd8\x3d\x3a\x0d\xc9\x80\x6c\x3c\x66\xf3\xef\xd8" }, - {NULL, { 0 } } -}; - struct test md4_tests[] = { {"", {0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, 0x3c, 0x59, @@ -338,7 +311,6 @@ int main (void) { return - hash_test(&md2, md2_tests) + hash_test(&md4, md4_tests) + hash_test(&md5, md5_tests) + hash_test(&sha1, sha1_tests) + diff --git a/third_party/heimdal/lib/hcrypto/pkcs5.c b/third_party/heimdal/lib/hcrypto/pkcs5.c index dff3ccea398..88e71c4dd53 100644 --- a/third_party/heimdal/lib/hcrypto/pkcs5.c +++ b/third_party/heimdal/lib/hcrypto/pkcs5.c @@ -85,7 +85,8 @@ PKCS5_PBKDF2_HMAC(const void * password, size_t password_len, data = &tmpcksum[checksumsize]; - memcpy(data, salt, salt_len); + if (salt_len) + memcpy(data, salt, salt_len); keypart = 1; leftofkey = keylen; diff --git a/third_party/heimdal/lib/hcrypto/rand-fortuna.c b/third_party/heimdal/lib/hcrypto/rand-fortuna.c index 74ba12396fd..31f72330245 100644 --- a/third_party/heimdal/lib/hcrypto/rand-fortuna.c +++ b/third_party/heimdal/lib/hcrypto/rand-fortuna.c @@ -501,10 +501,9 @@ fortuna_reseed(void) /* add /etc/shadow */ fd = open("/etc/shadow", O_RDONLY, 0); if (fd >= 0) { - ssize_t n; rk_cloexec(fd); /* add_entropy will hash the buf */ - while ((n = read(fd, (char *)u.shad, sizeof(u.shad))) > 0) + while (read(fd, (char *)u.shad, sizeof(u.shad)) > 0) add_entropy(&main_state, u.shad, sizeof(u.shad)); close(fd); } diff --git a/third_party/heimdal/lib/hcrypto/rsa-ltm.c b/third_party/heimdal/lib/hcrypto/rsa-ltm.c index 1d5b73e60e5..49e35f5a38a 100644 --- a/third_party/heimdal/lib/hcrypto/rsa-ltm.c +++ b/third_party/heimdal/lib/hcrypto/rsa-ltm.c @@ -41,7 +41,7 @@ #include "tommath.h" #define CHECK(f) \ - do { if (ret == MP_OKAY && ((ret = f)) != MP_OKAY) { goto out; } } while (0) + do { where = __LINE__ + 1; if (ret == MP_OKAY && ((ret = f)) != MP_OKAY) { goto out; } } while (0) #define FIRST(e) do { ret = (e); } while (0) #define FIRST_ALLOC(e) \ do { where = __LINE__; ret = ((e)) ? MP_OKAY : MP_MEM; } while (0) @@ -50,9 +50,9 @@ #define THEN_IF_MP(cond, e) \ do { where = __LINE__ + 1; if (ret == MP_OKAY && (cond)) ret = (e); } while (0) #define THEN_IF_VOID(cond, e) \ - do { if (ret == MP_OKAY && (cond)) e; } while (0) + do { where = __LINE__ + 1; if (ret == MP_OKAY && (cond)) e; } while (0) #define THEN_VOID(e) \ - do { if (ret == MP_OKAY) e; } while (0) + do { where = __LINE__ + 1; if (ret == MP_OKAY) e; } while (0) #define THEN_ALLOC(e) \ do { where = __LINE__ + 1; if (ret == MP_OKAY) ret = ((e)) ? MP_OKAY : MP_MEM; } while (0) @@ -226,7 +226,7 @@ ltm_rsa_public_decrypt(int flen, const unsigned char* from, mp_err ret; size_t size; mp_int s, us, n, e; - int where = 0; + int where = __LINE__; if (padding != RSA_PKCS1_PADDING) return -1; @@ -250,7 +250,7 @@ ltm_rsa_public_decrypt(int flen, const unsigned char* from, mp_clear_multi(&e, &n, &s, NULL); mp_clear(&us); - if (ret != MP_OKAY) + if (ret != MP_OKAY || size == 0) return -where; /* head zero was skipped by mp_to_unsigned_bin */ @@ -280,7 +280,7 @@ ltm_rsa_private_encrypt(int flen, const unsigned char* from, size_t size; int blinding = (rsa->flags & RSA_FLAG_NO_BLINDING) == 0; int do_unblind = 0; - int where = 0; + int where = __LINE__; if (padding != RSA_PKCS1_PADDING) return -1; @@ -367,7 +367,7 @@ ltm_rsa_private_decrypt(int flen, const unsigned char* from, mp_int in, out, n, e, b, bi; int blinding = (rsa->flags & RSA_FLAG_NO_BLINDING) == 0; int do_unblind = 0; - int where = 0; + int where = __LINE__; if (padding != RSA_PKCS1_PADDING) return -1; @@ -530,7 +530,7 @@ ltm_rsa_generate_key(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb) mp_err ret; uint8_t high_nibbles = 0; int bitsp; - int where = 0; + int where = __LINE__; if (bits < 789) return -1; diff --git a/third_party/heimdal/lib/hcrypto/test_bulk.c b/third_party/heimdal/lib/hcrypto/test_bulk.c index e3737fd1e95..db5aeae1ee1 100644 --- a/third_party/heimdal/lib/hcrypto/test_bulk.c +++ b/third_party/heimdal/lib/hcrypto/test_bulk.c @@ -201,7 +201,6 @@ test_bulk_provider_hcrypto(void) test_bulk_cipher("hcrypto_aes_256_cfb8", EVP_hcrypto_aes_256_cfb8()); #endif test_bulk_cipher("hcrypto_rc4", EVP_hcrypto_rc4()); - test_bulk_digest("hcrypto_md2", EVP_hcrypto_md2()); test_bulk_digest("hcrypto_md4", EVP_hcrypto_md4()); test_bulk_digest("hcrypto_md5", EVP_hcrypto_md5()); test_bulk_digest("hcrypto_sha1", EVP_hcrypto_sha1()); @@ -219,7 +218,6 @@ test_bulk_provider_cc(void) test_bulk_cipher("cc_aes_256_cfb8", EVP_cc_aes_256_cfb8()); #endif test_bulk_cipher("cc_rc4", EVP_cc_rc4()); - test_bulk_digest("cc_md2", EVP_cc_md2()); test_bulk_digest("cc_md4", EVP_cc_md4()); test_bulk_digest("cc_md5", EVP_cc_md5()); test_bulk_digest("cc_sha1", EVP_cc_sha1()); @@ -238,7 +236,6 @@ test_bulk_provider_w32crypto(void) test_bulk_cipher("w32crypto_aes_256_cfb8", EVP_w32crypto_aes_256_cfb8()); #endif test_bulk_cipher("w32crypto_rc4", EVP_w32crypto_rc4()); - test_bulk_digest("w32crypto_md2", EVP_w32crypto_md2()); test_bulk_digest("w32crypto_md4", EVP_w32crypto_md4()); test_bulk_digest("w32crypto_md5", EVP_w32crypto_md5()); test_bulk_digest("w32crypto_sha1", EVP_w32crypto_sha1()); diff --git a/third_party/heimdal/lib/hcrypto/test_cipher.c b/third_party/heimdal/lib/hcrypto/test_cipher.c index c566e4bfd00..9be65dd16e5 100644 --- a/third_party/heimdal/lib/hcrypto/test_cipher.c +++ b/third_party/heimdal/lib/hcrypto/test_cipher.c @@ -422,14 +422,8 @@ main(int argc, char **argv) ret += test_cipher(i, EVP_ossl_aes_256_cbc(), &aes_tests[i]); for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++) ret += test_cipher(i, EVP_ossl_aes_128_cfb8(), &aes_cfb_tests[i]); - for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++) - ret += test_cipher(i, EVP_ossl_rc2_cbc(), &rc2_tests[i]); - for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++) - ret += test_cipher(i, EVP_ossl_rc2_40_cbc(), &rc2_40_tests[i]); for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++) ret += test_cipher(i, EVP_ossl_des_ede3_cbc(), &des_ede3_tests[i]); - for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++) - ret += test_cipher(i, EVP_ossl_rc4(), &rc4_tests[i]); #endif /* PKCS11_MODULE_PATH */ return ret; diff --git a/third_party/heimdal/lib/hcrypto/test_crypto.in b/third_party/heimdal/lib/hcrypto/test_crypto.in index d5b3893164a..91c4d8f1093 100644 --- a/third_party/heimdal/lib/hcrypto/test_crypto.in +++ b/third_party/heimdal/lib/hcrypto/test_crypto.in @@ -82,11 +82,11 @@ for a in unix fortuna egd w32crypto ;do { echo "rand output same!" ; exit 1; } done -./example_evp_cipher 1 ${srcdir}/test_crypto.in test-out-1 || \ - { echo "1 failed" ; exit 1; } - -for a in 7 15 16 17 31 32 33 ; do - ./example_evp_cipher $a ${srcdir}/test_crypto.in test-out-$a +for a in 1 7 15 16 17 31 32 33 ; do + ./example_evp_cipher $a ${srcdir}/test_crypto.in test-out-$a || + { echo "$s failed" ; exit 1; } +done +for a in 7 15 16 17 31 32 33 ; do cmp test-out-1 test-out-$a || { echo "cmp $a failed" ; exit 1; } done diff --git a/third_party/heimdal/lib/hcrypto/undef.h b/third_party/heimdal/lib/hcrypto/undef.h index 512b8c3f02e..6de5734a28f 100644 --- a/third_party/heimdal/lib/hcrypto/undef.h +++ b/third_party/heimdal/lib/hcrypto/undef.h @@ -116,7 +116,6 @@ #undef EVP_des_cbc #undef EVP_des_ede3_cbc #undef EVP_enc_null -#undef EVP_md2 #undef EVP_md4 #undef EVP_md5 #undef EVP_md_null @@ -158,7 +157,6 @@ #undef EVP_CIPH_ALWAYS_CALL_INIT #undef EVP_CIPH_RAND_KEY #undef EVP_CTRL_RAND_KEY -#undef NID_md2 #undef NID_md4 #undef NID_md5 #undef NID_sha1 diff --git a/third_party/heimdal/lib/hcrypto/version-script.map b/third_party/heimdal/lib/hcrypto/version-script.map index fbd8141dbf2..b51d4bb607f 100644 --- a/third_party/heimdal/lib/hcrypto/version-script.map +++ b/third_party/heimdal/lib/hcrypto/version-script.map @@ -154,7 +154,6 @@ HEIMDAL_CRYPTO_1.0 { hc_EVP_camellia_256_cbc; hc_EVP_enc_null; hc_EVP_get_cipherbyname; - hc_EVP_md2; hc_EVP_md4; hc_EVP_md5; hc_EVP_md_null; @@ -169,7 +168,6 @@ HEIMDAL_CRYPTO_1.0 { hc_EVP_sha384; hc_EVP_sha512; - hc_EVP_cc_md2; hc_EVP_cc_md4; hc_EVP_cc_md5; hc_EVP_cc_sha1; @@ -184,7 +182,6 @@ HEIMDAL_CRYPTO_1.0 { hc_EVP_cc_aes_192_cfb8; hc_EVP_cc_aes_256_cfb8; - hc_EVP_hcrypto_md2; hc_EVP_hcrypto_md4; hc_EVP_hcrypto_md5; hc_EVP_hcrypto_sha1; @@ -201,7 +198,6 @@ HEIMDAL_CRYPTO_1.0 { hc_EVP_hcrypto_rc4; hc_EVP_hcrypto_rc4_40; - hc_EVP_ossl_md2; hc_EVP_ossl_md4; hc_EVP_ossl_md5; hc_EVP_ossl_sha1; @@ -220,7 +216,6 @@ HEIMDAL_CRYPTO_1.0 { hc_EVP_ossl_rc4; hc_EVP_ossl_rc4_40; - hc_EVP_pkcs11_md2; hc_EVP_pkcs11_md4; hc_EVP_pkcs11_md5; hc_EVP_pkcs11_sha1; @@ -250,9 +245,6 @@ HEIMDAL_CRYPTO_1.0 { hc_HMAC_Init_ex; hc_HMAC_Update; hc_HMAC_size; - hc_MD2_Final; - hc_MD2_Init; - hc_MD2_Update; hc_MD4_Final; hc_MD4_Init; hc_MD4_Update; diff --git a/third_party/heimdal/lib/hdb/common.c b/third_party/heimdal/lib/hdb/common.c index 56e582abaa8..1c947b3cfc5 100644 --- a/third_party/heimdal/lib/hdb/common.c +++ b/third_party/heimdal/lib/hdb/common.c @@ -1586,6 +1586,7 @@ fix_princ_name(krb5_context context, s, NULL); } + free(s); return ret; } diff --git a/third_party/heimdal/lib/hdb/hdb.c b/third_party/heimdal/lib/hdb/hdb.c index 171ba9e3fd3..c9c6c85bef2 100644 --- a/third_party/heimdal/lib/hdb/hdb.c +++ b/third_party/heimdal/lib/hdb/hdb.c @@ -333,7 +333,7 @@ hdb_next_enctype2key(krb5_context context, krb5_error_code hdb_enctype2key(krb5_context context, - hdb_entry *e, + const hdb_entry *e, const Keys *keyset, krb5_enctype enctype, Key **key) diff --git a/third_party/heimdal/lib/hx509/ca.c b/third_party/heimdal/lib/hx509/ca.c index 3d62b93fa57..1ca8d51da39 100644 --- a/third_party/heimdal/lib/hx509/ca.c +++ b/third_party/heimdal/lib/hx509/ca.c @@ -1179,6 +1179,7 @@ hx509_ca_tbs_add_san_permanentIdentifier_string(hx509_context context, char *freeme, *p; int ret; + memset(&oid, 0, sizeof(oid)); if ((freeme = strdup(str)) == NULL) return hx509_enomem(context); @@ -1287,6 +1288,7 @@ hx509_ca_tbs_add_san_hardwareModuleName_string(hx509_context context, char *freeme, *p; int ret; + memset(&oid, 0, sizeof(oid)); if ((freeme = strdup(str)) == NULL) return hx509_enomem(context); @@ -2611,7 +2613,6 @@ set_template(hx509_context context, "template_cert", NULL); subj_name = heim_config_get_string(context->hcontext, cf, "subject_name", NULL); - ekus = heim_config_get_strings(context->hcontext, cf, "ekus", NULL); if (cert_template) { hx509_certs certs; @@ -2667,6 +2668,7 @@ set_template(hx509_context context, return ret; } + ekus = heim_config_get_strings(context->hcontext, cf, "ekus", NULL); if (ekus) { size_t i; diff --git a/third_party/heimdal/lib/hx509/cert.c b/third_party/heimdal/lib/hx509/cert.c index 3dda886edc5..e7e2423c54d 100644 --- a/third_party/heimdal/lib/hx509/cert.c +++ b/third_party/heimdal/lib/hx509/cert.c @@ -1207,12 +1207,71 @@ certificate_is_self_signed(hx509_context context, if (ret) { hx509_set_error_string(context, 0, ret, "Failed to check if self signed"); - } else + } else if (diff == 0) ret = _hx509_self_signed_valid(context, &cert->signatureAlgorithm); return ret; } +HX509_LIB_FUNCTION int HX509_LIB_CALL +hx509_cert_is_self_signed(hx509_context context, + hx509_cert c, + int *self_signed) +{ + return certificate_is_self_signed(context, c->data, self_signed); +} + +HX509_LIB_FUNCTION int HX509_LIB_CALL +hx509_cert_is_ca(hx509_context context, + hx509_cert c, + int *is_ca) +{ + BasicConstraints bc; + const Extension *e; + size_t size; + size_t i = 0; + int ret = 0; + + *is_ca = 0; + if (_hx509_cert_get_version(c->data) < 3) + return certificate_is_self_signed(context, c->data, is_ca); + + e = find_extension(c->data, &asn1_oid_id_x509_ce_basicConstraints, &i); + if (e == NULL) { + *is_ca = 0; + return 0; + } + + ret = decode_BasicConstraints(e->extnValue.data, + e->extnValue.length, &bc, + &size); + if (ret) + return ret; + + *is_ca = bc.cA; + free_BasicConstraints(&bc); + return 0; +} + +HX509_LIB_FUNCTION int HX509_LIB_CALL +hx509_cert_is_root(hx509_context context, + hx509_cert c, + int *is_root) +{ + int ret; + + *is_root = 0; + ret = hx509_cert_is_ca(context, c, is_root); + if (ret) + return ret; + if (*is_root == 0) + /* Not a CA certificate -> not a root certificate */ + return 0; + + /* A CA certificate. If it's self-signed, it's a root certificate. */ + return hx509_cert_is_self_signed(context, c, is_root); +} + /* * The subjectName is "null" when it's empty set of relative DBs. */ diff --git a/third_party/heimdal/lib/hx509/collector.c b/third_party/heimdal/lib/hx509/collector.c index 7b46809816c..f1423aced2f 100644 --- a/third_party/heimdal/lib/hx509/collector.c +++ b/third_party/heimdal/lib/hx509/collector.c @@ -147,6 +147,16 @@ _hx509_collector_private_key_add(hx509_context context, key_data->data, key_data->length, HX509_KEY_FORMAT_DER, &key->private_key); + if (ret && localKeyId) { + int ret2; + + ret2 = hx509_parse_private_key(context, alg, + localKeyId->data, localKeyId->length, + HX509_KEY_FORMAT_PKCS8, + &key->private_key); + if (ret2 == 0) + ret = 0; + } if (ret) goto out; } diff --git a/third_party/heimdal/lib/hx509/crypto-ec.c b/third_party/heimdal/lib/hx509/crypto-ec.c index 46e6cd8e339..bd5d01a609a 100644 --- a/third_party/heimdal/lib/hx509/crypto-ec.c +++ b/third_party/heimdal/lib/hx509/crypto-ec.c @@ -34,11 +34,16 @@ #include <config.h> #ifdef HAVE_HCRYPTO_W_OPENSSL +#include <openssl/evp.h> #include <openssl/ec.h> #include <openssl/ecdsa.h> #include <openssl/rsa.h> #include <openssl/bn.h> #include <openssl/objects.h> +#ifdef HAVE_OPENSSL_30 +#include <openssl/asn1.h> +#include <openssl/core_names.h> +#endif #define HEIM_NO_CRYPTO_HDRS #endif /* HAVE_HCRYPTO_W_OPENSSL */ @@ -53,43 +58,50 @@ HX509_LIB_FUNCTION void HX509_LIB_CALL _hx509_private_eckey_free(void *eckey) { #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 + EVP_PKEY_free(eckey); +#else EC_KEY_free(eckey); #endif +#endif } #ifdef HAVE_HCRYPTO_W_OPENSSL -static int -heim_oid2ecnid(heim_oid *oid) -{ - /* - * Now map to openssl OID fun - */ - - if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0) - return NID_X9_62_prime256v1; +static struct oid2nid_st { + const heim_oid *oid; + int nid; +} oid2nid[] = { + { ASN1_OID_ID_EC_GROUP_SECP256R1, NID_X9_62_prime256v1 }, #ifdef NID_secp521r1 - else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP521R1) == 0) - return NID_secp521r1; + { ASN1_OID_ID_EC_GROUP_SECP521R1, NID_secp521r1 }, #endif #ifdef NID_secp384r1 - else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP384R1) == 0) - return NID_secp384r1; + { ASN1_OID_ID_EC_GROUP_SECP384R1, NID_secp384r1 }, #endif #ifdef NID_secp160r1 - else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0) - return NID_secp160r1; + { ASN1_OID_ID_EC_GROUP_SECP160R1, NID_secp160r1 }, #endif #ifdef NID_secp160r2 - else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0) - return NID_secp160r2; + { ASN1_OID_ID_EC_GROUP_SECP160R2, NID_secp160r2 }, #endif + /* XXX Add more! Add X25519! */ +}; + +int +_hx509_ossl_oid2nid(heim_oid *oid) +{ + size_t i; + for (i = 0; i < sizeof(oid2nid)/sizeof(oid2nid[0]); i++) + if (der_heim_oid_cmp(oid, oid2nid[i].oid) == 0) + return oid2nid[i].nid; return NID_undef; } static int -parse_ECParameters(hx509_context context, - heim_octet_string *parameters, int *nid) +ECParameters2nid(hx509_context context, + heim_octet_string *parameters, + int *nid) { ECParameters ecparam; size_t size; @@ -117,7 +129,7 @@ parse_ECParameters(hx509_context context, return HX509_CRYPTO_SIG_INVALID_FORMAT; } - *nid = heim_oid2ecnid(&ecparam.u.namedCurve); + *nid = _hx509_ossl_oid2nid(&ecparam.u.namedCurve); free_ECParameters(&ecparam); if (*nid == NID_undef) { hx509_set_error_string(context, 0, ret, @@ -127,6 +139,39 @@ parse_ECParameters(hx509_context context, return 0; } +#ifdef HAVE_OPENSSL_30 +static const EVP_MD * +signature_alg2digest_evp_md(hx509_context context, + const AlgorithmIdentifier *digest_alg) +{ + if ((&digest_alg->algorithm == &asn1_oid_id_sha512 || + der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha512) == 0)) + return EVP_sha512(); + if ((&digest_alg->algorithm == &asn1_oid_id_sha384 || + der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha384) == 0)) + return EVP_sha384(); + if ((&digest_alg->algorithm == &asn1_oid_id_sha256 || + der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_sha256) == 0)) + return EVP_sha256(); + if ((&digest_alg->algorithm == &asn1_oid_id_secsig_sha_1 || + der_heim_oid_cmp(&digest_alg->algorithm, &asn1_oid_id_secsig_sha_1) == 0)) + return EVP_sha1(); + if ((&digest_alg->algorithm == &asn1_oid_id_rsa_digest_md5 || + der_heim_oid_cmp(&digest_alg->algorithm, + &asn1_oid_id_rsa_digest_md5) == 0)) + return EVP_md5(); + + /* + * XXX Decode the `digest_alg->algorithm' OID and include it in the error + * message. + */ + hx509_set_error_string(context, 0, EINVAL, + "Digest algorithm not found"); + return NULL; +} +#endif + + /* * @@ -140,6 +185,106 @@ ecdsa_verify_signature(hx509_context context, const heim_octet_string *data, const heim_octet_string *sig) { +#ifdef HAVE_OPENSSL_30 + const AlgorithmIdentifier *digest_alg = sig_alg->digest_alg; + const EVP_MD *md = signature_alg2digest_evp_md(context, digest_alg); + const SubjectPublicKeyInfo *spi; + const char *curve_sn = NULL; /* sn == short name in OpenSSL parlance */ + OSSL_PARAM params[2]; + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *mdctx = NULL; + EVP_PKEY *template = NULL; + EVP_PKEY *public = NULL; + const unsigned char *p; + size_t len; + char *curve_sn_dup = NULL; + int groupnid; + int ret = 0; + + spi = &signer->tbsCertificate.subjectPublicKeyInfo; + if (der_heim_oid_cmp(&spi->algorithm.algorithm, + ASN1_OID_ID_ECPUBLICKEY) != 0) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + /* XXX Include the OID in the message */ + "Unsupported subjectPublicKey algorithm"); + if (ret == 0) + ret = ECParameters2nid(context, spi->algorithm.parameters, &groupnid); + if (ret == 0 && (curve_sn = OBJ_nid2sn(groupnid)) == NULL) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + "Could not resolve curve NID %d to its short name", + groupnid); + if (ret == 0 && (curve_sn_dup = strdup(curve_sn)) == NULL) + ret = hx509_enomem(context); + if (ret == 0 && (mdctx = EVP_MD_CTX_new()) == NULL) + ret = hx509_enomem(context); + + /* + * In order for d2i_PublicKey() to work we need to create a template key + * that has the curve parameters for the subjectPublicKey. + * + * Or maybe we could learn to use the OSSL_DECODER(3) API. But this works, + * at least until OpenSSL deprecates d2i_PublicKey() and forces us to use + * OSSL_DECODER(3). + */ + if (ret == 0) { + /* + * Apparently there's no error checking to be done here? Why does + * OSSL_PARAM_construct_utf8_string() want a non-const for the value? + * Is that a bug in OpenSSL? + */ + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + curve_sn_dup, 0); + params[1] = OSSL_PARAM_construct_end(); + + if ((pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) + ret = hx509_enomem(context); + } + if (ret == 0 && EVP_PKEY_fromdata_init(pctx) != 1) + ret = hx509_enomem(context); + if (ret == 0 && + EVP_PKEY_fromdata(pctx, &template, + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, params) != 1) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + "Could not set up to parse key for curve %s", + curve_sn); + + /* Finally we can decode the subjectPublicKey */ + p = spi->subjectPublicKey.data; + len = spi->subjectPublicKey.length / 8; + if (ret == 0 && + (public = d2i_PublicKey(EVP_PKEY_EC, &template, &p, len)) == NULL) + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + + /* EVP_DigestVerifyInit() will allocate a new pctx */ + EVP_PKEY_CTX_free(pctx); + pctx = NULL; + + if (ret == 0 && + EVP_DigestVerifyInit(mdctx, &pctx, md, NULL, public) != 1) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + "Could not initialize " + "OpenSSL signature verification"); + if (ret == 0 && + EVP_DigestVerifyUpdate(mdctx, data->data, data->length) != 1) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + "Could not initialize " + "OpenSSL signature verification"); + if (ret == 0 && + EVP_DigestVerifyFinal(mdctx, sig->data, sig->length) != 1) + hx509_set_error_string(context, 0, + ret = HX509_CRYPTO_SIG_INVALID_FORMAT, + "Signature verification failed"); + + EVP_MD_CTX_free(mdctx); + EVP_PKEY_free(template); + free(curve_sn_dup); + return ret; +#else const AlgorithmIdentifier *digest_alg; const SubjectPublicKeyInfo *spi; heim_octet_string digest; @@ -153,28 +298,28 @@ ecdsa_verify_signature(hx509_context context, digest_alg = sig_alg->digest_alg; ret = _hx509_create_signature(context, - NULL, - digest_alg, - data, - NULL, - &digest); + NULL, + digest_alg, + data, + NULL, + &digest); if (ret) - return ret; + return ret; /* set up EC KEY */ spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0) - return HX509_CRYPTO_SIG_INVALID_FORMAT; + return HX509_CRYPTO_SIG_INVALID_FORMAT; /* * Find the group id */ - ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid); + ret = ECParameters2nid(context, spi->algorithm.parameters, &groupnid); if (ret) { - der_free_octet_string(&digest); - return ret; + der_free_octet_string(&digest); + return ret; } /* @@ -190,20 +335,21 @@ ecdsa_verify_signature(hx509_context context, len = spi->subjectPublicKey.length / 8; if (o2i_ECPublicKey(&key, &p, len) == NULL) { - EC_KEY_free(key); - return HX509_CRYPTO_SIG_INVALID_FORMAT; + EC_KEY_free(key); + return HX509_CRYPTO_SIG_INVALID_FORMAT; } ret = ECDSA_verify(-1, digest.data, digest.length, - sig->data, sig->length, key); + sig->data, sig->length, key); der_free_octet_string(&digest); EC_KEY_free(key); if (ret != 1) { - ret = HX509_CRYPTO_SIG_INVALID_FORMAT; - return ret; + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + return ret; } return 0; +#endif } static int @@ -215,6 +361,56 @@ ecdsa_create_signature(hx509_context context, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { +#ifdef HAVE_OPENSSL_30 + const AlgorithmIdentifier *digest_alg = sig_alg->digest_alg; + const EVP_MD *md = signature_alg2digest_evp_md(context, digest_alg); + EVP_MD_CTX *mdctx = NULL; + EVP_PKEY_CTX *pctx = NULL; + const heim_oid *sig_oid; + int ret = 0; + + sig->data = NULL; + sig->length = 0; + if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) + _hx509_abort("internal error passing private key to wrong ops"); + + sig_oid = sig_alg->sig_oid; + digest_alg = sig_alg->digest_alg; + + if (signatureAlgorithm) + ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, + "\x05\x00", 2); + mdctx = EVP_MD_CTX_new(); + if (mdctx == NULL) + ret = hx509_enomem(context); + if (ret == 0 && EVP_DigestSignInit(mdctx, &pctx, md, NULL, + signer->private_key.ecdsa) != 1) + ret = HX509_CMS_FAILED_CREATE_SIGATURE; + if (ret == 0 && EVP_DigestSignUpdate(mdctx, data->data, data->length) != 1) + ret = HX509_CMS_FAILED_CREATE_SIGATURE; + if (ret == 0 && EVP_DigestSignFinal(mdctx, NULL, &sig->length) != 1) + ret = HX509_CMS_FAILED_CREATE_SIGATURE; + if (ret == 0 && (sig->data = malloc(sig->length)) == NULL) + ret = hx509_enomem(context); + if (ret == 0 && EVP_DigestSignFinal(mdctx, sig->data, &sig->length) != 1) + ret = HX509_CMS_FAILED_CREATE_SIGATURE; + + if (ret == HX509_CMS_FAILED_CREATE_SIGATURE) { + /* XXX Extract error detail from OpenSSL */ + hx509_set_error_string(context, 0, ret, + "ECDSA sign failed"); + } + + if (ret) { + if (signatureAlgorithm) + free_AlgorithmIdentifier(signatureAlgorithm); + free(sig->data); + sig->data = NULL; + sig->length = 0; + } + EVP_MD_CTX_free(mdctx); + return ret; +#else const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; @@ -222,7 +418,7 @@ ecdsa_create_signature(hx509_context context, int ret; if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) - _hx509_abort("internal error passing private key to wrong ops"); + _hx509_abort("internal error passing private key to wrong ops"); sig_oid = sig_alg->sig_oid; digest_alg = sig_alg->digest_alg; @@ -230,59 +426,63 @@ ecdsa_create_signature(hx509_context context, if (signatureAlgorithm) { ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); - if (ret) { - hx509_clear_error_string(context); - return ret; - } + if (ret) { + hx509_clear_error_string(context); + return ret; + } } ret = _hx509_create_signature(context, - NULL, - digest_alg, - data, - NULL, - &indata); + NULL, + digest_alg, + data, + NULL, + &indata); if (ret) - goto error; + goto error; sig->length = ECDSA_size(signer->private_key.ecdsa); sig->data = malloc(sig->length); if (sig->data == NULL) { - der_free_octet_string(&indata); - ret = ENOMEM; - hx509_set_error_string(context, 0, ret, "out of memory"); - goto error; + der_free_octet_string(&indata); + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto error; } siglen = sig->length; ret = ECDSA_sign(-1, indata.data, indata.length, - sig->data, &siglen, signer->private_key.ecdsa); + sig->data, &siglen, signer->private_key.ecdsa); der_free_octet_string(&indata); if (ret != 1) { - ret = HX509_CMS_FAILED_CREATE_SIGATURE; - hx509_set_error_string(context, 0, ret, - "ECDSA sign failed: %d", ret); - goto error; + ret = HX509_CMS_FAILED_CREATE_SIGATURE; + hx509_set_error_string(context, 0, ret, + "ECDSA sign failed: %d", ret); + goto error; } if (siglen > sig->length) - _hx509_abort("ECDSA signature prelen longer the output len"); + _hx509_abort("ECDSA signature prelen longer the output len"); sig->length = siglen; return 0; - error: +error: if (signatureAlgorithm) - free_AlgorithmIdentifier(signatureAlgorithm); + free_AlgorithmIdentifier(signatureAlgorithm); return ret; +#endif } static int ecdsa_available(const hx509_private_key signer, const AlgorithmIdentifier *sig_alg) { +#ifdef HAVE_OPENSSL_30 const struct signature_alg *sig; - const EC_GROUP *group; + size_t group_name_len = 0; + char group_name_buf[96]; + EC_GROUP *group = NULL; BN_CTX *bnctx = NULL; BIGNUM *order = NULL; int ret = 0; @@ -291,34 +491,75 @@ ecdsa_available(const hx509_private_key signer, _hx509_abort("internal error passing private key to wrong ops"); sig = _hx509_find_sig_alg(&sig_alg->algorithm); - if (sig == NULL || sig->digest_size == 0) return 0; + if (EVP_PKEY_get_group_name(signer->private_key.ecdsa, group_name_buf, + sizeof(group_name_buf), + &group_name_len) != 1 || + group_name_len >= sizeof(group_name_buf)) { + return 0; + } + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(group_name_buf)); + bnctx = BN_CTX_new(); + order = BN_new(); + if (group && bnctx && order && + EC_GROUP_get_order(group, order, bnctx) == 1) + ret = 1; + +#if 0 + /* + * If anything, require a digest at least as wide as the EC key size + * + * if (BN_num_bytes(order) > sig->digest_size) + * ret = 0; + */ +#endif + + BN_CTX_free(bnctx); + BN_clear_free(order); + EC_GROUP_free(group); + return ret; +#else + const struct signature_alg *sig; + const EC_GROUP *group; + BN_CTX *bnctx = NULL; + BIGNUM *order = NULL; + int ret = 0; + + if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0) + _hx509_abort("internal error passing private key to wrong ops"); + + sig = _hx509_find_sig_alg(&sig_alg->algorithm); + + if (sig == NULL || sig->digest_size == 0) + return 0; + group = EC_KEY_get0_group(signer->private_key.ecdsa); if (group == NULL) - return 0; + return 0; bnctx = BN_CTX_new(); order = BN_new(); if (order == NULL) - goto err; + goto err; if (EC_GROUP_get_order(group, order, bnctx) != 1) - goto err; + goto err; #if 0 /* If anything, require a digest at least as wide as the EC key size */ if (BN_num_bytes(order) > sig->digest_size) #endif - ret = 1; + ret = 1; err: if (bnctx) - BN_CTX_free(bnctx); + BN_CTX_free(bnctx); if (order) - BN_clear_free(order); + BN_clear_free(order); - return ret; + return ret; +#endif } static int @@ -347,55 +588,119 @@ ecdsa_private_key_import(hx509_context context, hx509_key_format_t format, hx509_private_key private_key) { +#ifdef HAVE_OPENSSL_30 + const unsigned char *p = data; + EVP_PKEY *key = NULL; + int ret = 0; + + switch (format) { + case HX509_KEY_FORMAT_PKCS8: + key = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, len); + if (key == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to parse EC private key"); + return HX509_PARSING_KEY_FAILED; + } + break; + + default: + return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; + } + + /* + * We used to have to call EC_KEY_new(), then EC_KEY_set_group() the group + * (curve) on the resulting EC_KEY _before_ we could d2i_ECPrivateKey() the + * key, but that's all deprecated in OpenSSL 3.0. + * + * In fact, it's not clear how ever to assign a group to a private key, + * but that's what the documentation for d2i_PrivateKey() says: that + * its `EVP_PKEY **' argument must be non-NULL pointing to a key that + * has had the group set. + * + * However, from code inspection it's clear that when the ECParameters + * are present in the private key payload passed to d2i_PrivateKey(), + * the group will be taken from that. + * + * What we'll do is that if we have `keyai->parameters' we'll check if the + * key we got is for the same group. + */ + if (keyai->parameters) { + size_t gname_len = 0; + char buf[96]; + int got_group_nid = NID_undef; + int want_groupnid = NID_undef; + + ret = ECParameters2nid(context, keyai->parameters, &want_groupnid); + if (ret == 0 && + (EVP_PKEY_get_group_name(key, buf, sizeof(buf), &gname_len) != 1 || + gname_len >= sizeof(buf))) + ret = HX509_ALG_NOT_SUPP; + if (ret == 0) + got_group_nid = OBJ_txt2nid(buf); + if (ret == 0 && + (got_group_nid == NID_undef || want_groupnid != got_group_nid)) + ret = HX509_ALG_NOT_SUPP; + } + + if (ret == 0) { + private_key->private_key.ecdsa = key; + private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; + key = NULL; + } + + EVP_PKEY_free(key); + return ret; +#else const unsigned char *p = data; EC_KEY **pkey = NULL; EC_KEY *key; if (keyai->parameters) { - EC_GROUP *group; - int groupnid; - int ret; - - ret = parse_ECParameters(context, keyai->parameters, &groupnid); - if (ret) - return ret; - - key = EC_KEY_new(); - if (key == NULL) - return ENOMEM; - - group = EC_GROUP_new_by_curve_name(groupnid); - if (group == NULL) { - EC_KEY_free(key); - return ENOMEM; - } - EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); - if (EC_KEY_set_group(key, group) == 0) { - EC_KEY_free(key); - EC_GROUP_free(group); - return ENOMEM; - } - EC_GROUP_free(group); - pkey = &key; + EC_GROUP *group; + int groupnid; + int ret; + + ret = ECParameters2nid(context, keyai->parameters, &groupnid); + if (ret) + return ret; + + key = EC_KEY_new(); + if (key == NULL) + return ENOMEM; + + group = EC_GROUP_new_by_curve_name(groupnid); + if (group == NULL) { + EC_KEY_free(key); + return ENOMEM; + } + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_set_group(key, group) != 1) { + EC_KEY_free(key); + EC_GROUP_free(group); + return ENOMEM; + } + EC_GROUP_free(group); + pkey = &key; } switch (format) { case HX509_KEY_FORMAT_DER: - private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); - if (private_key->private_key.ecdsa == NULL) { - hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, - "Failed to parse EC private key"); - return HX509_PARSING_KEY_FAILED; - } - private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; - break; + private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); + if (private_key->private_key.ecdsa == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to parse EC private key"); + return HX509_PARSING_KEY_FAILED; + } + private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; + break; default: - return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; + return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; +#endif } static int diff --git a/third_party/heimdal/lib/hx509/crypto.c b/third_party/heimdal/lib/hx509/crypto.c index 8d368ed9c4d..05f694b41c5 100644 --- a/third_party/heimdal/lib/hx509/crypto.c +++ b/third_party/heimdal/lib/hx509/crypto.c @@ -1304,10 +1304,27 @@ hx509_parse_private_key(hx509_context context, *private_key = NULL; - if (format == HX509_KEY_FORMAT_PKCS8) { + ops = hx509_find_private_alg(&keyai->algorithm); + if (ops == NULL) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_NO_SUPPORTED; + } + + ret = hx509_private_key_init(private_key, ops, NULL); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + + ret = (*ops->import)(context, keyai, data, len, format, *private_key); + if (ret) + hx509_private_key_free(private_key); + + if (ret && format == HX509_KEY_FORMAT_PKCS8) { PKCS8PrivateKeyInfo ki; hx509_private_key key; + /* Re-enter to try parsing the DER-encoded key from PKCS#8 envelope */ ret = decode_PKCS8PrivateKeyInfo(data, len, &ki, NULL); if (ret) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, @@ -1315,39 +1332,18 @@ hx509_parse_private_key(hx509_context context, "key"); return HX509_PARSING_KEY_FAILED; } - - /* Re-enter to parse DER-encoded key from PKCS#8 envelope */ ret = hx509_parse_private_key(context, &ki.privateKeyAlgorithm, ki.privateKey.data, ki.privateKey.length, HX509_KEY_FORMAT_DER, &key); free_PKCS8PrivateKeyInfo(&ki); if (ret) { - hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, - "Failed to parse RSA key from PKCS#8 " + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to parse RSA key from PKCS#8 " "envelope"); - return HX509_PARSING_KEY_FAILED; + return HX509_PARSING_KEY_FAILED; } - *private_key = key; - return ret; - } - - ops = hx509_find_private_alg(&keyai->algorithm); - if (ops == NULL) { - hx509_clear_error_string(context); - return HX509_SIG_ALG_NO_SUPPORTED; } - - ret = hx509_private_key_init(private_key, ops, NULL); - if (ret) { - hx509_set_error_string(context, 0, ret, "out of memory"); - return ret; - } - - ret = (*ops->import)(context, keyai, data, len, format, *private_key); - if (ret) - hx509_private_key_free(private_key); - return ret; } diff --git a/third_party/heimdal/lib/hx509/hx509.h b/third_party/heimdal/lib/hx509/hx509.h index 75d64734b27..6bd36e98b15 100644 --- a/third_party/heimdal/lib/hx509/hx509.h +++ b/third_party/heimdal/lib/hx509/hx509.h @@ -162,6 +162,7 @@ typedef enum { /* flags to hx509_certs_store */ #define HX509_CERTS_STORE_NO_PRIVATE_KEYS 0x04 +#define HX509_CERTS_STORE_NO_ROOTS 0x08 /* flags to hx509_set_error_string */ diff --git a/third_party/heimdal/lib/hx509/hxtool-commands.in b/third_party/heimdal/lib/hx509/hxtool-commands.in index 279095d270e..1bd0119ad72 100644 --- a/third_party/heimdal/lib/hx509/hxtool-commands.in +++ b/third_party/heimdal/lib/hx509/hxtool-commands.in @@ -363,6 +363,21 @@ command = { argument = "password" help = "password, prompter, or environment" } + option = { + long = "append" + type = "flag" + help = "append source to destination" + } + option = { + long = "root-certs" + type = "-flag" + help = "do not copy root certificates" + } + option = { + long = "private-keys" + type = "-flag" + help = "do not copy private keys" + } min_args="2" argument="in-certificates-1 ... out-certificate" help = "Copy in certificates stores into out certificate store" diff --git a/third_party/heimdal/lib/hx509/hxtool.1 b/third_party/heimdal/lib/hx509/hxtool.1 index 377500784ec..040573f4cde 100644 --- a/third_party/heimdal/lib/hx509/hxtool.1 +++ b/third_party/heimdal/lib/hx509/hxtool.1 @@ -63,24 +63,59 @@ Supported commands: .It help .It list-oids .It verify +Verify a certificate and its certification path up to a trust +anchor, possibly checking CRLs. .It print +Prints a human-readable rendering of certificates in a store. +See +.Sx CERTIFICATE STORES. .It validate +Validate a certificate (but not a full chain). .It certificate-copy, cc +Copy ceritificates and possibly private keys from one store to +another. +See +.Sx CERTIFICATE STORES. .It ocsp-fetch +Fetch an OCSP response. .It ocsp-verify +Fetch an OCSP response chain. .It ocsp-print +Prints a human-readable rendering of an OCSP response chain. .It revoke-print +Prints a human-readable rendering of a CRL or OCSP response +chain. .It generate-key +Generates a private key. .It request-create +Generates a Certificate Signing Request (CSR). .It request-print +Prints a human-readable rendering of a CSR. .It query +Queries a certificate store. .It info +Prints information about supported algorithms. .It random-data +Outputs entropy using a random number generator. .It crypto-available +Tests if a cryptographic algorithm is available. .It crypto-select +Selects a supported cryptographic algorithm given a peer's +capabilities. .It hex +Hex-encode/decode utility. .It certificate-sign, cert-sign, issue-certificate, ca +Issue a certificate. .It crl-sign +Sign a CRL. +.It cms-create-sd, cms-sign +Created a CMS SignedData. +.It cms-verify-sd +Verifies a CMS SignedData. +.It cms-unenvelope +Extracts enveloped data from a CMS SignedData. +.It cms-envelope +Creates an enveloped CMS SignedData. .El Other sub-commands reported by the .Nm help @@ -89,48 +124,80 @@ sub-command are not stable or fully supported at this time. Stores of certificates and/or keys have string names that can be used with .Nm 's -commands. -Sub-commands use these certificate store names to refer to files -and tokens where keys and/or certificates are to be found or -written. +commands as well as in various configuration parameters and +command-line arguments of Heimdal's Kerberos implementation (for +PKINIT). +.Pp For example, -.Sq FILE:/path/to/some/file . +.Ql FILE:/path/to/file , +.Ql PEM-FILE:/path/to/file , +.Ql DER-FILE:/path/to/file , +etc. +See below for a full list of store types. .Pp -Use the -.Nm certificate-copy -command to copy certificates from one store to another. -This is useful for, e.g., converting DER files to PEM or -vice-versa. +A certificate store name starts with a store TYPE followed by a +colon followed by a name of form specific to that store type. +.Pp +Private keys can be stored in the same stores as the certificates +that certify their public keys. .Pp -Heimdal supports a variety of certificate and key store types: +Private keys can also be stored in separate files, but still be +referenced in one certificate store name by joining two with a +comma: +.Ql FILE:/path/to/certificate,/path/to/private/key +. +.Pp +Heimdal supports a variety of certificate and private key store +types: .Bl -tag -width Ds -offset indent .It PEM-FILE:/path -If writing, PEM will be written. -If reading, PEM will be expected. +If writing, PEM will be written (private keys may be written in +algorithm-specific formats or in PKCS#8). +If reading, PEM will be expected (private keys may be in +algorithm-specific formats or in PKCS#8). .It DER-FILE:/path If writing, DER will be written. If reading, DER will be expected. +Private keys will be in algorithm-specific formats. .It FILE:/path -If writing, PEM will be written. -If reading, PEM or DER will be detected. +If writing, PEM will be written as if +.Ql PEM-FILE +had been used. +If reading, PEM or DER will be detected and read as if +.Ql PEM-FILE +or +.Ql DER-FILE +had been used. .It PKCS12:/path -Barely supported at this time. +If writing, PKCS#12 will be written. +If reading, PKCS#12 will be expected. +Note that PKCS#12 support is currently very limited. .It DIR:/path OpenSSL-style hashed directory of trust anchors. -.It MEMORY:name -An in-memory only store, usually never used in -.NM 's -commands. .It KEYCHAIN:system-anchors On OS X this refers to the system's trust anchors. .It KEYCHAIN:FILE:/path On OS X this refers to an OS X keychain at the given path. +.It PKCS11:/path/to/shared/object[,slot=NUMBER] +Loads the given PKCS#11 provider object and uses the token at the +given slot number, or else the first token found. .It NULL: An empty store. -.It PKCS11:/path/to/shared/object,slot=NUMBER -Loads the given PKCS#11 provider object and uses the token at the -given slot number. +.It MEMORY:name +An in-memory only, ephemeral store, usually never used in +.NM 's +commands. +The MEMORY store name exists primarily for internal +.Sq hx509 +APIs. .El +.Pp +Use the +.Nm certificate-copy +command to copy certificates from one store to another. +This is useful for, e.g., converting DER files to PEM or +vice-versa, removing private keys, adding certificate chains, +and removing root certificates from chains. .Sh CERTIFICATES You can validate a certificate with the .Nm validate @@ -149,17 +216,17 @@ sub-command: .Pp Options: .Bl -tag -width Ds -offset indent -.Op Fl Fl content -.Op Fl Fl info -.Op Fl Fl never-fail -.Op Fl Fl pass=password -.Op Fl Fl raw-json +.It Fl Fl content +.It Fl Fl info +.It Fl Fl never-fail +.It Fl Fl pass=password +.It Fl Fl raw-json .El .Pp The .Fl Fl pass=password -option is for PKCS#12 and PKCS#11 stores, and if needed and not -given, will be prompted for. +option is for PKCS#8 (PEM), PKCS#12 and PKCS#11 stores, and if +needed and not given, will be prompted for. Note that it's not secure to pass passwords as command-line arguments on multi-tenant systems. .Pp @@ -176,7 +243,14 @@ sub-command will generate a key. .Sh CERTIFICATE SIGNING REQUESTS The .Nm request-create -sub-command will create a CSR. +sub-command will create a CSR, and has support for requesting +subject alternative names and extended key usage extensions. +See its +.Fl Fl help +option, and see +.Sx EXAMPLES +below. +.Pp The .Nm request-print sub-command will display a CSR. @@ -203,5 +277,104 @@ The .Nm crl-sign sub-command will add certificates to a certificate revocation list. +.Sh EXAMPLES +Generate an RSA key: +.Bd -literal -offset indent +hxtool generate-key --type=rsa --key-bits=4096 PEM-FILE:key.pem +.Ed +.Pp +Create a CSR (with an empty name) for some key: +.Bd -literal -offset indent +hxtool request-create --subject= --key=FILE:key.pem csr.der +.Ed +.Pp +Generate a key and create a CSR (with an empty name) for it: +.Bd -literal -offset indent +hxtool request-create \\ + --subject= \\ + --generate-key=rsa \\ + --key-bits=4096 \\ + --key=FILE:key.pem \\ + csr.der +.Ed +.Pp +Generate a key and create a CSR with an empty name but also +requesting a specific dNSName subject alternative name (SAN) for +it: +.Bd -literal -offset indent +hxtool request-create \\ + --subject= \\ + --generate-key=rsa \\ + --dnsname=foo.test.h5l.se \\ + --key=FILE:key.pem \\ + csr.der +.Ed +.Pp +Print a CSR: +.Bd -literal -offset indent +hxtool request-print csr.der +.Ed +which outputs: +.Bd -literal -offset indent +request print +PKCS#10 CertificationRequest: + name: + san: dNSName: foo.test.h5l.se +.Ed +.Pp +Issue a end-entity certificate for an HTTPS server given a CSR: +.Bd -literal -offset indent +hxtool issue-certificate \\ + --type=https-server \\ + --subject= \\ + --hostname=foo.test.h5l.se \\ + --ca-certificate=FILE:cacert.pem \\ + --ca-private-key=FILE:cakey.pem \\ + --req=PKCS10:csr.der \\ + --certificate=PEM-FILE:ee.pem +.Ed +.Pp +Add a chain to a PEM file: +.Bd -literal -offset indent +hxtool copy-certificiate \\ + --no-private-keys \\ + --no-root-certs \\ + FILE:ca.pem FILE:ee.pem +.Ed +.Pp +Create a self-signed end-entity certificate for an HTTPS server: +.Bd -literal -offset indent +hxtool issue-certificate \\ + --self-signed \\ + --type=https-server \\ + --subject= \\ + --hostname=foo.test.h5l.se \\ + --ca-private-key=FILE:key.pem \\ + --certificate-private-key=FILE:key.pem \\ + --certificate=PEM-FILE:cert.pem +.Ed +.Pp +Create a root certification authority certificate: +.Bd -literal -offset indent +hxtool issue-certificate \\ + --issue-ca \\ + --self-signed \\ + --subject=CN=SomeRootCA \\ + --ca-private-key=FILE:rootkey.pem \\ + --certificate=PEM-FILE:rootcert.pem +.Ed +.Pp +Create an intermediate certification authority certificate from a +CSR: +.Bd -literal -offset indent +hxtool issue-certificate \\ + --type=https-server \\ + --subject=CN=SomeIntermediateCA \\ + --ca-certificate=FILE:parent-cert.pem \\ + --ca-private-key=FILE:parent-key.pem \\ + --req=PKCS10:csr.der \\ + --certificate=PEM-FILE:intermediate.pem +.Ed +.Pp .Sh SEE ALSO .Xr openssl 1 diff --git a/third_party/heimdal/lib/hx509/hxtool.c b/third_party/heimdal/lib/hx509/hxtool.c index aa9e279c81c..866e4bfcab2 100644 --- a/third_party/heimdal/lib/hx509/hxtool.c +++ b/third_party/heimdal/lib/hx509/hxtool.c @@ -861,11 +861,26 @@ certificate_copy(struct certificate_copy_options *opt, int argc, char **argv) hx509_certs certs; hx509_lock inlock, outlock = NULL; char *sn; + int flags = 0; + int store_flags = 0; int ret; hx509_lock_init(context, &inlock); lock_strings(inlock, &opt->in_pass_strings); + if (!opt->root_certs_flag) + /* + * We're probably copying an EE cert, its issuer, and all intermediates + * up to and excluding the root. + */ + store_flags |= HX509_CERTS_STORE_NO_ROOTS; + + if (!opt->private_keys_flag) { + /* Neither read nor store private keys */ + store_flags |= HX509_CERTS_NO_PRIVATE_KEYS; + flags |= HX509_CERTS_NO_PRIVATE_KEYS; + } + if (opt->out_pass_string) { hx509_lock_init(context, &outlock); ret = hx509_lock_command_string(outlock, opt->out_pass_string); @@ -874,25 +889,53 @@ certificate_copy(struct certificate_copy_options *opt, int argc, char **argv) opt->out_pass_string, ret); } + if (argc < 2) + errx(1, "hxtool copy-certificate requires at least two positional " + "arguments"); + + /* + * The _last_ positional argument is the destination store. Because we use + * HX509_CERTS_CREATE we'll ignore its contents and then truncate to write + * it (well, if it's a file; see key store plugins). + * + * But note that the truncation doesn't happen until we call + * hx509_certs_store(), which means we still have a chance to _read_ this + * store. That means that one can write this: + * + * hxtool cc FILE:b FILE:a FILE:b + * + * to notionally append FILE:a to FILE:b. Still, we'll have an option to + * do the append anyways: + * + * hxtool cc --append FILE:a FILE:b + */ sn = fix_store_name(context, argv[argc - 1], "FILE"); ret = hx509_certs_init(context, sn, - HX509_CERTS_CREATE, inlock, &certs); + HX509_CERTS_CREATE | flags, inlock, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init %s", sn); + + if (opt->append_flag) { + /* Append == read the certs in the dst prior to doing anything else */ + ret = hx509_certs_append(context, certs, inlock, sn); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_append %s", sn); + } free(sn); + /* + * Read all the certificate stores in all but the last positional argument. + */ while(argc-- > 1) { - int retx; - sn = fix_store_name(context, argv[0], "FILE"); - retx = hx509_certs_append(context, certs, inlock, sn); - if (retx) - hx509_err(context, 1, retx, "hx509_certs_append %s", sn); + ret = hx509_certs_append(context, certs, inlock, sn); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_append %s", sn); free(sn); argv++; } - ret = hx509_certs_store(context, certs, 0, outlock); + ret = hx509_certs_store(context, certs, store_flags, outlock); if (ret) hx509_err(context, 1, ret, "hx509_certs_store"); @@ -1450,7 +1493,9 @@ request_create(struct request_create_options *opt, int argc, char **argv) opt->key_bits_integer, &signer); - hx509_request_init(context, &req); + ret = hx509_request_init(context, &req); + if (ret) + hx509_err(context, 1, ret, "Could not initialize CSR context"); if (opt->subject_string) { hx509_name name = NULL; diff --git a/third_party/heimdal/lib/hx509/ks_file.c b/third_party/heimdal/lib/hx509/ks_file.c index 880668b4561..6d8c77bd240 100644 --- a/third_party/heimdal/lib/hx509/ks_file.c +++ b/third_party/heimdal/lib/hx509/ks_file.c @@ -550,6 +550,14 @@ store_func(hx509_context context, void *ctx, hx509_cert c) heim_octet_string data; int ret = 0; + if ((sc->store_flags & HX509_CERTS_STORE_NO_ROOTS)) { + int self_signed = 0; + + ret = hx509_cert_is_self_signed(context, c, &self_signed); + if (ret || self_signed) + return ret; + } + if (hx509_cert_have_private_key_only(c)) { data.length = 0; data.data = NULL; diff --git a/third_party/heimdal/lib/hx509/ks_p12.c b/third_party/heimdal/lib/hx509/ks_p12.c index 6fd7cd10526..1e9a92a4ff5 100644 --- a/third_party/heimdal/lib/hx509/ks_p12.c +++ b/third_party/heimdal/lib/hx509/ks_p12.c @@ -91,14 +91,14 @@ keyBag_parser(hx509_context context, if (ret) return ret; - _hx509_collector_private_key_add(context, - c, - &ki.privateKeyAlgorithm, - NULL, - &ki.privateKey, - os); + ret = _hx509_collector_private_key_add(context, + c, + &ki.privateKeyAlgorithm, + NULL, + &ki.privateKey, + os); free_PKCS8PrivateKeyInfo(&ki); - return 0; + return ret; } static int @@ -525,6 +525,14 @@ store_func(hx509_context context, void *d, hx509_cert c) size_t size; int ret; + if ((ctx->store_flags & HX509_CERTS_STORE_NO_ROOTS)) { + int is_root = 0; + + ret = hx509_cert_is_root(context, c, &is_root); + if (ret || is_root) + return ret; + } + memset(&os, 0, sizeof(os)); memset(&cb, 0, sizeof(cb)); diff --git a/third_party/heimdal/lib/hx509/libhx509-exports.def b/third_party/heimdal/lib/hx509/libhx509-exports.def index 745ad5a4d80..81783ff7c34 100644 --- a/third_party/heimdal/lib/hx509/libhx509-exports.def +++ b/third_party/heimdal/lib/hx509/libhx509-exports.def @@ -19,6 +19,11 @@ EXPORTS _hx509_make_pkinit_san _hx509_map_file_os _hx509_name_from_Name + _hx509_private_key_export + _hx509_private_key_exportable + _hx509_private_key_get_internal + _hx509_private_key_oid + _hx509_private_key_ref hx509_private_key2SPKI hx509_private_key_free _hx509_private_key_ref @@ -41,6 +46,7 @@ EXPORTS _hx509_private_key_get_internal _hx509_private_key_oid _hx509_private_key_ref + hx509_request_eku_authorized_p hx509_request_free hx509_request_get_eku hx509_request_get_exts @@ -55,8 +61,11 @@ EXPORTS hx509_request_print hx509_request_set_SubjectPublicKeyInfo hx509_request_add_email + hx509_request_reject_eku + hx509_request_reject_san hx509_request_set_name hx509_request_set_ku + hx509_request_san_authorized_p hx509_request_to_pkcs10 _hx509_unmap_file_os _hx509_write_file @@ -122,6 +131,9 @@ EXPORTS hx509_cert_init hx509_cert_init_data hx509_cert_init_private_key + hx509_cert_is_ca + hx509_cert_is_root + hx509_cert_is_self_signed hx509_cert_keyusage_print hx509_cert_ref hx509_cert_set_friendly_name diff --git a/third_party/heimdal/lib/hx509/req.c b/third_party/heimdal/lib/hx509/req.c index c8fa42e5f99..d0bfe91a948 100644 --- a/third_party/heimdal/lib/hx509/req.c +++ b/third_party/heimdal/lib/hx509/req.c @@ -669,6 +669,8 @@ hx509_request_to_pkcs10(hx509_context context, Attribute *a = NULL; /* Quiet VC */ heim_any extns; + extns.data = NULL; + extns.length = 0; r.certificationRequestInfo.attributes = calloc(1, sizeof(r.certificationRequestInfo.attributes[0])); if (r.certificationRequestInfo.attributes == NULL) @@ -977,7 +979,7 @@ abitstring_check(abitstring a, size_t n, int idx) size_t bytes; if (idx >= n) - return EINVAL; + return HX509_NO_ITEM; bytes = (idx + 1) / CHAR_BIT + (((idx + 1) % CHAR_BIT) ? 1 : 0); if (a->feat_bytes < bytes) @@ -996,7 +998,7 @@ abitstring_set(abitstring a, size_t n, int idx) size_t bytes; if (idx >= n) - return EINVAL; + return HX509_NO_ITEM; bytes = n / CHAR_BIT + ((n % CHAR_BIT) ? 1 : 0); if (a->feat_bytes < bytes) { @@ -1026,7 +1028,7 @@ abitstring_reset(abitstring a, size_t n, int idx) size_t bytes; if (idx >= n) - return EINVAL; + return HX509_NO_ITEM; bytes = (idx + 1) / CHAR_BIT + (((idx + 1) % CHAR_BIT) ? 1 : 0); if (a->feat_bytes >= bytes && diff --git a/third_party/heimdal/lib/hx509/revoke.c b/third_party/heimdal/lib/hx509/revoke.c index 18b2f8f8f96..4cfdaaee48c 100644 --- a/third_party/heimdal/lib/hx509/revoke.c +++ b/third_party/heimdal/lib/hx509/revoke.c @@ -600,18 +600,15 @@ load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList FILE *f; int ret; + *t = 0; memset(crl, 0, sizeof(*crl)); - - ret = stat(path, &sb); - if (ret) - return errno; - - *t = sb.st_mtime; if ((f = fopen(path, "r")) == NULL) return errno; rk_cloexec_file(f); + if (fstat(fileno(f), &sb) == 0) + *t = sb.st_mtime; ret = hx509_pem_read(context, f, crl_parser, crl); fclose(f); diff --git a/third_party/heimdal/lib/hx509/test_req.in b/third_party/heimdal/lib/hx509/test_req.in index a070b1dd9df..9288df6738f 100644 --- a/third_party/heimdal/lib/hx509/test_req.in +++ b/third_party/heimdal/lib/hx509/test_req.in @@ -131,5 +131,33 @@ Certificate Request: email:foo@test.h5l.se, DNS:nutcracker.test.h5l.se, DNS:foo.nutcracker.test.h5l.se, othername:<unsupported>, othername:<unsupported>, Registered ID:1.2.3.4.5.6.9 Signature Algorithm: sha256WithRSAEncryption EOF - diff -w "${objdir}/expected" "${objdir}/actual" || exit 1 + if ! diff -u -w "${objdir}/expected" "${objdir}/actual"; then + cat > "$objdir/expected" <<EOF +Certificate Request: + Data: + Version: $v (0x0) + Subject: DC = se, DC = su, DC = it, CN = Love + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + ${k}Public-Key: (1024 bit) + Modulus: + 00:c2:aa:a2:42:b7:5b:99:a3:fd:ba:f0:9b:75:db: + ef:3c:9b:8c:cf:63:5f:46:d8:95:be:09:4a:a7:76: + 79:77:61:30:ef:0b:98:d2:47:ea:9c:09:b9:b9:b7: + 15:ac:4b:9c:2d:3f:f0:d9:99:9d:4d:5a:68:67:24: + 58:5e:65:60:13:9f:4d:dc:2f:03:1d:cd:e9:b6:33: + c2:5c:c6:de:c9:93:6c:ec:8d:9a:67:0e:dd:31:20: + ac:91:39:7a:c1:8f:39:65:ff:b3:1f:cf:7a:aa:79: + 8b:ed:eb:ad:a0:be:01:10:4c:5a:a7:47:1d:c6:ee: + 79:39:5c:c7:11:6c:b9:e7:2b + Exponent: 65537 (0x10001) + Attributes: + Requested Extensions: + X509v3 Extended Key Usage: critical + 1.2.3.4.5.6.7, 1.2.3.4.5.6.8, 1.2.3.4.5.6.10 + X509v3 Subject Alternative Name: + email:foo@test.h5l.se, DNS:nutcracker.test.h5l.se, DNS:foo.nutcracker.test.h5l.se, othername: 1.3.6.1.5.2.2::<unsupported>, othername: 1.3.6.1.5.2.2::<unsupported>, Registered ID:1.2.3.4.5.6.9 + Signature Algorithm: sha256WithRSAEncryption +EOF + fi fi diff --git a/third_party/heimdal/lib/hx509/version-script.map b/third_party/heimdal/lib/hx509/version-script.map index a6c81da1ce1..8f46b0ac051 100644 --- a/third_party/heimdal/lib/hx509/version-script.map +++ b/third_party/heimdal/lib/hx509/version-script.map @@ -21,6 +21,7 @@ HEIMDAL_X509_1.2 { _hx509_make_pkinit_san; _hx509_map_file_os; _hx509_name_from_Name; + _hx509_ossl_oid2nid; _hx509_private_key_export; _hx509_private_key_exportable; _hx509_private_key_get_internal; @@ -40,7 +41,11 @@ HEIMDAL_X509_1.2 { hx509_request_authorize_san; hx509_request_count_unsupported; hx509_request_count_unauthorized; + hx509_request_eku_authorized_p; hx509_request_print; + hx509_request_reject_eku; + hx509_request_reject_san; + hx509_request_san_authorized_p; hx509_request_to_pkcs10; _hx509_unmap_file_os; _hx509_write_file; @@ -108,6 +113,9 @@ HEIMDAL_X509_1.2 { hx509_cert_init; hx509_cert_init_data; hx509_cert_init_private_key; + hx509_cert_is_ca; + hx509_cert_is_root; + hx509_cert_is_self_signed; hx509_cert_keyusage_print; hx509_cert_public_encrypt; hx509_cert_ref; diff --git a/third_party/heimdal/lib/ipc/Makefile.am b/third_party/heimdal/lib/ipc/Makefile.am index 6915175618e..04ff2137434 100644 --- a/third_party/heimdal/lib/ipc/Makefile.am +++ b/third_party/heimdal/lib/ipc/Makefile.am @@ -23,6 +23,10 @@ libheim_ipcc_la_LIBADD = \ $(LIB_roken) \ $(PTHREAD_LIBADD) +if SUNOS +libheim_ipcc_la_LIBADD += -lsocket +endif + libheim_ipcs_la_LIBADD = $(libheim_ipcc_la_LIBADD) TESTS = $(check_PROGRAMS) diff --git a/third_party/heimdal/lib/ipc/server.c b/third_party/heimdal/lib/ipc/server.c index b0b2fa1cb66..5ada75af7d1 100644 --- a/third_party/heimdal/lib/ipc/server.c +++ b/third_party/heimdal/lib/ipc/server.c @@ -606,7 +606,6 @@ add_new_socket(int fd, void *userctx) { struct client *c; - int fileflags; c = calloc(1, sizeof(*c)); if (c == NULL) @@ -628,8 +627,7 @@ add_new_socket(int fd, c->callback = callback; c->userctx = userctx; - fileflags = fcntl(c->fd, F_GETFL, 0); - fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK); + socket_set_nonblocking(fd, 1); #ifdef HAVE_GCD init_globals(); @@ -1085,6 +1083,9 @@ heim_sipc_service_unix(const char *service, const char *d = secure_getenv("HEIM_IPC_DIR"); int fd, ret; + if (strncasecmp(service, "UNIX:", sizeof("UNIX:") - 1) == 0) + service += sizeof("UNIX:") - 1; + un.sun_family = AF_UNIX; if (snprintf(un.sun_path, sizeof(un.sun_path), @@ -1115,7 +1116,7 @@ heim_sipc_service_unix(const char *service, return errno; } - chmod(un.sun_path, 0666); + (void) chmod(un.sun_path, 0666); ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC, callback, user, ctx); @@ -1284,7 +1285,7 @@ heim_sipc_service_door(const char *service, ret = errno; goto cleanup; } - fchmod(dfd, 0666); /* XXX */ + (void) fchmod(dfd, 0666); /* XXX */ if (fattach(fd, path) < 0) { ret = errno; diff --git a/third_party/heimdal/lib/kadm5/Makefile.am b/third_party/heimdal/lib/kadm5/Makefile.am index a6151d51b1c..6b58c5f8fdd 100644 --- a/third_party/heimdal/lib/kadm5/Makefile.am +++ b/third_party/heimdal/lib/kadm5/Makefile.am @@ -15,7 +15,7 @@ libkadm5srv_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map endif sbin_PROGRAMS = iprop-log -check_PROGRAMS = default_keys +check_PROGRAMS = default_keys test_marshall noinst_PROGRAMS = test_pw_quality noinst_LTLIBRARIES = sample_passwd_check.la sample_hook.la @@ -37,6 +37,9 @@ libexec_PROGRAMS = ipropd-master ipropd-slave default_keys_SOURCES = default_keys.c default_keys_CPPFLAGS = -I$(srcdir)/../krb5 +test_marshall_SOURCES = marshall.c +test_marshall_CPPFLAGS = -I$(srcdir)/../krb5 -DTEST + kadm5includedir = $(includedir)/kadm5 buildkadm5include = $(buildinclude)/kadm5 diff --git a/third_party/heimdal/lib/kadm5/create_s.c b/third_party/heimdal/lib/kadm5/create_s.c index e603497ace2..26e4e4ca7e7 100644 --- a/third_party/heimdal/lib/kadm5/create_s.c +++ b/third_party/heimdal/lib/kadm5/create_s.c @@ -256,14 +256,12 @@ kadm5_s_create_principal(void *server_handle, if ((mask & KADM5_ATTRIBUTES) && (princ->attributes & (KRB5_KDB_VIRTUAL_KEYS | KRB5_KDB_VIRTUAL)) && !(princ->attributes & KRB5_KDB_MATERIALIZE)) { - ret = KADM5_DUP; /* XXX */ - goto out; + return _kadm5_error_code(KADM5_DUP); /* XXX More like EINVAL */ } if ((mask & KADM5_ATTRIBUTES) && (princ->attributes & KRB5_KDB_VIRTUAL_KEYS) && (princ->attributes & KRB5_KDB_VIRTUAL)) { - ret = KADM5_DUP; /* XXX */ - goto out; + return _kadm5_error_code(KADM5_DUP); /* XXX More like EINVAL */ } if ((mask & KADM5_ATTRIBUTES) && diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin Binary files differnew file mode 100644 index 00000000000..2c5f72c9991 --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin Binary files differnew file mode 100644 index 00000000000..ff0b705f5d7 --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin Binary files differnew file mode 100644 index 00000000000..645fea76bbe --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin Binary files differnew file mode 100644 index 00000000000..e64ec5a9a17 --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt new file mode 100644 index 00000000000..9d78e8dd07d --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt @@ -0,0 +1,101 @@ +# The body of this file contains a representation of a very small and contrived +# kadm5_principal_ent_rec meant for fuzzing. For fuzzing purposes, the smaller +# the input, the better. +# +# To compile this input into a binary input file suitable for fuzzing: +# +# cd build +# make -j4 +# cd lib/kadm5 +# make test_marshall +# ./test_marshall --in-text --byte-order-in=packed \ +# --byte-order-out=packed \ +# --out-hex \ +# kadm5_principal_ent_rec \ +# ../../../lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt +# +# then decode the hex (e.g., with xxd -r -p) and save it in a file. +# +# Currently we have that saved in lib/kadm5/fuzz-inputs-packed/. +# +# To build and fuzz with this input: +# +# cd build +# AFL_HARDEN=1 make -j4 CC=afl-clang all +# cd lib/kadm5 +# AFL_HARDEN=1 make -j4 CC=afl-clang test_marshall +# rm -rf f; mkdir f +# ../../libtool --mode=execute afl-fuzz \ +# -i ../../../lib/kadm5/fuzz-inputs-packed \ +# -o $PWD/f \ +# ./test_marshall --byte-order-in=packed \ +# --byte-order-out=packed \ +# --out-hex \ +# kadm5_principal_ent_rec '@@' +# +# A kadm5_principal_ent_rec follows: +# +# principal name +int32 0 +int32 1 +string T +string f +# expiration +int32 2 +# pw expiration +int32 3 +# last pw change +int32 4 +# max life +int32 5 +# mod name optional (boolean, principal name) +int32 1 +int32 0 +int32 1 +string T +string b +# mod time +int32 6 +# attrs +int32 7 +# kvno +int32 8 +# master kvno +int32 9 +# policy (boolean, string) +int32 1 +string default +# aux attrs +int32 10 +# max renew life +int32 11 +# last success +int32 12 +# last fail +int32 13 +# fail count +int32 14 +# nkeydata +int32 2 +# keydata[0] (ver, kvno, type, data, type, data) +int32 15 +int32 16 +int32 17 +data 1122 +int32 18 +data 2233 +# keydata[1] +int32 19 +int32 21 +int32 22 +data 3344 +int32 23 +data 4455 +# ntldata +int32 2 +# ntldata[0] (type, data) +int32 24 +data 5566 +# ntldata[1] +int32 25 +data 6677 diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt new file mode 100644 index 00000000000..332c759b946 --- /dev/null +++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt @@ -0,0 +1,54 @@ +# See lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt for instructions on how +# to fuzz krb5/kadm5 marshalling. +# +# A truncated kadm5_principal_ent_rec follows: +# +# principal name +int32 0 +int32 1 +string T +string f +# expiration +int32 2 +# pw expiration +int32 3 +# last pw change +int32 4 +# max life +int32 5 +# mod name optional (boolean, principal name) +int32 1 +int32 0 +int32 1 +string T +string b +# mod time +int32 6 +# attrs +int32 7 +# kvno +int32 8 +# master kvno +int32 9 +# policy (boolean, string) +int32 1 +string default +# aux attrs +int32 10 +# max renew life +int32 11 +# last success +int32 12 +# last fail +int32 13 +# fail count +int32 14 +# nkeydata +int32 2 +# keydata[0] (ver, kvno, type, data, type, data) +int32 15 +int32 16 +int32 17 +data 1122 +int32 18 +data 2233 diff --git a/third_party/heimdal/lib/kadm5/get_princs_c.c b/third_party/heimdal/lib/kadm5/get_princs_c.c index 93d7ce7ed1b..be86cfa579e 100644 --- a/third_party/heimdal/lib/kadm5/get_princs_c.c +++ b/third_party/heimdal/lib/kadm5/get_princs_c.c @@ -131,6 +131,7 @@ kadm5_c_iter_principals(void *server_handle, int32_t tmp; krb5_data reply; size_t i; + int stop = 0; ret = _kadm5_connect(server_handle, 0 /* want_write */); if (ret) @@ -188,7 +189,6 @@ kadm5_c_iter_principals(void *server_handle, if (tmp < 0) { size_t n = -tmp; int more = 1; - int stop = 0; /* The server supports online iteration, hooray! */ @@ -249,12 +249,6 @@ kadm5_c_iter_principals(void *server_handle, } free(princ); } - - if (!more) { - if (ret == 0) - ret = stop; - break; - } } /* Get the final result code */ krb5_data_free(&reply); @@ -293,10 +287,12 @@ kadm5_c_iter_principals(void *server_handle, } } - out: +out: krb5_clear_error_message(context->context); - out_keep_error: +out_keep_error: + if (stop) + ret = stop; krb5_storage_free(sp); krb5_data_free(&reply); return ret; diff --git a/third_party/heimdal/lib/kadm5/ipropd_master.c b/third_party/heimdal/lib/kadm5/ipropd_master.c index b3f7d8105ea..3d693bdfe0a 100644 --- a/third_party/heimdal/lib/kadm5/ipropd_master.c +++ b/third_party/heimdal/lib/kadm5/ipropd_master.c @@ -48,6 +48,7 @@ static int time_before_gone; const char *master_hostname; const char *pidfile_basename; +static char hostname[128]; static krb5_socket_t make_signal_socket (krb5_context context) @@ -275,12 +276,11 @@ static void add_slave (krb5_context context, krb5_keytab keytab, slave **root, krb5_socket_t fd) { - krb5_principal server; + krb5_principal server = NULL; krb5_error_code ret; slave *s; socklen_t addr_len; krb5_ticket *ticket = NULL; - char hostname[128]; s = calloc(1, sizeof(*s)); if (s == NULL) { @@ -301,10 +301,18 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root, goto error; } - if (master_hostname) - strlcpy(hostname, master_hostname, sizeof(hostname)); - else - gethostname(hostname, sizeof(hostname)); + /* + * We write message lengths separately from the payload, and may do + * back-to-back small writes when flushing pending input and then a new + * update. Avoid Nagle delays. + */ +#if defined(IPPROTO_TCP) && defined(TCP_NODELAY) + { + int nodelay = 1; + (void) setsockopt(s->fd, IPPROTO_TCP, TCP_NODELAY, + (void *)&nodelay, sizeof(nodelay)); + } +#endif ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, KRB5_NT_SRV_HST, &server); @@ -331,26 +339,11 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root, */ socket_set_nonblocking(s->fd, 1); - /* - * We write message lengths separately from the payload, and may do - * back-to-back small writes when flushing pending input and then a new - * update. Avoid Nagle delays. - */ -#if defined(IPPROTO_TCP) && defined(TCP_NODELAY) - { - int nodelay = 1; - (void) setsockopt(s->fd, IPPROTO_TCP, TCP_NODELAY, - (void *)&nodelay, sizeof(nodelay)); - } -#endif - - krb5_free_principal (context, server); if (ret) { krb5_warn (context, ret, "krb5_recvauth"); goto error; } ret = krb5_unparse_name (context, ticket->client, &s->name); - krb5_free_ticket (context, ticket); if (ret) { krb5_warn (context, ret, "krb5_unparse_name"); goto error; @@ -378,6 +371,8 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root, } } + krb5_free_principal(context, server); + krb5_free_ticket(context, ticket); krb5_warnx (context, "connection from %s", s->name); s->version = 0; @@ -389,6 +384,9 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root, return; error: remove_slave(context, s, root); + krb5_free_principal(context, server); + if (ticket) + krb5_free_ticket(context, ticket); } static int @@ -1197,10 +1195,11 @@ send_diffs(kadm5_server_context *server_context, slave *s, int log_fd, krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory"); return; } - krb5_store_uint32(sp, FOR_YOU); + ret = krb5_store_uint32(sp, FOR_YOU); krb5_storage_free(sp); - ret = mk_priv_tail(context, s, &data); + if (ret == 0) + ret = mk_priv_tail(context, s, &data); krb5_data_free(&data); if (ret == 0) { /* Save the fast-path continuation */ @@ -1587,6 +1586,20 @@ main(int argc, char **argv) exit(0); } + memset(hostname, 0, sizeof(hostname)); + + if (master_hostname && + strlcpy(hostname, master_hostname, + sizeof(hostname)) >= sizeof(hostname)) { + errx(1, "Hostname too long: %s", master_hostname); + } else if (master_hostname == NULL) { + if (gethostname(hostname, sizeof(hostname)) == -1) + err(1, "Could not get hostname"); + if (hostname[sizeof(hostname) - 1] != '\0') + errx(1, "Hostname too long %.*s...", + (int)sizeof(hostname), hostname); + } + if (detach_from_console && daemon_child == -1) daemon_child = roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(pidfile_basename); diff --git a/third_party/heimdal/lib/kadm5/log.c b/third_party/heimdal/lib/kadm5/log.c index 4f66426c4ca..96d063eb90e 100644 --- a/third_party/heimdal/lib/kadm5/log.c +++ b/third_party/heimdal/lib/kadm5/log.c @@ -843,19 +843,19 @@ kadm5_log_signal_master(kadm5_server_context *context) { kadm5_log_context *log_context = &context->log_context; #ifndef NO_UNIX_SOCKETS - sendto(log_context->socket_fd, - (void *)&log_context->version, - sizeof(log_context->version), - 0, - (struct sockaddr *)&log_context->socket_name, - sizeof(log_context->socket_name)); + (void) sendto(log_context->socket_fd, + (void *)&log_context->version, + sizeof(log_context->version), + 0, + (struct sockaddr *)&log_context->socket_name, + sizeof(log_context->socket_name)); #else - sendto(log_context->socket_fd, - (void *)&log_context->version, - sizeof(log_context->version), - 0, - log_context->socket_info->ai_addr, - log_context->socket_info->ai_addrlen); + (void) sendto(log_context->socket_fd, + (void *)&log_context->version, + sizeof(log_context->version), + 0, + log_context->socket_info->ai_addr, + log_context->socket_info->ai_addrlen); #endif } diff --git a/third_party/heimdal/lib/kadm5/marshall.c b/third_party/heimdal/lib/kadm5/marshall.c index 9d24233ba6e..f979ec0f5c9 100644 --- a/third_party/heimdal/lib/kadm5/marshall.c +++ b/third_party/heimdal/lib/kadm5/marshall.c @@ -371,11 +371,11 @@ ret_principal_ent(krb5_storage *sp, if (mask & KADM5_KEY_DATA) { CHECK(krb5_ret_int32(sp, &tmp)); princ->n_key_data = tmp; - princ->key_data = malloc(princ->n_key_data * sizeof(*princ->key_data)); + princ->key_data = calloc(princ->n_key_data, sizeof(*princ->key_data)); if (princ->key_data == NULL && princ->n_key_data != 0) return ENOMEM; for(i = 0; i < princ->n_key_data; i++) - ret = kadm5_ret_key_data(sp, &princ->key_data[i]); + CHECK(kadm5_ret_key_data(sp, &princ->key_data[i])); } if (mask & KADM5_TL_DATA) { CHECK(krb5_ret_int32(sp, &tmp)); @@ -472,3 +472,491 @@ _kadm5_unmarshal_params(krb5_context context, return ret; } + +#ifdef TEST +#include <getarg.h> +#include <krb5-protos.h> +#include <hex.h> + +static int version_flag; +static int help_flag; +static int verbose_flag; +static int in_text_flag = 0; +static int in_binary_flag = 0; +static int out_hex_flag = 0; +static int out_binary_flag = 0; +static int must_round_trip_flag = 0; +static char *byteorder_string_in_string; +static char *byteorder_string_out_string; +static struct getargs args[] = { + { "version", '\0', arg_flag, &version_flag, + "Version", NULL }, + { "help", '\0', arg_flag, &help_flag, + "Show this message", NULL }, + { "verbose", 'v', arg_flag, &verbose_flag, NULL, NULL }, + { "in-text", '\0', arg_flag, &in_text_flag, + "Input is a text \"recipe\"", NULL }, + { "in-binary", '\0', arg_flag, &in_binary_flag, + "Input is binary", NULL }, + { "out-hex", '\0', arg_flag, &out_hex_flag, + "Output hex", NULL }, + { "out-binary", '\0', arg_flag, &out_binary_flag, + "Output binary", NULL }, + { "must-round-trip", '\0', arg_flag, &must_round_trip_flag, + "Check that encoding and decoding round-trip", NULL }, + { "byte-order-out", '\0', arg_string, &byteorder_string_out_string, + "Output byte order", "host, network, be, or le" }, + { "byte-order-in", '\0', arg_string, &byteorder_string_in_string, + "Input byte order", "host, network, packed, be, or le" }, +}; + +#define DO_TYPE1(t, r, s) \ + if (strcmp(type, #t) == 0) { \ + t v; \ + ret = r(in, &v); \ + if (ret == 0) \ + ret = s(out, v); \ + return ret; \ + } + +#define DO_TYPE2(t, r, s) \ + if (strcmp(type, #t) == 0) { \ + t v; \ + ret = r(in, &v); \ + if (ret == 0) \ + ret = s(out, &v); \ + return ret; \ + } + +static krb5_error_code +reencode(const char *type, krb5_storage *in, krb5_storage *out) +{ + krb5_error_code ret; + + krb5_storage_seek(in, 0, SEEK_SET); + + /* + * TODO: When --verbose print a visual representation of the value. + * + * We have functionality in lib/krb5 for that for krb5_principal and + * krb5_address, but not any of the others. Adding krb5_print_*() + * and kadm5_print_*() functions just for this program to use seems + * annoying. + */ + DO_TYPE1(krb5_keyblock, krb5_ret_keyblock, krb5_store_keyblock); + DO_TYPE1(krb5_principal, krb5_ret_principal, krb5_store_principal); + DO_TYPE1(krb5_times, krb5_ret_times, krb5_store_times); + DO_TYPE1(krb5_address, krb5_ret_address, krb5_store_address); + DO_TYPE1(krb5_addresses, krb5_ret_addrs, krb5_store_addrs); + DO_TYPE1(krb5_authdata, krb5_ret_authdata, krb5_store_authdata); + + DO_TYPE2(krb5_creds, krb5_ret_creds, krb5_store_creds); + DO_TYPE2(krb5_key_data, kadm5_ret_key_data, kadm5_store_key_data); + DO_TYPE2(krb5_tl_data, kadm5_ret_tl_data, kadm5_store_tl_data); + DO_TYPE2(kadm5_principal_ent_rec, kadm5_ret_principal_ent, + kadm5_store_principal_ent); + + return ENOTSUP; +} + +static krb5_error_code +eval_recipe1(krb5_storage *sp, const char *typ, const char *val) +{ + krb5_error_code ret; + uint64_t vu = 0; + int64_t vi = 0; + int consumed = 0; + + if (strncmp(typ, "int", sizeof("int") - 1) == 0) { + if (sscanf(val, "%"PRIi64"%n", &vi, &consumed) != 1) + return EINVAL; + if (consumed < 1) + return EINVAL; + while (isspace(val[consumed])) + consumed++; + if (val[consumed] != '\0') + return EINVAL; + } else if (strncmp(typ, "uint", sizeof("uint") - 1) == 0) { + /* There's no equally-useful equivalent of %i for unsigned */ + if (val[0] == '0') { + if (val[1] == 'x') { + if (sscanf(val, "%"PRIx64"%n", &vu, &consumed) != 1) + return EINVAL; + } else { + if (sscanf(val, "%"PRIo64"%n", &vu, &consumed) != 1) + return EINVAL; + } + } else { + if (sscanf(val, "%"PRIu64"%n", &vu, &consumed) != 1) + return EINVAL; + } + if (consumed < 1) + return EINVAL; + while (isspace(val[consumed])) + consumed++; + if (val[consumed] != '\0') + return EINVAL; + vi = (int64_t)vu; + } +#define DO_INTn(n) \ + if (strcmp(typ, "int" #n) == 0) { \ + if (n < 64 && vi < INT ## n ## _MIN) \ + return EOVERFLOW; \ + if (n < 64 && vi > INT ## n ## _MAX) \ + return EOVERFLOW; \ + return krb5_store_int ## n (sp, vi); \ + } + DO_INTn(8); + DO_INTn(16); + DO_INTn(32); + DO_INTn(64); +#define DO_UINTn(n) \ + if (strcmp(typ, "uint" #n) == 0) { \ + if (n < 64 && vu > INT ## n ## _MAX) \ + return EOVERFLOW; \ + return krb5_store_int ## n (sp, vi); \ + } + DO_UINTn(8); + DO_UINTn(16); + DO_UINTn(32); + DO_UINTn(64); + if (strcmp(typ, "string") == 0) + return krb5_store_string(sp, val); + if (strcmp(typ, "stringz") == 0) + return krb5_store_stringz(sp, val); + if (strcmp(typ, "stringnl") == 0) + return krb5_store_stringnl(sp, val); + if (strcmp(typ, "data") == 0) { + ssize_t dsz = strlen(val); + krb5_data d; + + /* + * 'data' as in 'krb5_data'. + * + * krb5_store_data() stores the length then the data. + */ + if (krb5_data_alloc(&d, dsz)) + return ENOMEM; + dsz = hex_decode(val, d.data, d.length); + if (dsz < 0) + return EINVAL; + d.length = dsz; + ret = krb5_store_data(sp, d); + krb5_data_free(&d); + return ret; + } + if (strcmp(typ, "rawdata") == 0) { + ssize_t dsz = strlen(val); + void *d; + + /* Store the data w/o a length prefix */ + d = malloc(dsz); + if (d == NULL) + return ENOMEM; + dsz = hex_decode(val, d, dsz); + if (dsz < 0) + return EINVAL; + ret = krb5_store_datalen(sp, d, dsz); + free(d); + return ret; + } + return ENOTSUP; +} + +static krb5_storage * +eval_recipe(char *r, int spflags) +{ + krb5_error_code ret; + krb5_storage *sp; + unsigned int lineno = 0; + char *nxt = NULL; + char *p; + + sp = krb5_storage_emem(); + if (sp == NULL) + errx(1, "Out of memory"); + krb5_storage_set_flags(sp, spflags); + + for (p = r; p && *p; p = nxt) { + char *typ; + char *val; + + lineno++; + + /* Terminate p at \n */ + nxt = p; + do { + nxt = strpbrk(nxt, "\r\n"); + if (nxt && *nxt == '\r') { + if (*(++nxt) != '\n') + continue; + } + if (nxt && *nxt == '\n') { + *(nxt++) = '\0'; + break; + } + } while (nxt); + + while (isspace(*p)) + p++; + if (*p == '#') { + p = nxt; + continue; + } + if (*p == '\0') + continue; + typ = p; + val = strpbrk(p, " \t"); + if (val) { + *(val++) = '\0'; + while (isspace(*val)) + val++; + } + ret = eval_recipe1(sp, typ, val); + if (ret) + krb5_err(NULL, 1, ret, "Error at line %u", lineno); + } + return sp; +} + +static void +usage(int code) +{ + if (code) + dup2(STDERR_FILENO, STDOUT_FILENO); + + arg_printusage(args, sizeof(args) / sizeof(args[0]), "test_marshall", + "Usage: test_marshal [options] TYPE-NAME INPUT-FILE " + "[OUTPUT-FILE]\n" + "\tText inputs must be of the form:\n\n" + "\t\tsimpletype literalvalue\n\n" + "\twhere {simpletype} is one of:\n\n" + "\t\tint8\n" + "\t\tint16\n" + "\t\tint32\n" + "\t\tint64\n" + "\t\tuint8\n" + "\t\tuint16\n" + "\t\tuint32\n" + "\t\tuint64\n" + "\t\tstring\n" + "\t\tstringz\n" + "\t\tstringnl\n" + "\t\tdata\n" + "\t\trawdata\n\n" + "\tand {literalvalue} is as appropriate for the {simpletype}:\n\n" + "\t - For int types the value can be decimal, octal, or hexadecimal.\n" + "\t - For string types the string ends at the end of the line.\n" + "\t - For {data} the value is hex and will be encoded as a 32-bit\n" + "\t length then the raw binary data.\n" + "\t - For {rawdata} the value is hex and will be encoded as just the\n" + "\t raw binary data.\n\n" + "\tThe {TYPE} must be one of: krb5_keyblock, krb5_principal,\n" + "\tkrb5_times, krb5_address, krb5_addresses, krb5_authdata,\n" + "\tkrb5_creds, krb5_key_data, krb5_tl_data, or\n" + "\tkadm5_principal_ent_rec.\n\n" + "Options:\n"); + exit(code); +} + +static krb5_flags +byteorder_flags(const char *s) +{ + if (s == NULL) + return KRB5_STORAGE_BYTEORDER_BE; + if (strcasecmp(s, "packed") == 0) + return KRB5_STORAGE_BYTEORDER_PACKED; + if (strcasecmp(s, "host") == 0) + return KRB5_STORAGE_BYTEORDER_HOST; + if (strcasecmp(s, "network") == 0) + return KRB5_STORAGE_BYTEORDER_BE; + if (strcasecmp(s, "be") == 0) + return KRB5_STORAGE_BYTEORDER_BE; + if (strcasecmp(s, "le") == 0) + return KRB5_STORAGE_BYTEORDER_LE; + return 0; +} + +/* + * This program is intended to make fuzzing of krb5_ret_*() and kadm5_ret_*() + * possible. + * + * Inputs are either binary encodings or simplistic textual representations of + * XDR-ish data structures normally coded with {kadm5,krb5}_{ret,store}_*() + * functions. + * + * A textual representation of these structures looks like: + * + * type value + * .. + * + * where type is one of char, int32, etc., and where value is an appropriate + * literal for type. + */ +int +main(int argc, char **argv) +{ + krb5_error_code ret = 0; + krb5_storage *insp = NULL; + krb5_storage *insp2 = NULL; + krb5_storage *outsp = NULL; + krb5_flags spflags_in = 0; + krb5_flags spflags_out = 0; + krb5_data i, i2, o; + size_t insz = 0; + char *hexout = NULL; + char *hexin = NULL; + char *intxt = NULL; + void *inbin = NULL; + int optidx = 0; + + if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx)) + usage(1); + + if (help_flag) + usage(0); + + argc -= optidx; + argv += optidx; + + if (argc < 1) + errx(1, "Missing type name argument"); + if (argc < 2) + errx(1, "Missing input file argument"); + if (argc > 3) + errx(1, "Too many arguments"); + + if ((in_text_flag && in_binary_flag) || + (!in_text_flag && !in_binary_flag)) + errx(1, "One and only one of --in-text and --in-binary must be given"); + if (out_hex_flag && out_binary_flag) + errx(1, "At most one of --out-text and --out-binary must be given"); + + if (!out_hex_flag && !out_binary_flag) { + if (isatty(STDOUT_FILENO)) { + warnx("Will output hex because stdout is a terminal"); + out_hex_flag = 1; + } else { + warnx("Will output binary"); + out_binary_flag = 1; + } + } + + spflags_in |= byteorder_flags(byteorder_string_in_string); + spflags_out |= byteorder_flags(byteorder_string_out_string); + + /* Read the input */ + if (in_text_flag) + errno = rk_undumptext(argv[1], &intxt, NULL); + else + errno = rk_undumpdata(argv[1], &inbin, &insz); + if (errno) + err(1, "Could not read %s", argv[1]); + + /* If the input is a recipe, evaluate it */ + if (intxt) + insp = eval_recipe(intxt, spflags_in); + else + insp = krb5_storage_from_mem(inbin, insz); + if (insp == NULL) + errx(1, "Out of memory"); + krb5_storage_set_flags(insp, spflags_in); + + ret = krb5_storage_to_data(insp, &i); + if (ret) + krb5_err(NULL, 1, ret, "Could not check round-tripping"); + + if (out_hex_flag) { + char *hexstr = NULL; + + if (hex_encode(i.data, i.length, &hexstr) == -1) + err(1, "Could not hex-encode output"); + if (argv[2]) { + FILE *f; + + f = fopen(argv[2], "w"); + if (f == NULL) + err(1, "Could not open %s for writing", argv[2]); + if (fprintf(f, "%s\n", hexstr) < 0 || fclose(f)) + err(1, "Could write to %s", argv[2]); + } else { + if (printf("%s\n", hexstr) < 0) + err(1, "Could not write to stdout"); + } + free(hexstr); + } else { + if (argv[2]) { + rk_dumpdata(argv[2], i.data, i.length); + } else { + if (fwrite(i.data, i.length, 1, stdout) != 1 || + fflush(stdout) != 0) + err(1, "Could not output encoding"); + } + } + + outsp = krb5_storage_emem(); + if (outsp == NULL) + errx(1, "Out of memory"); + krb5_storage_set_flags(outsp, spflags_out); + + ret = reencode(argv[0], insp, outsp); + if (ret) + krb5_err(NULL, 1, ret, "Could not decode and re-encode"); + + if (i.length == o.length && memcmp(i.data, o.data, i.length) == 0) { + if (verbose_flag) + fprintf(stderr, "Encoding round-trips!\n"); + goto out; + } + + ret = krb5_storage_to_data(outsp, &o); + if (ret) + krb5_err(NULL, 1, ret, "Out of memory"); + + /* + * The encoding did not round trip. Sadly kadm5_ret_principal_ent() + * reverses the TL data list. So try to re-encode one more time. + */ + + if (strcmp(argv[0], "kadm5_principal_ent_rec") == 0) { + insp2 = krb5_storage_emem(); + if (insp2 == NULL) + errx(1, "Out of memory"); + + krb5_storage_set_flags(insp2, spflags_in); + ret = reencode(argv[0], outsp, insp2); + if (ret == 0) + ret = krb5_storage_to_data(insp2, &i2); + if (ret) + krb5_err(NULL, 1, ret, "Could not decode and re-encode"); + if (i.length == i2.length && memcmp(i.data, i2.data, i.length) == 0) { + if (verbose_flag) + fprintf(stderr, "Encoding round-trips!\n"); + goto out; + } + } + if (hex_encode(i.data, i.length, &hexin) < 0) + errx(1, "Out of memory"); + if (hex_encode(o.data, o.length, &hexout) < 0) + errx(1, "Out of memory"); + if (must_round_trip_flag) { + errx(1, "Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin, + hexout); + } else { + warnx("Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin, + hexout); + } + +out: + + free(hexin); + free(hexout); + krb5_data_free(&o); + krb5_data_free(&i); + krb5_data_free(&i2); + krb5_storage_free(insp); + krb5_storage_free(outsp); + krb5_storage_free(insp2); + return ret; +} +#endif diff --git a/third_party/heimdal/lib/kadm5/version-script-client.map b/third_party/heimdal/lib/kadm5/version-script-client.map index baae30b1173..56a4ff33511 100644 --- a/third_party/heimdal/lib/kadm5/version-script-client.map +++ b/third_party/heimdal/lib/kadm5/version-script-client.map @@ -61,6 +61,9 @@ HEIMDAL_KADM5_CLIENT_1.0 { kadm5_store_principal_ent; kadm5_store_principal_ent_mask; kadm5_store_tl_data; + _kadm5_client_recv; + _kadm5_client_send; + _kadm5_connect; local: *; }; diff --git a/third_party/heimdal/lib/krb5/aes-test.c b/third_party/heimdal/lib/krb5/aes-test.c index 01522dd593a..2d048e426e5 100644 --- a/third_party/heimdal/lib/krb5/aes-test.c +++ b/third_party/heimdal/lib/krb5/aes-test.c @@ -328,7 +328,8 @@ krb_enc(krb5_context context, } if (decrypt.length != clear->length || - memcmp(decrypt.data, clear->data, decrypt.length) != 0) { + (decrypt.length && + memcmp(decrypt.data, clear->data, decrypt.length) != 0)) { krb5_warnx(context, "clear text not same"); return EINVAL; } @@ -568,7 +569,8 @@ krb_enc_mit(krb5_context context, return ret; if (decrypt.length != clear->length || - memcmp(decrypt.data, clear->data, decrypt.length) != 0) { + (decrypt.length && + memcmp(decrypt.data, clear->data, decrypt.length) != 0)) { krb5_warnx(context, "clear text not same"); return EINVAL; } diff --git a/third_party/heimdal/lib/krb5/aname_to_localname.c b/third_party/heimdal/lib/krb5/aname_to_localname.c index 8515c306e69..307d421daa0 100644 --- a/third_party/heimdal/lib/krb5/aname_to_localname.c +++ b/third_party/heimdal/lib/krb5/aname_to_localname.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#include <string.h> #include "krb5_locl.h" #include "an2ln_plugin.h" #include "db_plugin.h" +#include <string.h> + /* Default plugin (DB using binary search of sorted text file) follows */ static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **); static void KRB5_LIB_CALL an2ln_def_plug_fini(void *); diff --git a/third_party/heimdal/lib/krb5/context.c b/third_party/heimdal/lib/krb5/context.c index d6ddd902d64..7d2ef5f68b4 100644 --- a/third_party/heimdal/lib/krb5/context.c +++ b/third_party/heimdal/lib/krb5/context.c @@ -584,7 +584,7 @@ krb5_copy_context(krb5_context context, krb5_context *out) ret = ENOMEM; } if (ret == 0 && context->configured_default_cc_name) { - free(context->configured_default_cc_name); + free(p->configured_default_cc_name); if ((p->configured_default_cc_name = strdup(context->configured_default_cc_name)) == NULL) ret = ENOMEM; diff --git a/third_party/heimdal/lib/krb5/crypto-evp.c b/third_party/heimdal/lib/krb5/crypto-evp.c index a03cdca2ebc..0ed749a243c 100644 --- a/third_party/heimdal/lib/krb5/crypto-evp.c +++ b/third_party/heimdal/lib/krb5/crypto-evp.c @@ -82,9 +82,11 @@ _krb5_evp_digest_iov(krb5_crypto crypto, if (ret != 1) goto out; + /* Minimize EVP calls by coalescing contiguous iovec elements */ for (i = 0; i < niov; i++) { if (_krb5_crypto_iov_should_sign(&iov[i])) { - if ((char *)current.data + current.length == iov[i].data.data) { + if (current.data && + (char *)current.data + current.length == iov[i].data.data) { current.length += iov[i].data.length; } else { if (current.data) { @@ -145,7 +147,8 @@ _krb5_evp_hmac_iov(krb5_context context, for (i = 0; i < niov; i++) { if (_krb5_crypto_iov_should_sign(&iov[i])) { - if ((char *)current.data + current.length == iov[i].data.data) { + if (current.data && + (char *)current.data + current.length == iov[i].data.data) { current.length += iov[i].data.length; } else { if (current.data) diff --git a/third_party/heimdal/lib/krb5/dcache.c b/third_party/heimdal/lib/krb5/dcache.c index af88aed9156..6a4a226a6c3 100644 --- a/third_party/heimdal/lib/krb5/dcache.c +++ b/third_party/heimdal/lib/krb5/dcache.c @@ -273,19 +273,18 @@ verify_directory(krb5_context context, const char *path) return EINVAL; } + /* XXX should use mkdirx_np() */ + if (rk_mkdir(path, S_IRWXU) == 0) + return 0; + if (stat(path, &sb) != 0) { if (errno == ENOENT) { - /* XXX should use mkdirx_np() */ - if (rk_mkdir(path, S_IRWXU) == 0) - return 0; - krb5_set_error_message(context, ENOENT, N_("DIR directory %s doesn't exists", ""), path); return ENOENT; } else { - int ret = errno; - krb5_set_error_message(context, ret, - N_("DIR directory %s is bad: %s", ""), path, strerror(ret)); + krb5_set_error_message(context, errno, + N_("DIR directory %s is bad: %s", ""), path, strerror(errno)); return errno; } } diff --git a/third_party/heimdal/lib/krb5/fast.c b/third_party/heimdal/lib/krb5/fast.c index 83893542d69..90133a7abc0 100644 --- a/third_party/heimdal/lib/krb5/fast.c +++ b/third_party/heimdal/lib/krb5/fast.c @@ -851,9 +851,9 @@ krb5_error_code _krb5_fast_anon_pkinit_step(krb5_context context, krb5_init_creds_context ctx, struct krb5_fast_state *state, - krb5_data *in, + const krb5_data *in, krb5_data *out, - krb5_krbhst_info *hostinfo, + krb5_realm *out_realm, unsigned int *flags) { krb5_error_code ret; @@ -864,6 +864,9 @@ _krb5_fast_anon_pkinit_step(krb5_context context, krb5_creds cred; krb5_data data = { 3, rk_UNCONST("yes") }; + krb5_data_zero(out); + *out_realm = NULL; + memset(&cred, 0, sizeof(cred)); if (state->anon_pkinit_opt == NULL) { @@ -877,7 +880,7 @@ _krb5_fast_anon_pkinit_step(krb5_context context, ret = krb5_make_principal(context, &principal, realm, KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); if (ret) - return ret; + goto out; ret = krb5_get_init_creds_opt_set_pkinit(context, state->anon_pkinit_opt, @@ -899,7 +902,7 @@ _krb5_fast_anon_pkinit_step(krb5_context context, anon_pk_ctx = state->anon_pkinit_ctx; - ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, hostinfo, flags); + ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, out_realm, flags); if (ret || (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) goto out; @@ -950,8 +953,6 @@ _krb5_fast_anon_pkinit_step(krb5_context context, krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt); state->anon_pkinit_opt = NULL; - *flags |= KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; - out: krb5_free_principal(context, principal); krb5_free_cred_contents(context, &cred); diff --git a/third_party/heimdal/lib/krb5/fcache.c b/third_party/heimdal/lib/krb5/fcache.c index 30dff35893b..20c335db3be 100644 --- a/third_party/heimdal/lib/krb5/fcache.c +++ b/third_party/heimdal/lib/krb5/fcache.c @@ -581,7 +581,7 @@ again: * locations on tmpfs "run" directories. But we don't know here * that this is the case. Thus: no hard-links, no symlinks. */ - if (sb2.st_nlink != 1) { + if (sb2.st_nlink > 1) { krb5_set_error_message(context, EPERM, N_("Refuses to open hardlinks for caches FILE:%s", ""), filename); close(fd); return EPERM; diff --git a/third_party/heimdal/lib/krb5/get_host_realm.c b/third_party/heimdal/lib/krb5/get_host_realm.c index 955d5462d41..fdf8513405f 100644 --- a/third_party/heimdal/lib/krb5/get_host_realm.c +++ b/third_party/heimdal/lib/krb5/get_host_realm.c @@ -185,20 +185,21 @@ _krb5_get_host_realm_int(krb5_context context, { const char *p, *q; const char *port; + char *freeme = NULL; krb5_boolean dns_locate_enable; krb5_error_code ret = 0; /* Strip off any trailing ":port" suffix. */ port = strchr(host, ':'); - if (port != NULL) { - host = strndup(host, port - host); + if (port != NULL && port != host && port[1] != '\0') { + host = freeme = strndup(host, port - host); if (host == NULL) return krb5_enomem(context); } dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE, "libdefaults", "dns_lookup_realm", NULL); - for (p = host; p != NULL; p = strchr (p + 1, '.')) { + for (p = host; p != NULL && p[0] != '\0'; p = strchr (p + 1, '.')) { if (config_find_realm(context, p, realms) == 0) { if (strcasecmp(*realms[0], "dns_locate") != 0) break; @@ -219,11 +220,20 @@ _krb5_get_host_realm_int(krb5_context context, /* * If 'p' is NULL, we did not find an explicit realm mapping in either the - * configuration file or DNS. Try the hostname suffix as a last resort. + * configuration file or DNS. Try the hostname suffix -upcased- as a realm + * as a last resort. * - * XXX: If we implement a KDC-specific variant of this function just for - * referrals, we could check whether we have a cross-realm TGT for the - * realm in question, and if not try the parent (loop again). + * NOTE: If we implement a KDC-specific variant of this function just for + * referrals, we could check whether we have a cross-realm TGT for the + * realm in question, and if not try the parent (loop again). Such a + * variant would have to have access to the HDB, naturally. + * + * We should start by adding an argument to this function that + * indicates whether this fallback here is desired (the KDC wouldn't + * desire it). Then when the KDC gets KRB5_ERR_HOST_REALM_UNKNOWN + * from this function, the KDC would search the HDB for cross-realm + * krbtgt principals that denote a hierarchical path to a realm that + * matches the host's domain suffix (or a suffix of it...). */ if (p == NULL) { p = strchr(host, '.'); @@ -246,9 +256,7 @@ _krb5_get_host_realm_int(krb5_context context, } } - /* If 'port' is not NULL, we have a copy of 'host' to free. */ - if (port) - free((void *)host); + free(freeme); return ret; } diff --git a/third_party/heimdal/lib/krb5/init_creds_pw.c b/third_party/heimdal/lib/krb5/init_creds_pw.c index 9257886530a..402aefd8868 100644 --- a/third_party/heimdal/lib/krb5/init_creds_pw.c +++ b/third_party/heimdal/lib/krb5/init_creds_pw.c @@ -1221,7 +1221,7 @@ pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa static krb5_error_code pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, - const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) { krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE; struct pkinit_context *pkinit_ctx = pa_ctx; @@ -1245,7 +1245,6 @@ pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_ a->req_body.realm, ctx->pk_init_ctx, rep->enc_part.etype, - hi, ctx->pk_nonce, &ctx->req_buffer, pa, @@ -1374,7 +1373,6 @@ pa_gss_step(krb5_context context, PA_DATA *pa, const AS_REQ *a, const AS_REP *rep, - const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) { @@ -1588,7 +1586,7 @@ process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_in static krb5_error_code enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, - const AS_REP *rep, const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) { struct pa_info_data paid, *ppaid; krb5_keyblock challengekey; @@ -1727,7 +1725,6 @@ static krb5_error_code enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, const AS_REP *rep, - const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) { struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; @@ -1855,8 +1852,7 @@ enc_ts_release(void *pa_ctx) static krb5_error_code pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, - const AS_REP *rep, const krb5_krbhst_info *hi, - METHOD_DATA *in_md, METHOD_DATA *out_md) + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) { size_t len = 0, length; krb5_error_code ret; @@ -1888,8 +1884,7 @@ pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_ static krb5_error_code pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, - const AS_REP *rep, const krb5_krbhst_info *hi, - METHOD_DATA *in_md, METHOD_DATA *out_md) + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) { if (ctx->runflags.allow_enc_pa_rep) return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); @@ -1904,7 +1899,6 @@ pa_fx_cookie_step(krb5_context context, PA_DATA *pa, const AS_REQ *a, const AS_REP *rep, - const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) { @@ -1944,7 +1938,7 @@ pa_fx_cookie_step(krb5_context context, typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *); typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *); typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *); -typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, const krb5_krbhst_info *, METHOD_DATA *, METHOD_DATA *); +typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, METHOD_DATA *, METHOD_DATA *); typedef void (*pa_release_f)(void *); struct patype { @@ -2145,7 +2139,7 @@ pa_announce(krb5_context context, continue; if (patypes[n].step) - patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, NULL, in_md, out_md); + patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, in_md, out_md); else ret = krb5_padata_add(context, out_md, patypes[n].type, NULL, 0); } @@ -2258,7 +2252,6 @@ pa_step(krb5_context context, krb5_init_creds_context ctx, const AS_REQ *a, const AS_REP *rep, - const krb5_krbhst_info *hi, METHOD_DATA *in_md, METHOD_DATA *out_md) { @@ -2310,7 +2303,7 @@ pa_step(krb5_context context, _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name); - ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, hi, in_md, out_md); + ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, in_md, out_md); _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret); if (ret == 0) { struct pa_auth_mech *next_pa = ctx->pa_mech->next; @@ -2386,7 +2379,7 @@ process_pa_data_to_md(krb5_context context, log_kdc_pa_types(context, in_md); - ret = pa_step(context, ctx, a, NULL, NULL, in_md, *out_md); + ret = pa_step(context, ctx, a, NULL, in_md, *out_md); if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { _krb5_debug(context, 0, "pamech need more stepping"); } else if (ret == 0) { @@ -2419,7 +2412,6 @@ process_pa_data_to_key(krb5_context context, krb5_creds *creds, AS_REQ *a, AS_REP *rep, - const krb5_krbhst_info *hi, krb5_keyblock **key) { struct pa_info_data paid, *ppaid = NULL; @@ -2446,7 +2438,7 @@ process_pa_data_to_key(krb5_context context, } } - ret = pa_step(context, ctx, a, rep, hi, rep->padata, NULL); + ret = pa_step(context, ctx, a, rep, rep->padata, NULL); if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { _krb5_debug(context, 0, "In final stretch and pa require more stepping ?"); return ret; @@ -2966,9 +2958,9 @@ available_padata_count(METHOD_DATA *md) static krb5_error_code init_creds_step(krb5_context context, krb5_init_creds_context ctx, - krb5_data *in, + const krb5_data *in, krb5_data *out, - krb5_krbhst_info *hostinfo, + krb5_realm *out_realm, unsigned int *flags) { struct timeval start_time, end_time; @@ -2981,6 +2973,7 @@ init_creds_step(krb5_context context, gettimeofday(&start_time, NULL); krb5_data_zero(out); + *out_realm = NULL; krb5_data_zero(&checksum_data); if (ctx->as_req.req_body.cname == NULL) { @@ -3055,7 +3048,7 @@ init_creds_step(krb5_context context, ret = process_pa_data_to_key(context, ctx, &ctx->cred, &ctx->as_req, &rep.kdc_rep, - hostinfo, &ctx->fast_state.reply_key); + &ctx->fast_state.reply_key); if (ret) { free_AS_REP(&rep.kdc_rep); goto out; @@ -3413,8 +3406,18 @@ init_creds_step(krb5_context context, if(len != ctx->req_buffer.length) krb5_abortx(context, "internal error in ASN.1 encoder"); - out->data = ctx->req_buffer.data; - out->length = ctx->req_buffer.length; + ret = krb5_data_copy(out, + ctx->req_buffer.data, + ctx->req_buffer.length); + if (ret) + goto out; + + *out_realm = strdup(ctx->cred.client->realm); + if (*out_realm == NULL) { + krb5_data_free(out); + ret = ENOMEM; + goto out; + } *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; @@ -3436,9 +3439,9 @@ init_creds_step(krb5_context context, * * @param context a Kerberos 5 context. * @param ctx ctx krb5_init_creds_context context. - * @param in input data from KDC, first round it should be reset by krb5_data_zer(). - * @param out reply to KDC. - * @param hostinfo KDC address info, first round it can be NULL. + * @param in input data from KDC, first round it should be reset by krb5_data_zero(). + * @param out reply to KDC. The caller needs to call krb5_data_free() + * @param out_realm the destination realm for 'out', free with krb5_xfree() * @param flags status of the round, if * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. * @@ -3451,20 +3454,22 @@ init_creds_step(krb5_context context, KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_step(krb5_context context, krb5_init_creds_context ctx, - krb5_data *in, + const krb5_data *in, krb5_data *out, - krb5_krbhst_info *hostinfo, + krb5_realm *out_realm, unsigned int *flags) { krb5_error_code ret; krb5_data empty; krb5_data_zero(&empty); + krb5_data_zero(out); + *out_realm = NULL; if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) && ctx->fast_state.armor_ccache == NULL) { ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state, - in, out, hostinfo, flags); + in, out, out_realm, flags); if (ret && (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC)) { _krb5_debug(context, 5, "Preauth failed with optimistic " "FAST, trying w/o FAST"); @@ -3472,14 +3477,13 @@ krb5_init_creds_step(krb5_context context, ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED; ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR; } else if (ret || - ((*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) || - out->length) + (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) return ret; in = ∅ } - return init_creds_step(context, ctx, in, out, hostinfo, flags); + return init_creds_step(context, ctx, in, out, out_realm, flags); } /** @@ -3672,7 +3676,6 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) { krb5_sendto_ctx stctx = NULL; - krb5_krbhst_info *hostinfo = NULL; krb5_error_code ret; krb5_data in, out; unsigned int flags = 0; @@ -3692,9 +3695,10 @@ krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) while (1) { struct timeval nstart, nend; + krb5_realm realm = NULL; flags = 0; - ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); + ret = krb5_init_creds_step(context, ctx, &in, &out, &realm, &flags); krb5_data_free(&in); if (ret) goto out; @@ -3704,8 +3708,9 @@ krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) gettimeofday(&nstart, NULL); - ret = krb5_sendto_context (context, stctx, &out, - ctx->cred.client->realm, &in); + ret = krb5_sendto_context (context, stctx, &out, realm, &in); + krb5_data_free(&out); + free(realm); if (ret) goto out; diff --git a/third_party/heimdal/lib/krb5/kcm.c b/third_party/heimdal/lib/krb5/kcm.c index f20fc51936f..17a26e34f72 100644 --- a/third_party/heimdal/lib/krb5/kcm.c +++ b/third_party/heimdal/lib/krb5/kcm.c @@ -1355,7 +1355,7 @@ _krb5_kcm_is_running(krb5_context context) krb5_ccache id = &ccdata; krb5_boolean running; - ret = kcm_alloc(context, NULL, NULL, NULL, &id); + ret = kcm_alloc(context, &krb5_kcm_ops, NULL, NULL, &id); if (ret) return 0; diff --git a/third_party/heimdal/lib/krb5/krb5.conf.5 b/third_party/heimdal/lib/krb5/krb5.conf.5 index 8a9623ecada..06d069d251a 100644 --- a/third_party/heimdal/lib/krb5/krb5.conf.5 +++ b/third_party/heimdal/lib/krb5/krb5.conf.5 @@ -175,6 +175,18 @@ EXAMPLE.COM = { delegate-destination-tgt = true } .Ed +.It Li pkinit_pool = Va HX509-STORE +This is a multi-valued parameter naming one or more stores of +intermediate certification authority (CA) certificates for the +client's end entity certificate. +.It Li pkinit_anchors = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +anchors for PKINIT KDC certificates. +.It Li pkinit_revoke = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +of CRLs for the issuers of PKINIT KDC certificates. +If no CRLs are configured, then CRLs will not be checked. +This is because hx509 currently lacks support. .El .It Li [libdefaults] .Bl -tag -width "xxx" -offset indent @@ -875,7 +887,7 @@ See the Heimdal hx509 documentation for more information. This is a multi-valued parameter naming one or more stores of intermediate certification authority (CA) certificates for the KDC's end entity certificate. -.It Li pkinit_anchors = Va HX509-STORE +.It Li pkinit_anchors = Va HX509-STORE ... This is a multi-valued parameter naming one or more stores of anchors for PKINIT client certificates. Note that the @@ -885,6 +897,12 @@ type of is also supported here. .Va DIR type stores are OpenSSL-style CA certificate hash directories. +.It Li pkinit_revoke = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +of CRLs for the issuers of PKINIT client certificates. +If no CRLs are configured, then CRLs will not be checked. +This is because the KDC will not dereference CRL distribution +points nor request OCSP responses. .It Li pkinit_kdc_ocsp = Va PATH This names a file whose contents is the DER encoding of an OCSPResponse for the KDC's end entity certificate. diff --git a/third_party/heimdal/lib/krb5/krb5_locl.h b/third_party/heimdal/lib/krb5/krb5_locl.h index 91751c1baba..1b037cdcba3 100644 --- a/third_party/heimdal/lib/krb5/krb5_locl.h +++ b/third_party/heimdal/lib/krb5/krb5_locl.h @@ -82,6 +82,7 @@ struct mbuf; #include <com_err.h> #include <heimbase.h> +#include "heimbase-atomics.h" #define HEIMDAL_TEXTDOMAIN "heimdal_krb5" diff --git a/third_party/heimdal/lib/krb5/krbhst.c b/third_party/heimdal/lib/krb5/krbhst.c index 3688d6ad7ce..6ce7b53c5b6 100644 --- a/third_party/heimdal/lib/krb5/krbhst.c +++ b/third_party/heimdal/lib/krb5/krbhst.c @@ -352,7 +352,13 @@ append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host) _krb5_free_krbhst_info(host); return; } - *kd->end = host; + /* + * We should always initialize kd->end in common_init(), but static + * analyzers may not see that we do, and the compiler might conclude + * there's UB here. + */ + if (kd->end) + *kd->end = host; kd->end = &host->next; } @@ -571,6 +577,7 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, "Realm %s needs immediate attention " "see https://icann.org/namecollision", kd->realm); + free(host); freeaddrinfo(ai); return KRB5_KDC_UNREACH; } diff --git a/third_party/heimdal/lib/krb5/libkrb5-exports.def.in b/third_party/heimdal/lib/krb5/libkrb5-exports.def.in index 191a0c48c86..3845cd73601 100644 --- a/third_party/heimdal/lib/krb5/libkrb5-exports.def.in +++ b/third_party/heimdal/lib/krb5/libkrb5-exports.def.in @@ -506,7 +506,9 @@ EXPORTS krb5_pac_get_kdc_checksum_info krb5_pac_get_types krb5_pac_init + krb5_pac_is_trusted krb5_pac_parse + krb5_pac_set_trusted krb5_pac_verify krb5_padata_add _krb5_parse_address_no_lookup diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c index 26403e665a0..a944b9391c1 100644 --- a/third_party/heimdal/lib/krb5/pac.c +++ b/third_party/heimdal/lib/krb5/pac.c @@ -85,6 +85,8 @@ struct krb5_pac_data { /* PAC_ATTRIBUTES_INFO */ uint64_t pac_attributes; + + krb5_boolean is_trusted; }; #define PAC_ALIGNMENT 8 @@ -662,6 +664,30 @@ krb5_pac_get_types(krb5_context context, * */ +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_pac_is_trusted(krb5_const_pac p) +{ + return p->is_trusted; +} + +/* + * + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_pac_set_trusted(krb5_pac p, krb5_boolean is_trusted) +{ + p->is_trusted = is_trusted; +} + +/* + * + */ + +/* + * + */ + KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_pac_free(krb5_context context, krb5_pac pac) { diff --git a/third_party/heimdal/lib/krb5/pkinit-ec.c b/third_party/heimdal/lib/krb5/pkinit-ec.c index 33bc62c8dce..34cefd506fe 100644 --- a/third_party/heimdal/lib/krb5/pkinit-ec.c +++ b/third_party/heimdal/lib/krb5/pkinit-ec.c @@ -56,6 +56,7 @@ #include <openssl/ecdh.h> #include <openssl/evp.h> #include <openssl/bn.h> +#include <openssl/dh.h> #define HEIM_NO_CRYPTO_HDRS #endif @@ -125,6 +126,9 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, if (ret) return ret; +#ifdef HAVE_OPENSSL_30 + ctx->u.eckey = EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1)); +#else ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (ctx->u.eckey == NULL) return krb5_enomem(context); @@ -132,8 +136,13 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, ret = EC_KEY_generate_key(ctx->u.eckey); if (ret != 1) return EINVAL; +#endif +#ifdef HAVE_OPENSSL_30 + xlen = i2d_PublicKey(ctx->u.eckey, NULL); +#else xlen = i2o_ECPublicKey(ctx->u.eckey, NULL); +#endif if (xlen <= 0) return EINVAL; @@ -143,7 +152,11 @@ _krb5_build_authpack_subjectPK_EC(krb5_context context, a->clientPublicValue->subjectPublicKey.data = p; +#ifdef HAVE_OPENSSL_30 + xlen = i2d_PublicKey(ctx->u.eckey, &p); +#else xlen = i2o_ECPublicKey(ctx->u.eckey, &p); +#endif if (xlen <= 0) { a->clientPublicValue->subjectPublicKey.data = NULL; free(p); @@ -171,6 +184,61 @@ _krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context, int *out_sz) { #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 + krb5_error_code ret = 0; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *template = NULL; + EVP_PKEY *public = NULL; + size_t shared_len = 0; + + if ((template = EVP_PKEY_new()) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && + EVP_PKEY_copy_parameters(template, ctx->u.eckey) != 1) + ret = krb5_enomem(context); + if (ret == 0 && (pctx = EVP_PKEY_CTX_new(ctx->u.eckey, NULL)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1) + ret = krb5_enomem(context); + if (ret == 0 && + EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1) + ret = krb5_enomem(context); + if (ret == 0 && + (public = d2i_PublicKey(EVP_PKEY_EC, &template, &in, in_sz)) == NULL) + krb5_set_error_message(context, + ret = HX509_PARSING_KEY_FAILED, + "PKINIT: Can't parse the KDC's ECDH public key"); + if (ret == 0 && + EVP_PKEY_derive_set_peer_ex(pctx, public, 1) != 1) + krb5_set_error_message(context, + ret = KRB5KRB_ERR_GENERIC, + "Could not derive ECDH shared secret for PKINIT key exchange " + "(EVP_PKEY_derive_set_peer_ex)"); + if (ret == 0 && + (EVP_PKEY_derive(pctx, NULL, &shared_len) != 1 || shared_len == 0)) + krb5_set_error_message(context, + ret = KRB5KRB_ERR_GENERIC, + "Could not derive ECDH shared secret for PKINIT key exchange " + "(EVP_PKEY_derive to get length)"); + if (ret == 0 && shared_len > INT_MAX) + krb5_set_error_message(context, + ret = KRB5KRB_ERR_GENERIC, + "Could not derive ECDH shared secret for PKINIT key exchange " + "(shared key too large)"); + if (ret == 0 && (*out = malloc(shared_len)) == NULL) + ret = krb5_enomem(context); + if (ret == 0 && EVP_PKEY_derive(pctx, *out, &shared_len) != 1) + krb5_set_error_message(context, + ret = KRB5KRB_ERR_GENERIC, + "Could not derive ECDH shared secret for PKINIT key exchange " + "(EVP_PKEY_derive)"); + if (ret == 0) + *out_sz = shared_len; + EVP_PKEY_CTX_free(pctx); // move + EVP_PKEY_free(template); + + return ret; +#else krb5_error_code ret = 0; int dh_gen_keylen; @@ -219,6 +287,7 @@ _krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context, *out_sz = dh_gen_keylen; return ret; +#endif #else krb5_set_error_message(context, ENOTSUP, N_("PKINIT: ECDH not supported", "")); @@ -230,8 +299,12 @@ void _krb5_pk_eckey_free(void *eckey) { #ifdef HAVE_HCRYPTO_W_OPENSSL +#ifdef HAVE_OPENSSL_30 + EVP_PKEY_free(eckey); +#else EC_KEY_free(eckey); #endif +#endif } #else diff --git a/third_party/heimdal/lib/krb5/pkinit.c b/third_party/heimdal/lib/krb5/pkinit.c index 0501728d3e5..2a0979b7e12 100644 --- a/third_party/heimdal/lib/krb5/pkinit.c +++ b/third_party/heimdal/lib/krb5/pkinit.c @@ -1014,7 +1014,6 @@ get_reply_key(krb5_context context, static krb5_error_code pk_verify_host(krb5_context context, const char *realm, - const krb5_krbhst_info *hi, struct krb5_pk_init_ctx_data *ctx, struct krb5_pk_cert *host) { @@ -1092,18 +1091,6 @@ pk_verify_host(krb5_context context, if (ret) return ret; - if (hi) { - ret = hx509_verify_hostname(context->hx509ctx, host->cert, - ctx->require_hostname_match, - HX509_HN_HOSTNAME, - hi->hostname, - hi->ai->ai_addr, hi->ai->ai_addrlen); - - if (ret) - krb5_set_error_message(context, ret, - N_("Address mismatch in " - "the KDC certificate", "")); - } return ret; } @@ -1115,7 +1102,6 @@ pk_rd_pa_reply_enckey(krb5_context context, const char *realm, krb5_pk_init_ctx ctx, krb5_enctype etype, - const krb5_krbhst_info *hi, unsigned nonce, const krb5_data *req_buffer, PA_DATA *pa, @@ -1219,7 +1205,7 @@ pk_rd_pa_reply_enckey(krb5_context context, if (host) { /* make sure that it is the kdc's certificate */ - ret = pk_verify_host(context, realm, hi, ctx, host); + ret = pk_verify_host(context, realm, ctx, host); if (ret) goto out; @@ -1365,7 +1351,6 @@ pk_rd_pa_reply_dh(krb5_context context, const char *realm, krb5_pk_init_ctx ctx, krb5_enctype etype, - const krb5_krbhst_info *hi, const DHNonce *c_n, const DHNonce *k_n, unsigned nonce, @@ -1407,7 +1392,7 @@ pk_rd_pa_reply_dh(krb5_context context, if (host) { /* make sure that it is the kdc's certificate */ - ret = pk_verify_host(context, realm, hi, ctx, host); + ret = pk_verify_host(context, realm, ctx, host); if (ret) goto out; @@ -1567,7 +1552,6 @@ _krb5_pk_rd_pa_reply(krb5_context context, const char *realm, void *c, krb5_enctype etype, - const krb5_krbhst_info *hi, unsigned nonce, const krb5_data *req_buffer, PA_DATA *pa, @@ -1658,14 +1642,14 @@ _krb5_pk_rd_pa_reply(krb5_context context, switch (rep.element) { case choice_PA_PK_AS_REP_dhInfo: - ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi, + ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, ctx->clientDHNonce, rep.u.dhInfo.serverDHNonce, nonce, pa, key); break; case choice_PA_PK_AS_REP_encKeyPack: ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm, - ctx, etype, hi, nonce, req_buffer, pa, key); + ctx, etype, nonce, req_buffer, pa, key); break; default: krb5_abortx(context, "pk-init as-rep case not possible to happen"); @@ -1717,7 +1701,7 @@ _krb5_pk_rd_pa_reply(krb5_context context, } ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm, - ctx, etype, hi, nonce, req_buffer, pa, key); + ctx, etype, nonce, req_buffer, pa, key); der_free_octet_string(&data); der_free_oid(&oid); @@ -2124,17 +2108,22 @@ _krb5_parse_moduli_line(krb5_context context, return ret; } +static void +free_moduli_element(struct krb5_dh_moduli *element) +{ + free(element->name); + der_free_heim_integer(&element->p); + der_free_heim_integer(&element->g); + der_free_heim_integer(&element->q); + free(element); +} + KRB5_LIB_FUNCTION void KRB5_LIB_CALL _krb5_free_moduli(struct krb5_dh_moduli **moduli) { int i; - for (i = 0; moduli[i] != NULL; i++) { - free(moduli[i]->name); - der_free_heim_integer(&moduli[i]->p); - der_free_heim_integer(&moduli[i]->g); - der_free_heim_integer(&moduli[i]->q); - free(moduli[i]); - } + for (i = 0; moduli[i] != NULL; i++) + free_moduli_element(moduli[i]); free(moduli); } @@ -2252,29 +2241,33 @@ _krb5_parse_moduli(krb5_context context, const char *file, buf[strcspn(buf, "\n")] = '\0'; lineno++; + ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element); + if (ret) + break; + if (element == NULL) + continue; + m2 = realloc(m, (n + 2) * sizeof(m[0])); if (m2 == NULL) { - _krb5_free_moduli(m); - return krb5_enomem(context); + free_moduli_element(element); + ret = krb5_enomem(context); + break; } m = m2; - m[n] = NULL; - - ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element); - if (ret) { - _krb5_free_moduli(m); - return ret; - } - if (element == NULL) - continue; - m[n] = element; m[n + 1] = NULL; n++; } + if (ret) { + _krb5_free_moduli(m); + m = NULL; + } + *moduli = m; - return 0; + + (void) fclose(f); + return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL diff --git a/third_party/heimdal/lib/krb5/principal.c b/third_party/heimdal/lib/krb5/principal.c index 91743488d9f..ec9649dfed5 100644 --- a/third_party/heimdal/lib/krb5/principal.c +++ b/third_party/heimdal/lib/krb5/principal.c @@ -1789,6 +1789,8 @@ _krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules) krb5_config_free_strings(values); if (ret) return ret; + if (*rules == NULL) + return krb5_enomem(context); if (krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults", "safe_name_canon", NULL)) @@ -1857,7 +1859,7 @@ apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules, krb5_name_canon_rule_options *rule_opts) { krb5_name_canon_rule rule = &rules[rule_idx]; - krb5_error_code ret; + krb5_error_code ret = 0; unsigned int ndots = 0; krb5_principal nss = NULL; const char *sname = NULL; @@ -1902,17 +1904,17 @@ apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules, ndots++; } if (rule->mindots > 0 && ndots < rule->mindots) - return 0; + goto out; if (ndots > rule->maxdots) - return 0; + goto out; if (rule->match_domain != NULL && !is_domain_suffix(orig_hostname, rule->match_domain)) - return 0; + goto out; if (rule->match_realm != NULL && strcmp(rule->match_realm, in_princ->realm) != 0) - return 0; + goto out; new_realm = rule->realm; switch (rule->type) { diff --git a/third_party/heimdal/lib/krb5/rd_req.c b/third_party/heimdal/lib/krb5/rd_req.c index 371037c8403..012cfefc2d8 100644 --- a/third_party/heimdal/lib/krb5/rd_req.c +++ b/third_party/heimdal/lib/krb5/rd_req.c @@ -1037,11 +1037,10 @@ krb5_rd_req_ctx(krb5_context context, goto out; } - ret = krb5_ticket_get_authorization_data_type(context, o->ticket, - KRB5_AUTHDATA_KDC_ISSUED, - NULL); - if (ret == 0) - o->ticket->client->nameattrs->kdc_issued_verified = 1; + if (krb5_ticket_get_authorization_data_type(context, o->ticket, + KRB5_AUTHDATA_KDC_ISSUED, + NULL) == 0) + o->ticket->client->nameattrs->kdc_issued_verified = 1; /* If there is a PAC, verify its server signature */ if (inctx == NULL || inctx->check_pac) { diff --git a/third_party/heimdal/lib/krb5/recvauth.c b/third_party/heimdal/lib/krb5/recvauth.c index caf1451b2b1..656378309db 100644 --- a/third_party/heimdal/lib/krb5/recvauth.c +++ b/third_party/heimdal/lib/krb5/recvauth.c @@ -160,6 +160,11 @@ krb5_recvauth_match_version(krb5_context context, return KRB5_SENDAUTH_BADAPPLVERS; } len = ntohl(len); + if (len > 1024 * 1024) { + krb5_set_error_message(context, ret = KRB5_SENDAUTH_REJECTED, + "AP-REQ too long"); + return ret; + } her_appl_version = malloc (len); if (her_appl_version == NULL) { repl = 2; diff --git a/third_party/heimdal/lib/krb5/salt-aes-sha2.c b/third_party/heimdal/lib/krb5/salt-aes-sha2.c index bfd726c34c1..bc674bd2dab 100644 --- a/third_party/heimdal/lib/krb5/salt-aes-sha2.c +++ b/third_party/heimdal/lib/krb5/salt-aes-sha2.c @@ -92,8 +92,9 @@ AES_SHA2_string_to_key(krb5_context context, goto cleanup; } memcpy(saltp.data, et->name, enctypesz); - memcpy((unsigned char *)saltp.data + enctypesz, - salt.saltvalue.data, salt.saltvalue.length); + if (salt.saltvalue.length) + memcpy((unsigned char *)saltp.data + enctypesz, + salt.saltvalue.data, salt.saltvalue.length); ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md); if (ret) diff --git a/third_party/heimdal/lib/krb5/salt-des.c b/third_party/heimdal/lib/krb5/salt-des.c index d898d6c2057..474ba5d591d 100644 --- a/third_party/heimdal/lib/krb5/salt-des.c +++ b/third_party/heimdal/lib/krb5/salt-des.c @@ -194,7 +194,8 @@ krb5_DES_string_to_key(krb5_context context, if (len > 0 && s == NULL) return krb5_enomem(context); memcpy(s, password.data, password.length); - memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length); + if (salt.saltvalue.length) + memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length); DES_string_to_key_int(s, len, &tmp); key->keytype = enctype; krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp)); diff --git a/third_party/heimdal/lib/krb5/salt-des3.c b/third_party/heimdal/lib/krb5/salt-des3.c index 8cb73cf465a..a9293ccec9c 100644 --- a/third_party/heimdal/lib/krb5/salt-des3.c +++ b/third_party/heimdal/lib/krb5/salt-des3.c @@ -113,7 +113,8 @@ DES3_string_to_key_derived(krb5_context context, if (len != 0 && s == NULL) return krb5_enomem(context); memcpy(s, password.data, password.length); - memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length); + if (salt.saltvalue.length) + memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length); ret = krb5_string_to_key_derived(context, s, len, diff --git a/third_party/heimdal/lib/krb5/scache.c b/third_party/heimdal/lib/krb5/scache.c index 7a39664946b..479c58b8ff1 100644 --- a/third_party/heimdal/lib/krb5/scache.c +++ b/third_party/heimdal/lib/krb5/scache.c @@ -1285,6 +1285,9 @@ scc_remove_cred(krb5_context context, sqlite3_finalize(stmt); + if (ret) + return ret; + ret = prepare_stmt(context, s->db, &stmt, "DELETE FROM credentials WHERE oid=?"); if (ret) diff --git a/third_party/heimdal/lib/krb5/sendauth.c b/third_party/heimdal/lib/krb5/sendauth.c index 5011c268089..a2c8aedb8ea 100644 --- a/third_party/heimdal/lib/krb5/sendauth.c +++ b/third_party/heimdal/lib/krb5/sendauth.c @@ -109,6 +109,7 @@ krb5_sendauth(krb5_context context, ssize_t sret; krb5_boolean my_ccache = FALSE; + memset(&this_cred, 0, sizeof(this_cred)); len = strlen(version) + 1; net_len = htonl(len); if (krb5_net_write (context, p_fd, &net_len, 4) != 4 @@ -159,7 +160,6 @@ krb5_sendauth(krb5_context context, } client = this_client; } - memset(&this_cred, 0, sizeof(this_cred)); this_cred.client = client; this_cred.server = server; this_cred.times.endtime = 0; @@ -185,13 +185,6 @@ krb5_sendauth(krb5_context context, creds, &ap_req); - if (out_creds) - *out_creds = creds; - else - krb5_free_creds(context, creds); - if(this_client) - krb5_free_principal(context, this_client); - if (ret) return ret; @@ -251,5 +244,14 @@ krb5_sendauth(krb5_context context, if (rep_result == NULL) krb5_free_ap_rep_enc_part (context, ignore); } - return 0; + + if (out_creds) + ret = krb5_copy_creds(context, creds, out_creds); + + this_cred.server = NULL; + if (creds == &this_cred) + krb5_free_cred_contents(context, creds); + else if (creds) + krb5_free_creds(context, creds); + return ret; } diff --git a/third_party/heimdal/lib/krb5/store.c b/third_party/heimdal/lib/krb5/store.c index 8b966f83e79..f95fd83aa95 100644 --- a/third_party/heimdal/lib/krb5/store.c +++ b/third_party/heimdal/lib/krb5/store.c @@ -300,7 +300,7 @@ krb5_storage_free(krb5_storage *sp) } /** - * Copy the contnent of storage + * Copy the content of storage to a krb5_data. * * @param sp the storage to copy to a data * @param data the copied data, free with krb5_data_free() @@ -329,9 +329,18 @@ krb5_storage_to_data(krb5_storage *sp, krb5_data *data) return ret; } if (size) { + ssize_t bytes; + sp->seek(sp, 0, SEEK_SET); - sp->fetch(sp, data->data, data->length); + bytes = sp->fetch(sp, data->data, data->length); sp->seek(sp, pos, SEEK_SET); + + /* sp->fetch() really shouldn't fail */ + if (bytes < 0) + return sp->eof_code; + + /* Maybe the underlying file (or whatever) got truncated? */ + data->length = bytes; } return 0; } @@ -528,6 +537,8 @@ krb5_ret_int(krb5_storage *sp, ret = sp->fetch(sp, v, 1); if (ret < 0) return errno; + if (ret != 1) + return sp->eof_code; len = unpack_int_length(v); if (len < 1) @@ -536,6 +547,8 @@ krb5_ret_int(krb5_storage *sp, ret = sp->fetch(sp, v + 1, len - 1); if (ret < 0) return errno; + if (ret != len - 1) + return sp->eof_code; } ret = unpack_int(v, len, &w, &len); if (ret) @@ -816,10 +829,10 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_ret_int8(krb5_storage *sp, int8_t *value) { - int ret; + ssize_t ret; ret = sp->fetch(sp, value, sizeof(*value)); - if (ret != sizeof(*value)) + if (ret < 0 || (size_t)ret != sizeof(*value)) return (ret<0)?errno:sp->eof_code; return 0; } @@ -937,7 +950,7 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_ret_data(krb5_storage *sp, krb5_data *data) { - int ret; + krb5_error_code ret; int32_t size; ret = krb5_ret_int32(sp, &size); @@ -950,8 +963,10 @@ krb5_ret_data(krb5_storage *sp, if (ret) return ret; if (size) { - ret = sp->fetch(sp, data->data, size); - if(ret != size) { + ssize_t bytes; + + bytes = sp->fetch(sp, data->data, size); + if (bytes < 0 || bytes != size) { krb5_data_free(data); return (ret < 0)? errno : sp->eof_code; } @@ -1869,7 +1884,8 @@ _krb5_ret_data_at_offset(krb5_storage *sp, sp->seek(sp, offset, SEEK_SET); size = sp->fetch(sp, data->data, length); - heim_assert(size == length, "incomplete buffer fetched"); + if (size < 0 || (size_t)size != length) + return sp->eof_code; } cleanup: @@ -1945,7 +1961,7 @@ _krb5_store_data_at_offset(krb5_storage *sp, krb5_ssize_t nbytes; off_t pos; - if (offset == (off_t)-1) { + if (offset == (size_t)-1) { if (data == NULL || data->data == NULL) { offset = 0; } else { @@ -1953,7 +1969,7 @@ _krb5_store_data_at_offset(krb5_storage *sp, offset = sp->seek(sp, 0, SEEK_END); sp->seek(sp, pos, SEEK_SET); - if (offset == (off_t)-1) + if (offset == (size_t)-1) return HEIM_ERR_NOT_SEEKABLE; } } @@ -2025,7 +2041,6 @@ _krb5_store_utf8_as_ucs2le_at_offset(krb5_storage *sp, ucs2le = NULL; ucs2le_size = 0; offset = 0; - ret = 0; } { diff --git a/third_party/heimdal/lib/krb5/store_emem.c b/third_party/heimdal/lib/krb5/store_emem.c index 6a3908f7463..daef4d793e0 100644 --- a/third_party/heimdal/lib/krb5/store_emem.c +++ b/third_party/heimdal/lib/krb5/store_emem.c @@ -84,7 +84,8 @@ emem_store(krb5_storage *sp, const void *data, size_t size) s->base = base; s->ptr = (unsigned char*)base + off; } - memmove(s->ptr, data, size); + if (size) + memmove(s->ptr, data, size); sp->seek(sp, size, SEEK_CUR); return size; } @@ -125,10 +126,17 @@ emem_trunc(krb5_storage *sp, off_t offset) * shrunk more then half of the current size, adjust buffer. */ if (offset == 0) { - free(s->base); - s->size = 0; - s->base = NULL; - s->ptr = NULL; + if (s->size > 1024) { + void *base; + + base = realloc(s->base, 1024); + if (base) { + s->base = base; + s->size = 1024; + } + } + s->len = 0; + s->ptr = s->base; } else if ((size_t)offset > s->size || (s->size / 2) > (size_t)offset) { void *base; size_t off; @@ -209,6 +217,6 @@ krb5_storage_emem(void) sp->trunc = emem_trunc; sp->fsync = NULL; sp->free = emem_free; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } diff --git a/third_party/heimdal/lib/krb5/store_fd.c b/third_party/heimdal/lib/krb5/store_fd.c index 720ed66f75b..9184e593ab6 100644 --- a/third_party/heimdal/lib/krb5/store_fd.c +++ b/third_party/heimdal/lib/krb5/store_fd.c @@ -195,6 +195,6 @@ krb5_storage_from_fd(int fd_in) sp->trunc = fd_trunc; sp->fsync = fd_sync; sp->free = fd_free; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } diff --git a/third_party/heimdal/lib/krb5/store_mem.c b/third_party/heimdal/lib/krb5/store_mem.c index ff2a570ca62..638c341a64f 100644 --- a/third_party/heimdal/lib/krb5/store_mem.c +++ b/third_party/heimdal/lib/krb5/store_mem.c @@ -147,7 +147,7 @@ krb5_storage_from_mem(void *buf, size_t len) sp->trunc = mem_trunc; sp->fsync = NULL; sp->free = NULL; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } @@ -207,6 +207,6 @@ krb5_storage_from_readonly_mem(const void *buf, size_t len) sp->trunc = mem_no_trunc; sp->fsync = NULL; sp->free = NULL; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } diff --git a/third_party/heimdal/lib/krb5/store_sock.c b/third_party/heimdal/lib/krb5/store_sock.c index fd6fe8f4c6c..72d3e9d22bd 100644 --- a/third_party/heimdal/lib/krb5/store_sock.c +++ b/third_party/heimdal/lib/krb5/store_sock.c @@ -82,10 +82,13 @@ static void socket_free(krb5_storage * sp) { int save_errno = errno; - if (rk_IS_SOCKET_ERROR(rk_closesocket(SOCK(sp)))) + if (rk_IS_SOCKET_ERROR(rk_closesocket(SOCK(sp)))) { +#ifdef WIN32 errno = rk_SOCK_ERRNO; - else +#endif + } else { errno = save_errno; + } } /** @@ -155,6 +158,6 @@ krb5_storage_from_socket(krb5_socket_t sock_in) sp->trunc = socket_trunc; sp->fsync = socket_sync; sp->free = socket_free; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } diff --git a/third_party/heimdal/lib/krb5/store_stdio.c b/third_party/heimdal/lib/krb5/store_stdio.c index dddaa924578..9244b9e7f5f 100644 --- a/third_party/heimdal/lib/krb5/store_stdio.c +++ b/third_party/heimdal/lib/krb5/store_stdio.c @@ -83,6 +83,13 @@ stdio_store(krb5_storage * sp, const void *data, size_t size) ssize_t count; size_t rem = size; + /* + * It's possible we just went from reading to writing if the file was open + * for both. Per C99 (N869 final draft) section 7.18.5.3, point 6, when + * going from reading to writing [a file opened for both] one must seek. + */ + (void) fseeko(F(sp), 0, SEEK_CUR); + /* similar pattern to net_write() to support pipes */ while (rem > 0) { count = fwrite(cbuf, 1, rem, F(sp)); @@ -259,6 +266,6 @@ krb5_storage_stdio_from_fd(int fd_in, const char *mode) sp->trunc = stdio_trunc; sp->fsync = stdio_sync; sp->free = stdio_free; - sp->max_alloc = UINT_MAX/8; + sp->max_alloc = UINT32_MAX/64; return sp; } diff --git a/third_party/heimdal/lib/krb5/version-script.map b/third_party/heimdal/lib/krb5/version-script.map index f6278e9ecbf..a81b08fa147 100644 --- a/third_party/heimdal/lib/krb5/version-script.map +++ b/third_party/heimdal/lib/krb5/version-script.map @@ -499,7 +499,9 @@ HEIMDAL_KRB5_2.0 { krb5_pac_get_kdc_checksum_info; krb5_pac_get_types; krb5_pac_init; + krb5_pac_is_trusted; krb5_pac_parse; + krb5_pac_set_trusted; krb5_pac_verify; krb5_padata_add; _krb5_parse_address_no_lookup; diff --git a/third_party/heimdal/lib/otp/otp_db.c b/third_party/heimdal/lib/otp/otp_db.c index 76daa870aa2..c7b2b763a01 100644 --- a/third_party/heimdal/lib/otp/otp_db.c +++ b/third_party/heimdal/lib/otp/otp_db.c @@ -226,7 +226,7 @@ otp_put (void *v, OtpContext *ctx) return -1; strlcpy (p, ctx->seed, rem); p += len; - rem -= len; + /* rem -= len; */ dat.dptr = buf; dat.dsize = p - buf; return dbm_store (dbm, key, dat, DBM_REPLACE); diff --git a/third_party/heimdal/lib/roken/Makefile.am b/third_party/heimdal/lib/roken/Makefile.am index f6d038f3f6e..1f530c7aee5 100644 --- a/third_party/heimdal/lib/roken/Makefile.am +++ b/third_party/heimdal/lib/roken/Makefile.am @@ -198,6 +198,9 @@ EXTRA_libroken_la_SOURCES = \ vis.hin libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile) +if SUNOS +libroken_la_LIBADD += -lnsl -lsocket +endif $(LTLIBOBJS) $(libroken_la_OBJECTS): roken.h $(XHEADERS) diff --git a/third_party/heimdal/lib/roken/base32.c b/third_party/heimdal/lib/roken/base32.c index 4ec86ae90f1..1a275321644 100644 --- a/third_party/heimdal/lib/roken/base32.c +++ b/third_party/heimdal/lib/roken/base32.c @@ -141,9 +141,7 @@ token_decode(const char *token, enum rk_base32_flags flags) int preserve_order = !!(flags & RK_BASE32_FLAG_PRESERVE_ORDER); int i, c; - if (strlen(token) < 8) - return DECODE_ERROR; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8 && token[i] != '\0'; i++) { val <<= 5; if (token[i] == '=') marker++; @@ -157,7 +155,7 @@ token_decode(const char *token, enum rk_base32_flags flags) else val |= c; } - if (marker > 6) + if (i < 8 || marker > 6) return DECODE_ERROR; return (marker << 40) | val; } diff --git a/third_party/heimdal/lib/roken/base64.c b/third_party/heimdal/lib/roken/base64.c index 582d183bcf7..1391c828079 100644 --- a/third_party/heimdal/lib/roken/base64.c +++ b/third_party/heimdal/lib/roken/base64.c @@ -45,17 +45,31 @@ #include "base64.h" #include "roken.h" -static const char base64_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define base64_chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" static int pos(char c) { +#if 'A' == '\301' const char *p; for (p = base64_chars; *p; p++) if (*p == c) return p - base64_chars; return -1; +#else + if (c >= 'A' && c <= 'Z') + return c - 'A'; + if (c >= 'a' && c <= 'z') + return ('Z' + 1 - 'A') + c - 'a'; + if (c >= '0' && c <= '9') + return ('Z' + 1 - 'A') + + ('z' + 1 - 'a') + c - '0'; + if (c == '+') + return 62; + if (c == '/') + return 63; + return -1; +#endif } ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL @@ -112,9 +126,7 @@ token_decode(const char *token) int i; unsigned int val = 0; int marker = 0; - if (strlen(token) < 4) - return DECODE_ERROR; - for (i = 0; i < 4; i++) { + for (i = 0; i < 4 && token[i] != '\0'; i++) { val *= 64; if (token[i] == '=') marker++; @@ -123,7 +135,7 @@ token_decode(const char *token) else val += pos(token[i]); } - if (marker > 2) + if (i < 4 || marker > 2) return DECODE_ERROR; return (marker << 24) | val; } @@ -135,7 +147,7 @@ rk_base64_decode(const char *str, void *data) unsigned char *q; q = data; - for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) { + for (p = str; *p && (*p == '=' || pos(*p) != -1); p += 4) { unsigned int val = token_decode(p); unsigned int marker = (val >> 24) & 0xff; if (val == DECODE_ERROR) { diff --git a/third_party/heimdal/lib/roken/closefrom.c b/third_party/heimdal/lib/roken/closefrom.c index 770eb2c67ac..1a950f7a867 100644 --- a/third_party/heimdal/lib/roken/closefrom.c +++ b/third_party/heimdal/lib/roken/closefrom.c @@ -43,7 +43,7 @@ #include "roken.h" ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL -closefrom(int fd) +rk_closefrom(int fd) { int num = getdtablesize(); diff --git a/third_party/heimdal/lib/roken/dumpdata.c b/third_party/heimdal/lib/roken/dumpdata.c index 252744e28e2..a8b3efefcff 100644 --- a/third_party/heimdal/lib/roken/dumpdata.c +++ b/third_party/heimdal/lib/roken/dumpdata.c @@ -51,8 +51,86 @@ rk_dumpdata (const char *filename, const void *buf, size_t size) close(fd); } +/* For not-regular files */ +static int +undump_not_file(int fd, char **out, size_t *size, int nul_terminate) +{ + size_t lim = 10 * 1024 * 1024; + size_t bufsz = 0; + size_t sz = 0; + char *buf = NULL; + char *tmp; + + *out = NULL; + if (size && *size != 0 && *size < lim) + lim = *size; + if (size) + *size = 0; + + /* + * We can't use net_read() if we're on WIN32 because that really wants a + * socket FD, which is in a distinct FD namespace from those returned by + * open() on Windows. + */ + do { + ssize_t bytes; + + if (sz == bufsz) { + if (bufsz == 0) + bufsz = 1024; + else + bufsz += bufsz >> 1; + + tmp = realloc(buf, bufsz); + if (tmp == NULL) { + free(buf); + return ENOMEM; + } + buf = tmp; + } + + bytes = read(fd, buf + sz, bufsz - sz); + if (bytes == 0) + break; + if (bytes < 0 && + (errno == EAGAIN || errno == EWOULDBLOCK)) + continue; + if (bytes < 0) { + free(buf); + return errno; + } + sz += bytes; + } while (sz < lim); + + *out = buf; + if (size) + *size = sz; + + if (!nul_terminate) + return 0; + + if (bufsz > sz) { + buf[sz] = '\0'; + return 0; + } + + *out = tmp = realloc(buf, bufsz + 1); + if (tmp == NULL) { + free(buf); + return ENOMEM; + } + buf = tmp; + buf[sz] = '\0'; + return 0; +} + /* - * Read all data from a filename, care about errors. + * Read all data from a file, care about errors. + * + * If `*size' is not zero and the file is not a regular file, then up to that + * many bytes will be read. + * + * Returns zero on success or a system error code on failure. */ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL @@ -71,6 +149,17 @@ rk_undumpdata(const char *filename, void **buf, size_t *size) ret = errno; goto out; } + if (!S_ISREG(sb.st_mode)) { + char *char_buf; + + ret = undump_not_file(fd, &char_buf, size, 0); + (void) close(fd); + *buf = char_buf; + return ret; + } + + if (sb.st_size < 0) + sb.st_size = 0; *buf = malloc(sb.st_size); if (*buf == NULL) { ret = ENOMEM; @@ -78,7 +167,7 @@ rk_undumpdata(const char *filename, void **buf, size_t *size) } *size = sb.st_size; - sret = net_read(fd, *buf, *size); + sret = read(fd, *buf, *size); if (sret < 0) ret = errno; else if (sret != (ssize_t)*size) @@ -94,3 +183,67 @@ rk_undumpdata(const char *filename, void **buf, size_t *size) close(fd); return ret; } + +/* + * Read all text from a file. + * + * Outputs a C string. It is up to the caller to check for embedded NULs. + * The number of bytes read will be stored in `*size' if `size' is not NULL. + * + * If `size' is not NULL and `*size' is not zero and the file is not a regular + * file, then up to that many bytes will be read. + * + * Returns zero on success or a system error code on failure. + */ + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +rk_undumptext(const char *filename, char **out, size_t *size) +{ + struct stat sb; + int fd, ret; + ssize_t sret; + char *buf; + + *out = NULL; + + fd = open(filename, O_RDONLY, 0); + if (fd < 0) + return errno; + if (fstat(fd, &sb) != 0) { + (void) close(fd); + return errno; + } + if (!S_ISREG(sb.st_mode)) { + ret = undump_not_file(fd, out, size, 1); + (void) close(fd); + return ret; + } + + if (sb.st_size < 0) + sb.st_size = 0; + buf = malloc(sb.st_size + 1); + if (buf == NULL) { + ret = ENOMEM; + goto out; + } + if (size) + *size = sb.st_size; + + sret = read(fd, buf, sb.st_size); + if (sret < 0) + ret = errno; + else if (sret != (ssize_t)sb.st_size) + ret = EINVAL; + else + ret = 0; + +out: + if (ret) { + free(buf); + } else { + buf[sb.st_size] = '\0'; + *out = buf; + } + close(fd); + return ret; +} diff --git a/third_party/heimdal/lib/roken/getauxval.c b/third_party/heimdal/lib/roken/getauxval.c index f12bc932bc3..785b1618f2f 100644 --- a/third_party/heimdal/lib/roken/getauxval.c +++ b/third_party/heimdal/lib/roken/getauxval.c @@ -159,7 +159,6 @@ rk_getprocauxval(unsigned long type) ROKEN_LIB_FUNCTION unsigned long ROKEN_LIB_CALL rk_getauxval(unsigned long type) { - const auxv_t *a; #ifdef HAVE_GETAUXVAL #ifdef GETAUXVAL_SETS_ERRNO if (rk_injected_auxv) @@ -169,6 +168,7 @@ rk_getauxval(unsigned long type) unsigned long ret; unsigned long ret2; static int getauxval_sets_errno = -1; + const auxv_t *a; int save_errno = errno; if (rk_injected_auxv) @@ -212,13 +212,21 @@ rk_getauxval(unsigned long type) getauxval_sets_errno = 0; errno = save_errno; + if ((a = rk_getauxv(type)) == NULL) { + errno = ENOENT; + return 0; + } + return a->a_un.a_val; #endif -#endif +#else + const auxv_t *a; + if ((a = rk_getauxv(type)) == NULL) { errno = ENOENT; return 0; } return a->a_un.a_val; +#endif } /** diff --git a/third_party/heimdal/lib/roken/getifaddrs.c b/third_party/heimdal/lib/roken/getifaddrs.c index 72eb5aa6ce7..3d5584adf08 100644 --- a/third_party/heimdal/lib/roken/getifaddrs.c +++ b/third_party/heimdal/lib/roken/getifaddrs.c @@ -53,6 +53,11 @@ struct mbuf; #include <ifaddrs.h> +#ifdef HAVE_IFADDRS_H +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +rk_freeifaddrs(struct ifaddrs *ifp); +#endif + #ifdef __hpux #define lifconf if_laddrconf #define lifc_len iflc_len @@ -853,7 +858,7 @@ rk_getifaddrs(struct ifaddrs **ifap) return 0; } -void ROKEN_LIB_FUNCTION +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_freeifaddrs(struct ifaddrs *ifp) { /* AF_NETLINK method uses a single allocation for all interfaces */ diff --git a/third_party/heimdal/lib/roken/hex.c b/third_party/heimdal/lib/roken/hex.c index cc47fa4d52d..5cd202603f3 100644 --- a/third_party/heimdal/lib/roken/hex.c +++ b/third_party/heimdal/lib/roken/hex.c @@ -58,13 +58,7 @@ hex_encode(const void *data, size_t size, char **str) size_t i; char *p; - /* check for overflow */ - if (size * 2 < size) { - *str = NULL; - return -1; - } - - p = malloc(size * 2 + 1); + p = calloc(size + 1, 2); if (p == NULL) { *str = NULL; return -1; diff --git a/third_party/heimdal/lib/roken/ifaddrs.hin b/third_party/heimdal/lib/roken/ifaddrs.hin index ef00b63bad6..88963912362 100644 --- a/third_party/heimdal/lib/roken/ifaddrs.hin +++ b/third_party/heimdal/lib/roken/ifaddrs.hin @@ -67,10 +67,10 @@ struct ifaddrs { #define ifa_broadaddr ifa_dstaddr #endif -int ROKEN_LIB_FUNCTION +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_getifaddrs(struct ifaddrs**); -void ROKEN_LIB_FUNCTION +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_freeifaddrs(struct ifaddrs*); #define getifaddrs(a) rk_getifaddrs(a) diff --git a/third_party/heimdal/lib/roken/issuid.c b/third_party/heimdal/lib/roken/issuid.c index 0c503a0d6b2..b9b7440351c 100644 --- a/third_party/heimdal/lib/roken/issuid.c +++ b/third_party/heimdal/lib/roken/issuid.c @@ -171,7 +171,7 @@ issuid(void) if (issetugid() == 0) return we_are_suid = 0; /* issetugid() == 1 might have been a false positive; fall through */ -#endif /* USE_RK_GETAUXVAL */ +#endif #ifdef AT_EXECFN /* @@ -186,6 +186,9 @@ issuid(void) * Also, this is technically a TOCTOU race, though for set-id * programs this is exceedingly unlikely to be an actual TOCTOU * race. + * + * TODO We should really make sure that none of the path components of the + * execpath are symlinks. */ { unsigned long p = rk_getauxval(AT_EXECPATH); @@ -267,10 +270,9 @@ issuid(void) return we_are_suid = 1; #endif -#endif /* !defined(HAVE_GETRESUID) || !defined(HAVE_GETRESGID) */ - errno = save_errno; return we_are_suid = 0; +#endif /* !defined(HAVE_GETRESUID) || !defined(HAVE_GETRESGID) */ #endif /* !defined(HAVE_ISSETUGID) */ #endif /* WIN32 */ } diff --git a/third_party/heimdal/lib/roken/mergesort_r.c b/third_party/heimdal/lib/roken/mergesort_r.c index 39b0301c454..4fbcd703327 100644 --- a/third_party/heimdal/lib/roken/mergesort_r.c +++ b/third_party/heimdal/lib/roken/mergesort_r.c @@ -100,6 +100,7 @@ mergesort_r(void *base, size_t nmemb, size_t size, cmp_t cmp, void *thunk) int big, iflag; u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; u_char *list2, *list1, *p2, *p, *last, **p1; + u_char *freeme = NULL; if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ errno = EINVAL; @@ -117,7 +118,7 @@ mergesort_r(void *base, size_t nmemb, size_t size, cmp_t cmp, void *thunk) if (!(size % ISIZE) && !(((uintptr_t)base) % ISIZE)) iflag = 1; - if ((list2 = malloc(nmemb * size + PSIZE)) == NULL) + if ((list2 = freeme = malloc(nmemb * size + PSIZE)) == NULL) return (-1); list1 = base; @@ -219,11 +220,9 @@ COPY: b = t; list2 = tp2; last = list2 + nmemb*size; } - if (base == list2) { + if (base == list2) memmove(list2, list1, nmemb*size); - list2 = list1; - } - free(list2); + free(freeme); return (0); } diff --git a/third_party/heimdal/lib/roken/mkdtemp.c b/third_party/heimdal/lib/roken/mkdtemp.c index b106add3278..c9f7463e950 100644 --- a/third_party/heimdal/lib/roken/mkdtemp.c +++ b/third_party/heimdal/lib/roken/mkdtemp.c @@ -59,9 +59,8 @@ mkdtemp(char *template) pid_t val = getpid(); for (i = 0; i < len && i < 7 && template[len - i] == 'X'; i++) { - template[len] = '0' + val % 10; + template[len - i] = '0' + val % 10; val /= 10; - len--; if (!val) val = getpid(); } diff --git a/third_party/heimdal/lib/roken/roken-common.h b/third_party/heimdal/lib/roken/roken-common.h index 035b99b8b97..906b000336c 100644 --- a/third_party/heimdal/lib/roken/roken-common.h +++ b/third_party/heimdal/lib/roken/roken-common.h @@ -509,7 +509,7 @@ free_environment(char **); #define warnerr rk_warnerr ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_warnerr(int doerrno, const char *fmt, va_list ap) - __attribute__ ((__format__ (__printf__, 2, 0))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 2, 0)); ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL rk_realloc(void *, size_t); @@ -521,7 +521,7 @@ rk_strpoolcollect(struct rk_strpool *); ROKEN_LIB_FUNCTION struct rk_strpool * ROKEN_LIB_CALL rk_strpoolprintf(struct rk_strpool *, const char *, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 2, 3)); ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_strpoolfree(struct rk_strpool *); @@ -532,6 +532,9 @@ rk_dumpdata (const char *, const void *, size_t); ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_undumpdata (const char *, void **, size_t *); +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +rk_undumptext (const char *, char **, size_t *); + ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_xfree (void *); diff --git a/third_party/heimdal/lib/roken/roken.awk b/third_party/heimdal/lib/roken/roken.awk index 47313c843af..00e60f026df 100644 --- a/third_party/heimdal/lib/roken/roken.awk +++ b/third_party/heimdal/lib/roken/roken.awk @@ -12,6 +12,14 @@ BEGIN { print "#ifdef HAVE_ERRNO_H" print "#include <errno.h>" print "#endif" + print "#if !defined(__has_extension)" + print "#define __has_extension(x) 0" + print "#endif" + print "#ifndef ROKEN_REQUIRE_GNUC" + print "#define ROKEN_REQUIRE_GNUC(m,n,p) \\" + print " (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \\" + print " (((m) * 10000) + ((n) * 100) + (p)))" + print "#endif" print "" print "int main(int argc, char **argv)" print "{" diff --git a/third_party/heimdal/lib/roken/roken.h.in b/third_party/heimdal/lib/roken/roken.h.in index e1902f582dc..c72d259a37a 100644 --- a/third_party/heimdal/lib/roken/roken.h.in +++ b/third_party/heimdal/lib/roken/roken.h.in @@ -96,6 +96,58 @@ # endif # endif +#if !defined(__has_extension) +#define __has_extension(x) 0 +#endif + +#ifndef ROKEN_REQUIRE_GNUC +#define ROKEN_REQUIRE_GNUC(m,n,p) \ + (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \ + (((m) * 10000) + ((n) * 100) + (p))) +#endif + +#ifndef ROKEN_DEPRECATED +#if __has_extension(deprecated) || ROKEN_REQUIRE_GNUC(3,1,0) +#define ROKEN_DEPRECATED __attribute__ ((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER>1200) +#define ROKEN_DEPRECATED __declspec(deprecated) +#else +#define ROKEN_DEPRECATED +#endif +#endif + +#ifndef ROKEN_PRINTF_ATTRIBUTE +#if __has_extension(format) || ROKEN_REQUIRE_GNUC(3,1,0) +#define ROKEN_PRINTF_ATTRIBUTE(x) __attribute__ ((__format__ x)) +#else +#define ROKEN_PRINTF_ATTRIBUTE(x) +#endif +#endif + +#ifndef ROKEN_NORETURN_ATTRIBUTE +#if __has_extension(noreturn) || ROKEN_REQUIRE_GNUC(3,1,0) +#define ROKEN_NORETURN_ATTRIBUTE __attribute__ ((__noreturn__)) +#else +#define ROKEN_NORETURN_ATTRIBUTE +#endif +#endif + +#ifndef ROKEN_UNUSED_ATTRIBUTE +#if __has_extension(unused) || ROKEN_REQUIRE_GNUC(3,1,0) +#define ROKEN_UNUSED_ATTRIBUTE __attribute__ ((__unused__)) +#else +#define ROKEN_UNUSED_ATTRIBUTE +#endif +#endif + +#ifndef ROKEN_WARN_UNUSED_RESULT_ATTRIBUTE +#if __has_extension(warn_unused_result) || ROKEN_REQUIRE_GNUC(3,3,0) +#define ROKEN_WARN_UNUSED_RESULT_ATTRIBUTE __attribute__ ((__warn_unused_result__)) +#else +#define ROKEN_WARN_UNUSED_RESULT_ATTRIBUTE +#endif +#endif + #ifdef HAVE_WINSOCK /* Declarations for Microsoft Windows */ @@ -515,7 +567,7 @@ ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL endusershell(void); #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_snprintf (char *, size_t, const char *, ...) - __attribute__ ((__format__ (__printf__, 3, 4))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 3, 4)); #endif #if !defined(HAVE_VSNPRINTF) || defined(NEED_VSNPRINTF_PROTO) @@ -524,7 +576,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_vsnprintf (char *, size_t, const char *, va_list) - __attribute__ ((__format__ (__printf__, 3, 0))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 3, 0)); #endif #if !defined(HAVE_ASPRINTF) || defined(NEED_ASPRINTF_PROTO) @@ -533,7 +585,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_asprintf (char **, const char *, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 2, 3)); #endif #if !defined(HAVE_VASPRINTF) || defined(NEED_VASPRINTF_PROTO) @@ -542,7 +594,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_vasprintf (char **, const char *, va_list) - __attribute__ ((__format__ (__printf__, 2, 0))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 2, 0)); #endif #if !defined(HAVE_ASNPRINTF) || defined(NEED_ASNPRINTF_PROTO) @@ -551,7 +603,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_asnprintf (char **, size_t, const char *, ...) - __attribute__ ((__format__ (__printf__, 3, 4))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 3, 4)); #endif #if !defined(HAVE_VASNPRINTF) || defined(NEED_VASNPRINTF_PROTO) @@ -560,7 +612,19 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL #endif ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL vasnprintf (char **, size_t, const char *, va_list) - __attribute__ ((__format__ (__printf__, 3, 0))); + ROKEN_PRINTF_ATTRIBUTE((__printf__, 3, 0)); +#endif + +#if !defined(HAVE_EVASPRINTF) || defined(NEED_EVASPRINTF_PROTO) +#define evasprintf rk_evasprintf +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rk_evasprintf(const char *format, va_list args); +#endif + +#if !defined(HAVE_EASPRINTF) || defined(NEED_EASPRINTF_PROTO) +#define easprintf rk_easprintf +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rk_easprintf(const char *format, ...); #endif #ifndef HAVE_STRDUP @@ -1224,9 +1288,9 @@ vis(char *, int, int, int); #if !defined(HAVE_CLOSEFROM) #define closefrom rk_closefrom -ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL -closefrom(int); #endif +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +rk_closefrom(int); #if !defined(HAVE_TIMEGM) #define timegm rk_timegm diff --git a/third_party/heimdal/lib/roken/snprintf.c b/third_party/heimdal/lib/roken/snprintf.c index c8afedb2620..0b99a4b6f3e 100644 --- a/third_party/heimdal/lib/roken/snprintf.c +++ b/third_party/heimdal/lib/roken/snprintf.c @@ -449,13 +449,16 @@ xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap) break; case 'd' : case 'i' : { - longest arg; - u_longest num; + int64_t arg; + uint64_t num; int minusp = 0; PARSE_INT_FORMAT(arg, ap, signed); - if (arg < 0) { + if (arg == INT64_MIN) { + minusp = 1; + num = (uint64_t)INT64_MAX + 1; + } else if (arg < 0) { minusp = 1; num = -arg; } else @@ -698,3 +701,30 @@ rk_vsnprintf (char *str, size_t sz, const char *format, va_list args) return ret; } #endif + +#if !defined(HAVE_EVASPRINTF) || defined(TEST_SNPRINTF) +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rk_evasprintf(const char *format, va_list args) +{ + char *s = NULL; + + if (vasprintf(&s, format, args) == -1 || s == NULL) + errx(1, "Out of memory"); + return s; +} +#endif + +#if !defined(HAVE_EASPRINTF) || defined(TEST_SNPRINTF) +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rk_easprintf(const char *format, ...) +{ + va_list args; + char *s = NULL; + + va_start(args, format); + if (vasprintf(&s, format, args) == -1 || s == NULL) + errx(1, "Out of memory"); + va_end(args); + return s; +} +#endif diff --git a/third_party/heimdal/lib/roken/socket.c b/third_party/heimdal/lib/roken/socket.c index a790e082d82..5b578c89fac 100644 --- a/third_party/heimdal/lib/roken/socket.c +++ b/third_party/heimdal/lib/roken/socket.c @@ -274,10 +274,11 @@ socket_set_nonblocking(rk_socket_t sock, int nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; - fcntl(sock, F_SETFL, flags); + (void) fcntl(sock, F_SETFL, flags); #elif defined(FIOBIO) int flags = !!nonblock; - return ioctl(sock, FIOBIO, &flags); + + (void) ioctl(sock, FIOBIO, &flags); #endif } diff --git a/third_party/heimdal/lib/roken/test-auxval.c b/third_party/heimdal/lib/roken/test-auxval.c index 69ab359920b..80ad9e08588 100644 --- a/third_party/heimdal/lib/roken/test-auxval.c +++ b/third_party/heimdal/lib/roken/test-auxval.c @@ -47,20 +47,19 @@ static void check_secure_getenv(char **env) { size_t i; - char *v; + char *v, *p; for (i = 0; env[i] != NULL; i++) { - if (strchr(env[i], '=') == NULL) - continue; if ((v = strdup(env[i])) == NULL) err(1, "could not allocate copy of %s", env[i]); - *strchr(v, '=') = '\0'; - if (issuid() && rk_secure_getenv(v) != NULL) - err(1, "rk_secure_getenv() returned non-NULL when issuid()!"); - if (!issuid() && rk_secure_getenv(v) == NULL) - err(1, "rk_secure_getenv() returned NULL when !issuid()"); + if ((p = strchr(v, '='))) { + *p = '\0'; + if (issuid() && rk_secure_getenv(v) != NULL) + err(1, "rk_secure_getenv() returned non-NULL when issuid()!"); + if (!issuid() && rk_secure_getenv(v) == NULL) + err(1, "rk_secure_getenv() returned NULL when !issuid()"); + } free(v); - return; } } diff --git a/third_party/heimdal/lib/roken/timeval.c b/third_party/heimdal/lib/roken/timeval.c index 3012513ee7d..71fdd009ff2 100644 --- a/third_party/heimdal/lib/roken/timeval.c +++ b/third_party/heimdal/lib/roken/timeval.c @@ -84,8 +84,8 @@ rk_time_add(time_t t, time_t delta) #else #error "Unexpected sizeof(time_t)" #endif -#endif return t + delta; +#endif } ROKEN_LIB_FUNCTION time_t ROKEN_LIB_CALL @@ -122,8 +122,8 @@ rk_time_sub(time_t t, time_t delta) /* Both t and delta are non-negative. */ if (delta > t) return 0; -#endif return t - delta; +#endif } /* diff --git a/third_party/heimdal/lib/roken/version-script.map b/third_party/heimdal/lib/roken/version-script.map index be1713e8261..b48e06b9e6a 100644 --- a/third_party/heimdal/lib/roken/version-script.map +++ b/third_party/heimdal/lib/roken/version-script.map @@ -52,12 +52,14 @@ HEIMDAL_ROKEN_2.0 { rk_dns_string_to_type; rk_dns_type_to_string; rk_dumpdata; + rk_easprintf; rk_ecalloc; rk_emalloc; rk_eread; rk_erealloc; rk_esetenv; rk_estrdup; + rk_evasprintf; rk_ewrite; rk_flock; rk_fnmatch; @@ -169,6 +171,7 @@ HEIMDAL_ROKEN_2.0 { rk_tsearch; rk_twalk; rk_undumpdata; + rk_undumptext; rk_unparse_flags; rk_unparse_time; rk_unparse_time_approx; diff --git a/third_party/heimdal/lib/roken/write_pid.c b/third_party/heimdal/lib/roken/write_pid.c index cf5299b64bf..c01091236f2 100644 --- a/third_party/heimdal/lib/roken/write_pid.c +++ b/third_party/heimdal/lib/roken/write_pid.c @@ -43,6 +43,8 @@ ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL pid_file_write(const char *progname) { const char *pidfile_dir = NULL; + const char *sep = "/"; + size_t pidfile_dir_len; char *ret = NULL; FILE *fp; @@ -57,7 +59,17 @@ pid_file_write(const char *progname) if (pidfile_dir == NULL) pidfile_dir = _PATH_VARRUN; - if (asprintf(&ret, "%s%s.pid", pidfile_dir, progname) < 0 || ret == NULL) + pidfile_dir_len = strlen(pidfile_dir); + if (pidfile_dir_len > 1 && + pidfile_dir[pidfile_dir_len - 1] == '/') + sep = ""; +#ifdef WIN32 + if (pidfile_dir_len > 1 && + pidfile_dir[pidfile_dir_len - 1] == '\\') + sep = ""; +#endif + + if (asprintf(&ret, "%s%s%s.pid", pidfile_dir, sep, progname) < 0 || ret == NULL) return NULL; fp = fopen(ret, "w"); if (fp == NULL) { diff --git a/third_party/heimdal/lib/sl/sl.c b/third_party/heimdal/lib/sl/sl.c index b78f9f675b2..c67cc525f33 100644 --- a/third_party/heimdal/lib/sl/sl.c +++ b/third_party/heimdal/lib/sl/sl.c @@ -311,13 +311,42 @@ int sl_command_loop(SL_cmd *cmds, const char *prompt, void **data) { int ret = 0; - char *buf; + char *buf = NULL; int argc; char **argv; + int continued = 0; - buf = sl_readline(prompt); - if(buf == NULL) - return -2; + do { + char *buf2; + size_t len; + + buf2 = sl_readline(buf == NULL ? prompt : "> "); + if (buf2 == NULL) { + free(buf); + return -2; + } + + if (buf) { + char *tmp = NULL; + + if (asprintf(&tmp, "%s %s", buf, buf2) == -1 || tmp == NULL) { + fprintf(stderr, "sl_loop: out of memory\n"); + free(buf2); + free(buf); + return -1; + } + free(buf2); + free(buf); + buf = tmp; + } else { + buf = buf2; + } + + len = strlen(buf); + continued = (len > 0 && buf[len - 1] == '\\'); + if (continued) + buf[len - 1] = '\0'; + } while (continued); if(*buf) add_history(buf); @@ -331,7 +360,7 @@ sl_command_loop(SL_cmd *cmds, const char *prompt, void **data) ret = sl_command(cmds, argc, argv); if(ret == -1) { sl_did_you_mean(cmds, argv[0]); - ret = 0; + ret = 1; } } free(buf); diff --git a/third_party/heimdal/lib/wind/idn-lookup.c b/third_party/heimdal/lib/wind/idn-lookup.c index 378c912a392..5b71a8fed7f 100644 --- a/third_party/heimdal/lib/wind/idn-lookup.c +++ b/third_party/heimdal/lib/wind/idn-lookup.c @@ -69,12 +69,17 @@ lookup(const char *name) struct addrinfo *ai; size_t u_len = strlen(name); - uint32_t *u = malloc(u_len * sizeof(uint32_t)); - size_t norm_len = u_len * 2; - uint32_t *norm = malloc(norm_len * sizeof(uint32_t)); + uint32_t *u; + size_t norm_len = u_len * 8; + uint32_t *norm; + if (u_len == 0) + return; + + u = calloc(u_len, sizeof(uint32_t)); if (u == NULL && u_len != 0) errx(1, "malloc failed"); + norm = calloc(norm_len, sizeof(uint32_t)); if (norm == NULL && norm_len != 0) errx(1, "malloc failed"); @@ -156,9 +161,7 @@ main(int argc, char **argv) if (argc == 0) usage(1); - for (i = 0; i < argc; ++i) { - if (argv[i][0]) /* Quiet lint */ - lookup(argv[i]); - } + for (i = 0; i < argc; ++i) + lookup(argv[i]); return 0; } diff --git a/third_party/heimdal/packages/windows/sdk/NTMakefile b/third_party/heimdal/packages/windows/sdk/NTMakefile index 4f2d355bf61..db3af6a87bd 100644 --- a/third_party/heimdal/packages/windows/sdk/NTMakefile +++ b/third_party/heimdal/packages/windows/sdk/NTMakefile @@ -93,7 +93,6 @@ INCFILES=\ $(SDKINCDIR)\hcrypto\evp-hcrypto.h \ $(SDKINCDIR)\hcrypto\evp.h \ $(SDKINCDIR)\hcrypto\hmac.h \ - $(SDKINCDIR)\hcrypto\md2.h \ $(SDKINCDIR)\hcrypto\md4.h \ $(SDKINCDIR)\hcrypto\md5.h \ $(SDKINCDIR)\hcrypto\rand.h \ diff --git a/third_party/heimdal/tests/bin/setup-env.in b/third_party/heimdal/tests/bin/setup-env.in index c9291d08bf2..8efa0e9922b 100644 --- a/third_party/heimdal/tests/bin/setup-env.in +++ b/third_party/heimdal/tests/bin/setup-env.in @@ -13,6 +13,7 @@ top_builddir="@top_builddir@" top_srcdir="@top_srcdir@" EGREP="@EGREP@" NO_AFS="@NO_AFS@" +MITKRB5="@MITKRB5@" # Meant to be sourced (source or .) by the tester application, offers # most commands in heimdal as variables diff --git a/third_party/heimdal/tests/kdc/Makefile.am b/third_party/heimdal/tests/kdc/Makefile.am index f61a7e85307..09f695348e9 100644 --- a/third_party/heimdal/tests/kdc/Makefile.am +++ b/third_party/heimdal/tests/kdc/Makefile.am @@ -80,7 +80,9 @@ do_subst = $(heim_verbose)sed $(do_dlopen) \ -e 's,[@]db_type[@],$(db_type),g' \ -e 's,[@]max_life_from_cert[@],$(pkinit_ticket_max_life_from_cert),g' \ -e 's,[@]ENABLE_AFS_STRING_TO_KEY[@],$(ENABLE_AFS_STRING_TO_KEY),' \ - -e 's,[@]EGREP[@],$(EGREP),g' + -e 's,[@]ENABLE_AFS_STRING_TO_KEY[@],$(ENABLE_AFS_STRING_TO_KEY),' \ + -e 's,[@]EGREP[@],$(EGREP),g' \ + -e 's,[@]MITKRB5[@],$(MITKRB5),g' chmod = chmod @@ -311,7 +313,7 @@ krb5-pkinit-win.conf: krb5-pkinit.conf.in Makefile mv krb5-pkinit-win.conf.tmp krb5-pkinit-win.conf clean: clean-am - rm -rf cc_dir simple_csr_authz + rm -rf cc_dir authz_dir CLEANFILES= \ $(TESTS) \ diff --git a/third_party/heimdal/tests/kdc/check-bx509.in b/third_party/heimdal/tests/kdc/check-bx509.in index 5109854fc26..d1e63741e1a 100644 --- a/third_party/heimdal/tests/kdc/check-bx509.in +++ b/third_party/heimdal/tests/kdc/check-bx509.in @@ -74,11 +74,18 @@ klistjson="${klist} --json -c $cache" klist="${klist} --hidden -v -c $cache" kgetcred="${kgetcred} -c $cache" kdestroy="${kdestroy} -c $cache ${afs_no_unlog}" +test_csr_authorizer="$test_csr_authorizer -A $objdir/authz_dir -S $objdir" kx509="${kx509} -c $cache" KRB5_CONFIG="${objdir}/krb5-bx509.conf" export KRB5_CONFIG +HEIM_PIDFILE_DIR="${objdir}/" +export HEIM_PIDFILE_DIR + +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR + rsa=yes pkinit=no if ${hxtool} info | grep 'rsa: hx509 null RSA' > /dev/null ; then @@ -102,26 +109,26 @@ rm -f current-db* rm -f out-* rm -f mkey.file* rm -f *.pem *.crt *.der -rm -rf simple_csr_authz +rm -rf authz_dir -mkdir -p simple_csr_authz +mkdir -p authz_dir > messages.log -# We'll avoid using a KDC for now. For testing /bx509 we only need keys for -# Negotiate tokens, and we'll use ktutil and kimpersonate to make it possible -# to create and accept those without a KDC. When we test /bnegotiate, however, -# we'll start a KDC. +kdcpid= +bx509pid= +test_csr_authorizer_pid= +trap 'kill -9 ${kdcpid} ${bx509pid} ${test_csr_authorizer_pid}; echo signal killing kdc, bx509d, and test_csr_authorizer; exit 1;' EXIT # csr_grant ext-type value grantee_principal csr_grant() { - mkdir -p "${objdir}/simple_csr_authz/${3}" - touch "${objdir}/simple_csr_authz/${3}/${1}-${2}" + mkdir -p "${objdir}/authz_dir/${3}" + touch "${objdir}/authz_dir/${3}/${1}=${2}" } csr_revoke() { - rm -rf "${objdir}/simple_csr_authz" - mkdir -p "${objdir}/simple_csr_authz" + rm -rf "${objdir}/authz_dir" + mkdir -p "${objdir}/authz_dir" } # get_cert "" curl-opts @@ -254,7 +261,7 @@ $hxtool ca --issue-ca --type=https-negotiate-server \ # XXX Before starting bx509d let us use kdc test programs to check that: # # - the negotiate token validator plugin works -# - the simple CSR authorizer plugin works +# - the authz_dir CSR authorizer plugin works # - the KDC CA tester program works echo "Check gss-token and Negotiate token validator plugin" @@ -265,6 +272,64 @@ token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) $test_token_validator -a datan.test.h5l.se Negotiate "$token" || { echo "Negotiate token validator failed to validate valid token"; exit 2; } + +echo "Starting CSR authorizer IPC service" +$test_csr_authorizer --server --daemon || + { echo "Failed to start test_csr_authorizer service"; exit 2; } +test_csr_authorizer_pid=`getpid test_csr_authorizer` + +# Make a CSR for foo@$R +$hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" --kerberos=foo@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=foo@$R)" +csr_revoke +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service granted foo@$R"; exit 2; } + +echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=foo@$R)" +csr_grant san_pkinit foo@$R foo@${R} +$test_csr_authorizer PKCS10:${objdir}/req foo@$R || + { echo "CSR authorizer IPC service rejected foo@$R"; exit 2; } + +# Make a CSR for bar@$R +$hxtool request-create --subject='' --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" --kerberos=bar@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +echo "Test CSR authorizer IPC service (deny foo@$R to san_pkinit=bar@$R)" +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } + +echo "Test CSR authorizer IPC service (grant foo@$R to san_pkinit=bar@$R)" +csr_grant san_pkinit foo@$R bar@${R} +$test_csr_authorizer PKCS10:${objdir}/req foo@$R && + { echo "CSR authorizer IPC service accepted foo@$R"; exit 2; } + +# Make a CSR for foo@$R and bar@$R +$hxtool request-create --subject='' --key-bits=1024 \ + --key=FILE:"${objdir}/k.der" \ + --kerberos=foo@$R --kerberos=bar@$R \ + ${objdir}/req || + { echo "Failed to make a CSR"; exit 2; } + +# Check that the authorizer does mark foo@$R as approved even though it denies +# the overall request because it rejects bar@$R +echo "Test CSR authorizer IPC service (partial authz)" +csr_revoke +csr_grant san_pkinit foo@$R foo@${R} +# Check that the authorizer grants foo@$R +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R || + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } +# Check that the authorizer rejects bar@$R +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=bar@$R && + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } +$test_csr_authorizer PKCS10:${objdir}/req foo@$R san_pkinit=foo@$R san_pkinit=bar@$R && + { echo "CSR authorizer IPC service partial approval check fail"; exit 2; } + echo "Making a plain CSR" $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ --key=FILE:"${objdir}/k.der" "${objdir}/req" || @@ -297,7 +362,7 @@ $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem && { echo "Trivial offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } -csr_grant dnsname foo.test.h5l.se foo@${R} +csr_grant san_dnsname foo.test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.1 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/server.pem || @@ -316,7 +381,7 @@ $hxtool request-create --subject='' --generate-key=rsa --key-bits=1024 \ $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem && { echo "Offline CA test failed: unauthorized issuance (dNSName)"; exit 2; } -csr_grant email foo@test.h5l.se foo@${R} +csr_grant san_email foo@test.h5l.se foo@${R} csr_grant eku 1.3.6.1.5.5.7.3.2 foo@${R} $test_kdc_ca -a bx509 foo@${R} PKCS10:${objdir}/req \ PEM-FILE:${objdir}/email.pem || @@ -329,30 +394,46 @@ $hxtool acert --expr="%{certificate.subject} == \"OU=Users,CN=KDC,$DCs\"" \ if ! which curl; then echo "curl is not available -- not testing bx509d" + sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 + trap '' EXIT exit 77 fi if ! test -x ${objdir}/../../kdc/bx509d; then echo "Configured w/o libmicrohttpd -- not testing bx509d" + sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 + trap '' EXIT exit 77 fi echo "Creating database" -${kadmin} init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R} || exit 1 -${kadmin} add -r --use-defaults foo@${R} || exit 1 -${kadmin} add -r --use-defaults bar@${R} || exit 1 -${kadmin} add -r --use-defaults baz@${R} || exit 1 -${kadmin} modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} || exit 1 +rm -f $kt $ukt +${kadmin} <<EOF || exit 1 +init --realm-max-ticket-life=1day --realm-max-renewable-life=1month ${R} +add -r --use-defaults foo@${R} +add -r --use-defaults bar@${R} +add -r --use-defaults baz@${R} +add -r --use-defaults raz@${R} +modify --pkinit-acl="CN=foo,DC=test,DC=h5l,DC=se" foo@${R} +add -r --use-defaults HTTP/${server}@${R} +ext_keytab -r -k $keytab HTTP/${server}@${R} +add -r --use-defaults HTTP/${otherserver}@${R} +ext_keytab -r -k $ukeytab foo@${R} +EOF + +echo "Starting kdc"; +${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; } +kdcpid=`getpid kdc` + +${kdestroy} +${kinit} -kt $ukeytab foo@${R} || exit 1 +$klist || { echo "failed to kinit"; exit 2; } echo "Starting bx509d" ${bx509d} --daemon || { echo "bx509 failed to start"; exit 2; } bx509pid=`getpid bx509d` -trap 'kill -9 ${bx509pid}; echo signal killing bx509d; exit 1;' EXIT ec=0 rm -f trivial.pem server.pem email.pem @@ -458,7 +539,7 @@ else fi echo "Fetching a server certificate with one dNSName SAN" -csr_grant dnsname $server foo@${R} +csr_grant san_dnsname $server foo@${R} if (set -vx; get_cert "&dNSName=$server" -sf -o "${objdir}/server.pem"); then $hxtool print --content "FILE:${objdir}/server.pem" if (set -vx; $hxtool acert --expr="%{certificate.subject} == \"\"" \ @@ -478,7 +559,7 @@ else fi echo "Fetching a server certificate with two dNSName SANs" -csr_grant dnsname "second-$server" foo@${R} +csr_grant san_dnsname "second-$server" foo@${R} if (set -vx; get_cert "&dNSName=${server}&dNSName=second-$server" -sf \ -o "${objdir}/server2.pem"); then @@ -502,7 +583,7 @@ else fi echo "Fetching an email certificate" -csr_grant email foo@bar.example foo@${R} +csr_grant san_email foo@bar.example foo@${R} if (set -vx; get_cert "&rfc822Name=foo@bar.example" -sf -o "${objdir}/email.pem"); then $hxtool print --content "FILE:${objdir}/email.pem" if $hxtool acert --end-entity -P "foo@${R}" "FILE:${objdir}/email.pem"; then @@ -521,22 +602,6 @@ else exit 1 fi -# Need to start a KDC to test this. -rm -f $kt $ukt -${kdestroy} -${kadmin} add -r --use-defaults HTTP/${server}@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab HTTP/${server}@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/${otherserver}@${R} || exit 1 -${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1 - -echo "Starting kdc"; -${kdc} --detach --testing || { echo "kdc failed to start"; cat messages.log; exit 1; } -kdcpid=`getpid kdc` -trap 'kill -9 ${kdcpid} ${bx509pid}; echo signal killing kdc and bx509d; exit 1;' EXIT - -${kinit} -kt $ukeytab foo@${R} || exit 1 -$klist || { echo "failed to kinit"; exit 2; } - echo "Fetch TGT (not granted for other)" token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) if (set -vx; @@ -549,8 +614,8 @@ if (set -vx; fi echo "Fetch TGT" -(set -vx; csr_grant pkinit foo@${R} foo@${R}) -(set -vx; csr_grant eku 1.3.6.1.5.2.3.4 foo@${R}) +csr_grant san_pkinit foo@${R} foo@${R} +csr_grant eku 1.3.6.1.5.2.3.4 foo@${R} token=$(KRB5CCNAME=$cache $gsstoken HTTP@$server) if ! (set -vx; curl -o "${cachefile2}" -Lgsf \ @@ -581,7 +646,7 @@ ${klist} | grep Addresses:.IPv4:8.8.8.8 || { echo "Failed to get a TGT with /get-tgt end-point with addresses"; exit 2; } echo "Fetch TGT (for other)" -(set -vx; csr_grant pkinit bar@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -599,7 +664,7 @@ ${klist} | grep Addresses:.IPv4:8.8.8.8 || echo "Fetch TGT (for other, w/ lifetime req under max)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(set -vx; csr_grant pkinit bar@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -625,7 +690,7 @@ fi echo "Fetch TGT (for other, w/ lifetime req over max)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(set -vx; csr_grant pkinit bar@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -640,10 +705,10 @@ ${kgetcred} -H HTTP/${server}@${R} || { echo "Fetched TGT didn't work"; exit 2; } if which jq >/dev/null; then if ! ${klistjson} | jq -e ' - (reduce (.tickets[0]|(.Issued,.Expires)| - strptime("%b %e %H:%M:%S %Y")|mktime) as $t - (0; if .==0 then $t else $t - . end) / 86400) | floor | - . == 5'; then + (.tickets[0].Issued | strptime("%b %e %H:%M:%S %Y")|mktime) as $iat + | (.tickets[0].Expires | strptime("%b %e %H:%M:%S %Y")|mktime) as $exp + | (($exp - $iat) / 86400) as $life_days + | ($life_days > 4 and $life_days <= 5)'; then echo "Incorrect lifetime" exit 2 fi @@ -651,7 +716,7 @@ fi echo "Fetch TGT (for other, w/ lifetime req under max)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(set -vx; csr_grant pkinit bar@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -677,22 +742,115 @@ fi echo "Fetch TGTs (batch, authz fail)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(set -vx; csr_grant pkinit bar@${R} foo@${R}) +csr_revoke ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) +rm -f "${cachefile}.json" if (set -vx; curl -o "${cachefile}.json" -Lgsf \ --resolve ${server}:${bx509port}:127.0.0.1 \ -H "Authorization: Negotiate $token" \ "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=baz@${R}"); then + # 200 Ok is not a problem. We have to check that the result is sane. + true +else + if grep ccache "${cachefile}.json"; then + echo "Got TGTs with /get-tgts end-point that should have been denied" + exit 2; + fi + if ! grep error_code "${cachefile}.json" > /dev/null; then + cat "${cachefile}.json" + echo "Request failed w/o error information" + exit 2; + fi +fi +cat "${cachefile}.json" +if grep ccache "${cachefile}.json"; then echo "Got TGTs with /get-tgts end-point that should have been denied" + exit 2; +fi + +echo "Fetch TGTs (batch, partial authz with IPC authorizer)" +${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} +csr_revoke +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} +${kdestroy} +token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) +if ! (set -vx; + curl -vvvo "${cachefile}.json" -Lgsf \ + --resolve ${server}:${bx509port}:127.0.0.1 \ + -H "Authorization: Negotiate $token" \ + "http://${server}:${bx509port}/get-tgts?cname=bar@${R}&cname=raz@${R}&cname=baz@${R}"); then + echo "Failed to get TGTs batch including non-existent principal" + exit 2 +fi +if which jq >/dev/null; then + set -vx + jq -e . "${cachefile}.json" > /dev/null || + { echo "/get-tgts produced non-JSON"; exit 2; } + jq -es '.[]|select(.name|startswith("raz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for raz@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("raz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for raz@${R}!"; exit 2; } + + # Check bar@$R's tickets: + jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } + + # Check baz@$R's tickets: + jq -r 'select(.name|startswith("baz@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p baz@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } +fi + +echo "Fetch TGTs (batch, partial authz)" +${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} +csr_revoke +csr_grant san_pkinit bar@${R} foo@${R} +${kdestroy} +token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) +if ! (set -vx; + curl -vvvo "${cachefile}.json" -Lgsf \ + --resolve ${server}:${bx509port}:127.0.0.1 \ + -H "Authorization: Negotiate $token" \ + "http://${server}:${bx509port}/get-tgts?cname=not@${R}&cname=bar@${R}&cname=baz@${R}"); then + echo "Failed to get TGTs batch including non-existent principal" exit 2 fi +if which jq >/dev/null; then + set -vx + jq -e . "${cachefile}.json" > /dev/null || + { echo "/get-tgts produced non-JSON"; exit 2; } + jq -es '.[]|select(.name|startswith("not@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for not@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("not@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for not@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("baz@"))|(.error_code//empty)' "${cachefile}.json" > /dev/null || + { echo "No error was reported for baz@${R}!"; exit 2; } + jq -es '.[]|select(.name|startswith("baz@"))|(.ccache//"")|(length==0)' "${cachefile}.json" > /dev/null || + { echo "Non-empty ccache included for baz@${R}!"; exit 2; } + + # Check bar@$R's tickets: + jq -r 'select(.name|startswith("bar@")).ccache' "${cachefile}.json" | + $rkbase64 -d -- - > "${cachefile}" + ${kgetcred} -H HTTP/${server}@${R} || + { echo "Fetched TGT didn't work"; exit 2; } + ${klistjson} | jq -e --arg p bar@$R '.principal == $p' > /dev/null || + { echo "/get-tgts produced wrong TGTs"; exit 2; } +fi echo "Fetch TGTs (batch, authz pass)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(csr_grant pkinit bar@${R} foo@${R}) -(csr_grant pkinit baz@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -726,9 +884,9 @@ fi echo "Fetch TGTs (batch, authz pass, one non-existent principal)" ${kadmin} modify --max-ticket-life=10d krbtgt/${R}@${R} -(csr_grant pkinit bar@${R} foo@${R}) -(csr_grant pkinit baz@${R} foo@${R}) -(csr_grant pkinit not@${R} foo@${R}) +csr_grant san_pkinit bar@${R} foo@${R} +csr_grant san_pkinit baz@${R} foo@${R} +csr_grant san_pkinit not@${R} foo@${R} ${kdestroy} token=$(KRB5CCNAME=$cache2 $gsstoken HTTP@$server) if ! (set -vx; @@ -913,9 +1071,10 @@ else exit 1 fi -echo "killing kdc (${kdcpid}) and bx509d (${bx509pid})" +echo "killing kdc (${kdcpid}) and bx509d (${bx509pid}) and test_csr_authorizer (${test_csr_authorizer_pid})" sh ${leaks_kill} kdc $kdcpid || ec=1 sh ${leaks_kill} bx509d $bx509pid || ec=1 +sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 trap "" EXIT diff --git a/third_party/heimdal/tests/kdc/check-fast.in b/third_party/heimdal/tests/kdc/check-fast.in index 3fbda813a63..d1683f2e750 100644 --- a/third_party/heimdal/tests/kdc/check-fast.in +++ b/third_party/heimdal/tests/kdc/check-fast.in @@ -171,9 +171,7 @@ done # Use MIT client tools # -mit=/usr/local/mitkerberos/bin - -if [ -f ${mit}/kinit ] ; then +if [ -n "$MITKRB5" -a -f "${MITKRB5}/kinit" ] ; then echo "Running MIT FAST tests" kinitpty=${objdir}/foopassword.rkpty @@ -183,25 +181,25 @@ password foo\n EOF echo "Acquire host ticket"; > messages.log - ${rkpty} ${kinitpty} ${mit}/kinit -c ${acache} ${server}@${R} >/dev/null|| { exit 1; } + ${rkpty} ${kinitpty} "${MITKRB5}/kinit" -c ${acache} ${server}@${R} >/dev/null|| { exit 1; } (${aklist} | grep ${server} > /dev/null ) || { exit 1; } echo "Checking for FAST avail"; > messages.log ${aklist} --hidden | grep fast_avail > /dev/null || { exit 1; } echo "Using plain to get a initial ticket"; > messages.log - ${rkpty} ${kinitpty} ${mit}/kinit -c ${cache} foo@${R} >/dev/null|| { exit 1; } + ${rkpty} ${kinitpty} "${MITKRB5}/kinit" -c ${cache} foo@${R} >/dev/null|| { exit 1; } (${klist} | grep foo > /dev/null ) || { exit 1; } echo "Using FAST to get a initial ticket"; > messages.log - ${rkpty} ${kinitpty} ${mit}/kinit -c ${cache} -T ${acache} foo@${R} >/dev/null || { exit 1; } + ${rkpty} ${kinitpty} "${MITKRB5}/kinit" -c ${cache} -T ${acache} foo@${R} >/dev/null || { exit 1; } (${klist} | grep foo > /dev/null ) || { exit 1; } echo "Checking for FAST avail"; > messages.log ${klist} --hidden | grep fast_avail > /dev/null || { exit 1; } echo "Getting service ticket"; > messages.log - ${mit}/kvno -c ${cache} ${server}@${R} || { exit 1; } + "${MITKRB5}/kvno" -c ${cache} ${server}@${R} || { exit 1; } fi diff --git a/third_party/heimdal/tests/kdc/check-httpkadmind.in b/third_party/heimdal/tests/kdc/check-httpkadmind.in index 816f753d079..9707fc14b55 100644 --- a/third_party/heimdal/tests/kdc/check-httpkadmind.in +++ b/third_party/heimdal/tests/kdc/check-httpkadmind.in @@ -97,15 +97,19 @@ KRB5_CONFIG="${objdir}/krb5-httpkadmind.conf" export KRB5_CONFIG KRB5CCNAME=$cache export KRB5CCNAME +HEIM_PIDFILE_DIR=$objdir +export HEIM_PIDFILE_DIR +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR rm -f current-db* rm -f out-* rm -f mkey.file* rm -f *.pem *.crt *.der -rm -rf simple_csr_authz +rm -rf authz_dir rm -f extracted_keytab* -mkdir -p simple_csr_authz +mkdir -p authz_dir > messages.log @@ -115,13 +119,13 @@ mkdir -p simple_csr_authz # grant ext-type value grantee_principal grant() { - mkdir -p "${objdir}/simple_csr_authz/${3}" - touch "${objdir}/simple_csr_authz/${3}/${1}-${2}" + mkdir -p "${objdir}/authz_dir/${3}" + touch "${objdir}/authz_dir/${3}/${1}=${2}" } revoke() { - rm -rf "${objdir}/simple_csr_authz" - mkdir -p "${objdir}/simple_csr_authz" + rm -rf "${objdir}/authz_dir" + mkdir -p "${objdir}/authz_dir" } if set -o|grep 'verbose.*on' > /dev/null || @@ -204,15 +208,18 @@ get_keytab_POST_redir() { kdcpid= httpkadmindpid= httpkadmind2pid= +test_csr_authorizer_pid= kadmindpid= kadmind2pid= cleanup() { test -n "$kdcpid" && { echo signal killing kdc; kill -9 "$kdcpid"; } + test -n "$test_csr_authorizer_pid" && + { echo signal killing test_csr_authorizer; kill -9 "$test_csr_authorizer_pid"; } test -n "$httpkadmindpid" && { echo signal killing httpkadmind; kill -9 "$httpkadmindpid"; } test -n "$httpkadmind2pid" && - { echo signal killing httpkadmind; kill -9 "$httpkadmind2pid"; } + { echo signal killing second httpkadmind; kill -9 "$httpkadmind2pid"; } test -n "$kadmindpid" && { echo signal killing kadmind; kill -9 "$kadmindpid"; } test -n "$kadmind2pid" && @@ -224,29 +231,28 @@ rm -f extracted_keytab echo "Creating database" rm -f $kt $ukt -${kadmin} init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R} || exit 1 -${kadmin} add -r --use-defaults foo@${R} || exit 1 -${kadmin} add -r --use-defaults httpkadmind/admin@${R} || exit 1 -${kadmin} add -r --use-defaults WELLKNOWN/CSRFTOKEN@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/localhost@${R} || exit 1 -${kadmin} add -r --use-defaults host/xyz.${domain}@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/xyz.${domain}@${R} || exit 1 -${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ - --max-ticket-life=1d --max-renewable-life=5d \ - --attributes= HTTP/ns.${domain}@${R} || exit 1 -${kadmin} add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ - --max-ticket-life=1d --max-renewable-life=5d \ - --attributes=ok-as-delegate host/.ns2.${domain}@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/${server}@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab kadmin/admin@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab httpkadmind/admin@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab HTTP/${server}@${R} || exit 1 -${kadmin} ext_keytab -r -k $keytab HTTP/localhost@${R} || exit 1 -${kadmin} add -r --use-defaults HTTP/${otherserver}@${R} || exit 1 -${kadmin} ext_keytab -r -k $ukeytab foo@${R} || exit 1 +${kadmin} <<EOF || exit 1 +init --realm-max-ticket-life=1day --realm-max-renewable-life=1month ${R} +add -r --use-defaults foo@${R} +add -r --use-defaults httpkadmind/admin@${R} +add -r --use-defaults WELLKNOWN/CSRFTOKEN@${R} +add -r --use-defaults HTTP/localhost@${R} +add -r --use-defaults host/xyz.${domain}@${R} +add -r --use-defaults HTTP/xyz.${domain}@${R} +add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ + --max-ticket-life=1d --max-renewable-life=5d \ + --attributes= HTTP/ns.${domain}@${R} +add_ns --key-rotation-epoch=-1d --key-rotation-period=5m \ + --max-ticket-life=1d --max-renewable-life=5d \ + --attributes=ok-as-delegate host/.ns2.${domain}@${R} +add -r --use-defaults HTTP/${server}@${R} +ext_keytab -r -k $keytab kadmin/admin@${R} +ext_keytab -r -k $keytab httpkadmind/admin@${R} +ext_keytab -r -k $keytab HTTP/${server}@${R} +ext_keytab -r -k $keytab HTTP/localhost@${R} +add -r --use-defaults HTTP/${otherserver}@${R} +ext_keytab -r -k $ukeytab foo@${R} +EOF ${kdestroy} # For a while let's not bother with a KDC @@ -259,6 +265,12 @@ $kimpersonate -A --ccache=$cache -k $keytab -R -t aes128-cts-hmac-sha1-96 \ $klist -t >/dev/null || { echo "failed to setup kimpersonate credentials"; exit 2; } +echo "Starting test_csr_authorizer" +${test_csr_authorizer} -A $objdir/authz_dir -S $objdir --server --daemon || + { echo "test_csr_authorizer failed to start"; exit 2; } +test_csr_authorizer_pid=`getpid test_csr_authorizer` +ec=0 + echo "Starting httpkadmind" ${httpkadmind} -H $server -H localhost --local -t --daemon || { echo "httpkadmind failed to start"; exit 2; } @@ -291,7 +303,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -307,7 +319,7 @@ hn=foo.ns.${domain} p=HTTP/$hn echo "Fetching keytab for virtual principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -329,9 +341,9 @@ p2=HTTP/$hn2 p3=HTTP/$hn3 echo "Fetching keytabs for more than one principal" rm -f extracted_keytab* -grant dnsname $hn1 foo@${R} -grant dnsname $hn2 foo@${R} -grant dnsname $hn3 foo@${R} +grant san_dnsname $hn1 foo@${R} +grant san_dnsname $hn2 foo@${R} +grant san_dnsname $hn3 foo@${R} # Note that httpkadmind will first process dNSName q-params, then the spn # q-params. ${kadmin} ext_keytab -k extracted_keytab $p1 || @@ -372,7 +384,7 @@ hn=xyz.${domain} p=host/$hn echo "Fetching keytab for virtual principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "service=host&dNSName=xyz.${domain}" -sf -o "${objdir}/extracted_keytab" && { echo "Got a keytab for $p even though it is a host service!"; exit 1; } get_keytab "spn=host/xyz.${domain}" -sf -o "${objdir}/extracted_keytab" && @@ -383,7 +395,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -405,7 +417,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation w/ revocation for concrete principal $p" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -425,7 +437,7 @@ hn=abc.${domain} p=HTTP/$hn echo "Checking concrete principal creation ($p)" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}&create=true" -sf -o "${objdir}/extracted_keytab" && { echo "GET succeeded for write operation!"; exit 1; } get_keytab_POST "dNSName=${hn}&create=true" -s -o "${objdir}/extracted_keytab" || @@ -444,7 +456,7 @@ hn=bar.ns.${domain} p=HTTP/$hn echo "Checking materialization of virtual principal ($p)" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}&materialize=true" -sf -o "${objdir}/extracted_keytab" && { echo "GET succeeded for write operation!"; exit 1; } get_keytab_POST "dNSName=${hn}&materialize=true" -s -o "${objdir}/extracted_keytab" || @@ -471,7 +483,7 @@ p=HTTP/$hn restport=$restport2 echo "Checking principal creation at secondary yields redirect" rm -f extracted_keytab -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab_POST_redir "dNSName=${hn}&create=true" \ -s -o "${objdir}/extracted_keytab" ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest || @@ -559,7 +571,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p using remote HDB" rm -f extracted_keytab* -grant dnsname $hn httpkadmind/admin@${R} +grant san_dnsname $hn httpkadmind/admin@${R} KRB5CCNAME=$admincache ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -575,7 +587,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p using remote HDB" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -610,7 +622,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Fetching keytab for concrete principal $p using local read-only HDB" rm -f extracted_keytab* -grant dnsname $hn httpkadmind/admin@${R} +grant san_dnsname $hn httpkadmind/admin@${R} KRB5CCNAME=$admincache ${kadmin} ext_keytab -k extracted_keytab $p || { echo "Failed to get a keytab for $p with kadmin"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.kadmin || @@ -626,7 +638,7 @@ hn=xyz.${domain} p=HTTP/$hn echo "Checking key rotation for concrete principal $p using local read-only HDB and remote HDB" rm -f extracted_keytab* -grant dnsname $hn foo@${R} +grant san_dnsname $hn foo@${R} get_keytab "dNSName=${hn}" -sf -o "${objdir}/extracted_keytab" || { echo "Failed to get a keytab for $p with curl"; exit 1; } ${ktutil} -k "${objdir}/extracted_keytab" list --keys > extracted_keytab.rest1 || @@ -810,6 +822,7 @@ KRB5CCNAME=$admincache ${kadmin} get $p | grep 'Internal error' messages.log && { echo "Internal errors in log"; exit 1; } +sh ${leaks_kill} test_csr_authorizer $test_csr_authorizer_pid || ec=1 sh ${leaks_kill} httpkadmind $httpkadmindpid || ec=1 sh ${leaks_kill} kadmind $kadmindpid || ec=1 sh ${leaks_kill} kadmind $kadmind2pid || ec=1 diff --git a/third_party/heimdal/tests/kdc/check-pkinit.in b/third_party/heimdal/tests/kdc/check-pkinit.in index 9f90fd040f9..571a64e9c15 100644 --- a/third_party/heimdal/tests/kdc/check-pkinit.in +++ b/third_party/heimdal/tests/kdc/check-pkinit.in @@ -64,6 +64,11 @@ kx509="${kx509} -c $cache" KRB5_CONFIG="${objdir}/krb5-pkinit.conf" export KRB5_CONFIG +HEIM_PIDFILE_DIR=$objdir +export HEIM_PIDFILE_DIR +HEIM_IPC_DIR=$objdir +export HEIM_IPC_DIR + rsa=yes pkinit=no diff --git a/third_party/heimdal/tests/kdc/krb5-bx509.conf.in b/third_party/heimdal/tests/kdc/krb5-bx509.conf.in index 8a9d0bb4940..2cd6fef2215 100644 --- a/third_party/heimdal/tests/kdc/krb5-bx509.conf.in +++ b/third_party/heimdal/tests/kdc/krb5-bx509.conf.in @@ -29,9 +29,6 @@ # Locate kdc plugins for testing plugin_dir = @objdir@/../../kdc/.libs - # Configure kdc plugins for testing - simple_csr_authorizer_directory = @objdir@/simple_csr_authz - enable-pkinit = true pkinit_identity = PEM-FILE:@objdir@/user-issuer.pem pkinit_anchors = PEM-FILE:@objdir@/pkinit-anchor.pem @@ -86,7 +83,6 @@ db-dir = @objdir@ [bx509] - simple_csr_authorizer_directory = @objdir@/simple_csr_authz realms = { TEST.H5L.SE = { # Default (no cert exts requested) @@ -127,7 +123,6 @@ [get-tgt] no_addresses = true allow_addresses = true - simple_csr_authorizer_directory = @objdir@/simple_csr_authz realms = { TEST.H5L.SE = { # Default (no cert exts requested) diff --git a/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in b/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in index f887e82c4f1..fb2fc6a2f08 100644 --- a/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in +++ b/third_party/heimdal/tests/kdc/krb5-httpkadmind.conf.in @@ -28,9 +28,6 @@ # Locate kdc plugins for testing plugin_dir = @objdir@/../../kdc/.libs - # Configure kdc plugins for testing - simple_csr_authorizer_directory = @objdir@/simple_csr_authz - database = { dbname = @objdir@/current-db realm = TEST.H5L.SE @@ -84,7 +81,6 @@ virtual_hostbased_princ_svcs = HTTP host [ext_keytab] - simple_csr_authorizer_directory = @objdir@/simple_csr_authz new_hostbased_service_principal_attributes = { host = { a-particular-hostname.test.h5l.se = ok-as-delegate,no-auth-data-reqd diff --git a/third_party/heimdal/tests/kdc/krb5-pkinit.conf.in b/third_party/heimdal/tests/kdc/krb5-pkinit.conf.in index fbc21277a3d..e2d3f3d26a8 100644 --- a/third_party/heimdal/tests/kdc/krb5-pkinit.conf.in +++ b/third_party/heimdal/tests/kdc/krb5-pkinit.conf.in @@ -25,7 +25,9 @@ plugin_dir = @objdir@/../../kdc/.libs - simple_csr_authorizer_directory = @objdir@/simple_csr_authz + ipc_csr_authorizer = { + optional = true + } enable_kx509 = true require_initial_kca_tickets = false diff --git a/third_party/heimdal/tests/plugin/kdc_test_plugin.c b/third_party/heimdal/tests/plugin/kdc_test_plugin.c index ff33b5f7262..6df40a2b722 100644 --- a/third_party/heimdal/tests/plugin/kdc_test_plugin.c +++ b/third_party/heimdal/tests/plugin/kdc_test_plugin.c @@ -61,7 +61,8 @@ pac_verify(void *ctx, hdb_entry * client, hdb_entry * server, hdb_entry * krbtgt, - krb5_pac *pac) + krb5_pac pac, + krb5_boolean *is_trusted) { krb5_context context = kdc_request_get_context((kdc_request_t)r); krb5_error_code ret; @@ -73,12 +74,12 @@ pac_verify(void *ctx, krb5_warnx(context, "pac_verify"); - ret = krb5_pac_get_buffer(context, *pac, 1, &data); + ret = krb5_pac_get_buffer(context, pac, 1, &data); if (ret) return ret; krb5_data_free(&data); - ret = krb5_pac_get_kdc_checksum_info(context, *pac, &cstype, &rodc_id); + ret = krb5_pac_get_kdc_checksum_info(context, pac, &cstype, &rodc_id); if (ret) return ret; @@ -95,7 +96,7 @@ pac_verify(void *ctx, if (ret) return ret; - return krb5_pac_verify(context, *pac, 0, NULL, NULL, &key->key); + return krb5_pac_verify(context, pac, 0, NULL, NULL, &key->key); } static void logit(const char *what, astgs_request_t r) @@ -161,11 +162,12 @@ audit(void *ctx, astgs_request_t r) } static krb5plugin_kdc_ftable kdc_plugin = { - KRB5_PLUGIN_KDC_VERSION_10, + KRB5_PLUGIN_KDC_VERSION_11, init, fini, pac_generate, pac_verify, + NULL, /* pac_update */ client_access, NULL, /* referral_policy */ finalize_reply, |