mirror of
https://github.com/docker/docker-credential-helpers.git
synced 2026-06-28 15:21:29 +05:30
Compare commits
149 Commits
v0.8.0
...
955f6c518d
| Author | SHA1 | Date | |
|---|---|---|---|
| 955f6c518d | |||
| 0202e5a960 | |||
| 4e2b0ff14f | |||
| 8fe8d458f7 | |||
| af758c414c | |||
| dc6f4f5cb9 | |||
| 4e68cd824e | |||
| d520877610 | |||
| f0e4adbf36 | |||
| bf6137df6b | |||
| 2b4e08bca3 | |||
| 62deeb49c1 | |||
| 6ca9924445 | |||
| 806dc5f678 | |||
| a7b23cd2b5 | |||
| 178a3a4e57 | |||
| f5fd80af0f | |||
| ae163ade7b | |||
| b871f76540 | |||
| 50c1460bf5 | |||
| aecf6e5780 | |||
| ecf6c1ccc7 | |||
| b844409a12 | |||
| 9df2c7782a | |||
| 7a15b77bcb | |||
| 81f7ebebfd | |||
| 3f97cf3ce3 | |||
| 8b5e6dffc6 | |||
| 4741f33d28 | |||
| 78303955b8 | |||
| 9b0c242b5c | |||
| 057ed818a9 | |||
| b7a754b9ff | |||
| 62777f0887 | |||
| 9d04e49561 | |||
| bc131d729d | |||
| 84c3413e0e | |||
| fcb0b664b5 | |||
| cf4e41fbb0 | |||
| 53f7bdc3fa | |||
| d4602cd917 | |||
| ae84c25786 | |||
| 2adf3cf9aa | |||
| 1fdce4c733 | |||
| 962a779645 | |||
| ec5efac3ca | |||
| 8154b98959 | |||
| d075f3cecc | |||
| fdddb02817 | |||
| c07513a69d | |||
| 4142982fb8 | |||
| 860f1459e3 | |||
| d378d46316 | |||
| 4c97a761df | |||
| b61abf1cb8 | |||
| 85841ea0ce | |||
| c32e697324 | |||
| d770c60191 | |||
| 5095e43ecf | |||
| 00313838c6 | |||
| bcf656656f | |||
| fd27520bbd | |||
| 4849c2328b | |||
| 2e8005f3a7 | |||
| 5d4d5150ae | |||
| f9d3010165 | |||
| e7bd3957ae | |||
| cfd6d21216 | |||
| ab29a6c87b | |||
| 576efaa084 | |||
| 9d6cdddf25 | |||
| d8e34f8743 | |||
| b1d5bf0326 | |||
| 50b162c340 | |||
| 833d2c334f | |||
| 9651bf7802 | |||
| 26274da6cf | |||
| f4cdabf916 | |||
| c7514a0999 | |||
| 36a3c50452 | |||
| 4e957ecd1b | |||
| f7f8554b4c | |||
| 1a77fa667f | |||
| 8a779f2b11 | |||
| a767624e34 | |||
| a5569fbfff | |||
| 99bf48e5f8 | |||
| 1041211a6e | |||
| 8c804df56c | |||
| a7974e91c5 | |||
| ffe5a9835c | |||
| e79a8203ca | |||
| 4cdcdc29eb | |||
| 8438667191 | |||
| fc66c3f02c | |||
| 7810dc4db9 | |||
| 28e893e56d | |||
| 1161e9c157 | |||
| a17e9a013b | |||
| fe0e8e3a01 | |||
| c2ca986943 | |||
| 407e50d36e | |||
| 10845d8f94 | |||
| 43ae7f3412 | |||
| 1d9eaaa4ef | |||
| 7346714456 | |||
| ab3fc5283d | |||
| 713df50a2d | |||
| 7e7c5576ba | |||
| ad253f54a5 | |||
| fa991bcbeb | |||
| db1da9da5d | |||
| c23b2d6e4f | |||
| 13e62f3bbe | |||
| 2d241f3602 | |||
| 58c87f0952 | |||
| dbb72e35c3 | |||
| 6e5b45e59f | |||
| 2ed5a274b6 | |||
| 7dbcc1c472 | |||
| 6b9df3ebb5 | |||
| dc10c50685 | |||
| 896eb37d47 | |||
| a14669f4ff | |||
| 74840b3740 | |||
| d3ef442f59 | |||
| f64d6b131b | |||
| 1bb9aa3210 | |||
| 73b9e5d51f | |||
| 0c43fede6d | |||
| a941c5247f | |||
| 097f945536 | |||
| 9272dcb90a | |||
| ecacf8cdcf | |||
| 5be670a285 | |||
| 73aa8c0daa | |||
| c23afb6c37 | |||
| d622133060 | |||
| 12500fb753 | |||
| bf726a0656 | |||
| d9632f6a08 | |||
| 292722b503 | |||
| 979dcc4762 | |||
| f411a65c31 | |||
| 9629bd77ad | |||
| f642c26173 | |||
| 8fc330691f | |||
| 6a3e64c0b4 | |||
| 218f1787ac |
@@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
Please make sure you've read and understood our contributing guidelines;
|
||||||
|
https://github.com/docker/cli/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
|
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||||
|
|
||||||
|
For additional information on our contributing process, read our contributing
|
||||||
|
guide https://docs.docker.com/opensource/code/
|
||||||
|
|
||||||
|
If this is a bug fix, make sure your description includes "fixes #xxxx", or
|
||||||
|
"closes #xxxx"
|
||||||
|
|
||||||
|
Please provide the following information:
|
||||||
|
-->
|
||||||
|
|
||||||
|
**- What I did**
|
||||||
|
|
||||||
|
**- How I did it**
|
||||||
|
|
||||||
|
**- How to verify it**
|
||||||
|
|
||||||
|
**- Description for the changelog**
|
||||||
|
<!--
|
||||||
|
Write a short (one line) summary that describes the changes in this
|
||||||
|
pull request for inclusion in the changelog:
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
**- A picture of a cute animal (not mandatory but encouraged)**
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
- "bot"
|
||||||
+47
-30
@@ -1,5 +1,14 @@
|
|||||||
name: build
|
name: build
|
||||||
|
|
||||||
|
# Default to 'contents: read', which grants actions to read commits.
|
||||||
|
#
|
||||||
|
# If any permission is set, any permission not included in the list is
|
||||||
|
# implicitly set to "none".
|
||||||
|
#
|
||||||
|
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -8,18 +17,19 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'master'
|
- 'main'
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DESTDIR: ./bin
|
DESTDIR: ./bin
|
||||||
GO_VERSION: 1.20.6
|
GO_VERSION: 1.25.8
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
validate:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30 # guardrails timeout for the whole job
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -29,10 +39,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v4
|
||||||
-
|
-
|
||||||
name: Run
|
name: Run
|
||||||
run: |
|
run: |
|
||||||
@@ -40,24 +50,26 @@ jobs:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 30 # guardrails timeout for the whole job
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
|
- ubuntu-24.04
|
||||||
- ubuntu-22.04
|
- ubuntu-22.04
|
||||||
- ubuntu-20.04
|
- macOS-15-intel
|
||||||
- macOS-11
|
- macOS-15
|
||||||
|
- macOS-14
|
||||||
- windows-2022
|
- windows-2022
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
-
|
-
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
cache: true
|
|
||||||
-
|
-
|
||||||
name: Install deps (ubuntu)
|
name: Install deps (ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu-')
|
if: startsWith(matrix.os, 'ubuntu-')
|
||||||
@@ -72,7 +84,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: GPG conf
|
name: GPG conf
|
||||||
if: ${{ !startsWith(matrix.os, 'windows-') }}
|
if: ${{ !startsWith(matrix.os, 'windows-') }}
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v8
|
||||||
id: gpg
|
id: gpg
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
@@ -89,7 +101,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Import GPG key
|
name: Import GPG key
|
||||||
if: ${{ !startsWith(matrix.os, 'windows-') }}
|
if: ${{ !startsWith(matrix.os, 'windows-') }}
|
||||||
uses: crazy-max/ghaction-import-gpg@v5
|
uses: crazy-max/ghaction-import-gpg@v7
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ steps.gpg.outputs.key }}
|
gpg_private_key: ${{ steps.gpg.outputs.key }}
|
||||||
passphrase: ${{ steps.gpg.outputs.passphrase }}
|
passphrase: ${{ steps.gpg.outputs.passphrase }}
|
||||||
@@ -107,22 +119,21 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
file: ${{ env.DESTDIR }}/coverage.txt
|
files: ${{ env.DESTDIR }}/coverage.txt
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
test-sandboxed:
|
test-sandboxed:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30 # guardrails timeout for the whole job
|
||||||
steps:
|
steps:
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v4
|
||||||
-
|
-
|
||||||
name: Test
|
name: Test
|
||||||
uses: docker/bake-action@v2
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
targets: test
|
targets: test
|
||||||
set: |
|
set: |
|
||||||
@@ -130,24 +141,29 @@ jobs:
|
|||||||
*.cache-to=type=gha,scope=test,mode=max
|
*.cache-to=type=gha,scope=test,mode=max
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
file: ${{ env.DESTDIR }}//coverage.txt
|
files: ${{ env.DESTDIR }}//coverage.txt
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30 # guardrails timeout for the whole job
|
||||||
|
permissions:
|
||||||
|
# required to create GitHub release
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v4
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v4
|
||||||
-
|
-
|
||||||
name: Build
|
name: Build
|
||||||
run: |
|
run: |
|
||||||
@@ -165,7 +181,7 @@ jobs:
|
|||||||
find ${{ env.DESTDIR }} -type f -exec file -e ascii -e text -- {} +
|
find ${{ env.DESTDIR }} -type f -exec file -e ascii -e text -- {} +
|
||||||
-
|
-
|
||||||
name: Upload artifacts
|
name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: docker-credential-helpers
|
name: docker-credential-helpers
|
||||||
path: ${{ env.DESTDIR }}/*
|
path: ${{ env.DESTDIR }}/*
|
||||||
@@ -173,7 +189,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: GitHub Release
|
name: GitHub Release
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@@ -181,11 +197,12 @@ jobs:
|
|||||||
files: ${{ env.DESTDIR }}/*
|
files: ${{ env.DESTDIR }}/*
|
||||||
|
|
||||||
build-deb:
|
build-deb:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30 # guardrails timeout for the whole job
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
-
|
||||||
|
|||||||
+12
-21
@@ -1,32 +1,23 @@
|
|||||||
|
version: "2"
|
||||||
run:
|
run:
|
||||||
timeout: 10m
|
|
||||||
modules-download-mode: vendor
|
modules-download-mode: vendor
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
|
default: none
|
||||||
enable:
|
enable:
|
||||||
- gofmt
|
|
||||||
- govet
|
- govet
|
||||||
- depguard
|
|
||||||
- goimports
|
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- misspell
|
- misspell
|
||||||
- unused
|
|
||||||
- revive
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- typecheck
|
- unused
|
||||||
disable-all: true
|
settings:
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: package-comments # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
|
||||||
|
disabled: true
|
||||||
|
|
||||||
linters-settings:
|
formatters:
|
||||||
depguard:
|
enable:
|
||||||
list-type: blacklist
|
- gofmt
|
||||||
include-go-root: true
|
- goimports
|
||||||
packages:
|
|
||||||
# The io/ioutil package has been deprecated.
|
|
||||||
# https://go.dev/doc/go1.16#ioutil
|
|
||||||
- io/ioutil
|
|
||||||
|
|
||||||
issues:
|
|
||||||
exclude-rules:
|
|
||||||
- linters:
|
|
||||||
- revive
|
|
||||||
text: "stutters"
|
|
||||||
|
|||||||
+28
-47
@@ -1,10 +1,11 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.20.6
|
ARG GO_VERSION=1.25.8
|
||||||
ARG XX_VERSION=1.2.1
|
ARG DEBIAN_VERSION=bookworm
|
||||||
ARG OSXCROSS_VERSION=11.3-r7-debian
|
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.51.1
|
ARG XX_VERSION=1.7.0
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG OSXCROSS_VERSION=11.3-r8-debian
|
||||||
|
ARG GOLANGCI_LINT_VERSION=v2.8
|
||||||
|
|
||||||
ARG PACKAGE=github.com/docker/docker-credential-helpers
|
ARG PACKAGE=github.com/docker/docker-credential-helpers
|
||||||
|
|
||||||
@@ -14,9 +15,8 @@ FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
|||||||
# osxcross contains the MacOSX cross toolchain for xx
|
# osxcross contains the MacOSX cross toolchain for xx
|
||||||
FROM crazymax/osxcross:${OSXCROSS_VERSION} AS osxcross
|
FROM crazymax/osxcross:${OSXCROSS_VERSION} AS osxcross
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bullseye AS gobase
|
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${DEBIAN_VERSION} AS gobase
|
||||||
COPY --from=xx / /
|
COPY --from=xx / /
|
||||||
ARG DEBIAN_FRONTEND
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends clang dpkg-dev file git lld llvm make pkg-config rsync
|
RUN apt-get update && apt-get install -y --no-install-recommends clang dpkg-dev file git lld llvm make pkg-config rsync
|
||||||
ENV GOFLAGS="-mod=vendor"
|
ENV GOFLAGS="-mod=vendor"
|
||||||
ENV CGO_ENABLED="1"
|
ENV CGO_ENABLED="1"
|
||||||
@@ -54,8 +54,7 @@ EOT
|
|||||||
|
|
||||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION} AS golangci-lint
|
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION} AS golangci-lint
|
||||||
FROM gobase AS lint
|
FROM gobase AS lint
|
||||||
ARG DEBIAN_FRONTEND
|
RUN apt-get install -y binutils gcc libc6-dev libgcc-11-dev libsecret-1-dev pkg-config
|
||||||
RUN apt-get install -y binutils gcc libc6-dev libgcc-10-dev libsecret-1-dev pkg-config
|
|
||||||
RUN --mount=type=bind,target=. \
|
RUN --mount=type=bind,target=. \
|
||||||
--mount=type=cache,target=/root/.cache \
|
--mount=type=cache,target=/root/.cache \
|
||||||
--mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
|
--mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
|
||||||
@@ -63,11 +62,9 @@ RUN --mount=type=bind,target=. \
|
|||||||
|
|
||||||
FROM gobase AS base
|
FROM gobase AS base
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG DEBIAN_FRONTEND
|
RUN xx-apt-get install -y binutils gcc libc6-dev libgcc-11-dev libsecret-1-dev pkg-config
|
||||||
RUN xx-apt-get install -y binutils gcc libc6-dev libgcc-10-dev libsecret-1-dev pkg-config
|
|
||||||
|
|
||||||
FROM base AS test
|
FROM base AS test
|
||||||
ARG DEBIAN_FRONTEND
|
|
||||||
RUN xx-apt-get install -y dbus-x11 gnome-keyring gpg-agent gpgconf libsecret-1-dev pass
|
RUN xx-apt-get install -y dbus-x11 gnome-keyring gpg-agent gpgconf libsecret-1-dev pass
|
||||||
RUN --mount=type=bind,target=. \
|
RUN --mount=type=bind,target=. \
|
||||||
--mount=type=cache,target=/root/.cache \
|
--mount=type=cache,target=/root/.cache \
|
||||||
@@ -97,21 +94,7 @@ FROM gobase AS version
|
|||||||
RUN --mount=target=. \
|
RUN --mount=target=. \
|
||||||
echo -n "$(./hack/git-meta version)" | tee /tmp/.version ; echo -n "$(./hack/git-meta revision)" | tee /tmp/.revision
|
echo -n "$(./hack/git-meta version)" | tee /tmp/.version ; echo -n "$(./hack/git-meta revision)" | tee /tmp/.revision
|
||||||
|
|
||||||
FROM base AS build-linux
|
FROM base AS build
|
||||||
ARG PACKAGE
|
|
||||||
RUN --mount=type=bind,target=. \
|
|
||||||
--mount=type=cache,target=/root/.cache \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
--mount=type=bind,source=/tmp/.version,target=/tmp/.version,from=version \
|
|
||||||
--mount=type=bind,source=/tmp/.revision,target=/tmp/.revision,from=version <<EOT
|
|
||||||
set -ex
|
|
||||||
xx-go --wrap
|
|
||||||
make build-pass build-secretservice PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
|
||||||
xx-verify /out/docker-credential-pass
|
|
||||||
xx-verify /out/docker-credential-secretservice
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM base AS build-darwin
|
|
||||||
ARG PACKAGE
|
ARG PACKAGE
|
||||||
RUN --mount=type=bind,target=. \
|
RUN --mount=type=bind,target=. \
|
||||||
--mount=type=cache,target=/root/.cache \
|
--mount=type=cache,target=/root/.cache \
|
||||||
@@ -122,28 +105,26 @@ RUN --mount=type=bind,target=. \
|
|||||||
set -ex
|
set -ex
|
||||||
export MACOSX_VERSION_MIN=$(make print-MACOSX_DEPLOYMENT_TARGET)
|
export MACOSX_VERSION_MIN=$(make print-MACOSX_DEPLOYMENT_TARGET)
|
||||||
xx-go --wrap
|
xx-go --wrap
|
||||||
go install std
|
case "$(xx-info os)" in
|
||||||
make build-osxkeychain build-pass PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
linux)
|
||||||
xx-verify /out/docker-credential-osxkeychain
|
make build-pass build-secretservice PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
||||||
xx-verify /out/docker-credential-pass
|
xx-verify /out/docker-credential-pass
|
||||||
|
xx-verify /out/docker-credential-secretservice
|
||||||
|
;;
|
||||||
|
darwin)
|
||||||
|
go install std
|
||||||
|
make build-osxkeychain build-pass PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
||||||
|
xx-verify /out/docker-credential-osxkeychain
|
||||||
|
xx-verify /out/docker-credential-pass
|
||||||
|
;;
|
||||||
|
windows)
|
||||||
|
make build-wincred PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
||||||
|
mv /out/docker-credential-wincred /out/docker-credential-wincred.exe
|
||||||
|
xx-verify /out/docker-credential-wincred.exe
|
||||||
|
;;
|
||||||
|
esac
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
FROM base AS build-windows
|
|
||||||
ARG PACKAGE
|
|
||||||
RUN --mount=type=bind,target=. \
|
|
||||||
--mount=type=cache,target=/root/.cache \
|
|
||||||
--mount=type=cache,target=/go/pkg/mod \
|
|
||||||
--mount=type=bind,source=/tmp/.version,target=/tmp/.version,from=version \
|
|
||||||
--mount=type=bind,source=/tmp/.revision,target=/tmp/.revision,from=version <<EOT
|
|
||||||
set -ex
|
|
||||||
xx-go --wrap
|
|
||||||
make build-wincred PACKAGE=$PACKAGE VERSION=$(cat /tmp/.version) REVISION=$(cat /tmp/.revision) DESTDIR=/out
|
|
||||||
mv /out/docker-credential-wincred /out/docker-credential-wincred.exe
|
|
||||||
xx-verify /out/docker-credential-wincred.exe
|
|
||||||
EOT
|
|
||||||
|
|
||||||
FROM build-$TARGETOS AS build
|
|
||||||
|
|
||||||
FROM scratch AS binaries
|
FROM scratch AS binaries
|
||||||
COPY --from=build /out /
|
COPY --from=build /out /
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,22 @@ COVERAGEDIR ?= ./bin/coverage
|
|||||||
# 10.11 is the minimum supported version for osxkeychain
|
# 10.11 is the minimum supported version for osxkeychain
|
||||||
export MACOSX_DEPLOYMENT_TARGET = 10.11
|
export MACOSX_DEPLOYMENT_TARGET = 10.11
|
||||||
ifeq "$(shell go env GOOS)" "darwin"
|
ifeq "$(shell go env GOOS)" "darwin"
|
||||||
export CGO_CFLAGS = -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
export CGO_CFLAGS = -Wno-atomic-alignment -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
||||||
|
else
|
||||||
|
# prevent warnings; see https://github.com/docker/docker-credential-helpers/pull/340#issuecomment-2437593837
|
||||||
|
# gcc_libinit.c:44:8: error: large atomic operation may incur significant performance penalty; the access size (4 bytes) exceeds the max lock-free size (0 bytes) [-Werror,-Watomic-alignment]
|
||||||
|
export CGO_CFLAGS = -Wno-atomic-alignment
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq "$(shell go env GOOS)/$(shell go env GOARCH)/$(shell go env GOARM)" "linux/arm/6"
|
||||||
|
# Neither the CGo compiler, nor the C toolchain automatically link to
|
||||||
|
# libatomic when the architecture doesn't support atomic intrinsics, as is
|
||||||
|
# the case for arm/v6.
|
||||||
|
#
|
||||||
|
# Here's the error we get when this is not done (see https://github.com/docker/docker-credential-helpers/pull/340#issuecomment-2437593837):
|
||||||
|
#
|
||||||
|
# gcc_libinit.c:44:8: error: large atomic operation may incur significant performance penalty; the access size (4 bytes) exceeds the max lock-free size (0 bytes) [-Werror,-Watomic-alignment]
|
||||||
|
export CGO_LDFLAGS=-latomic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|||||||
@@ -34,20 +34,20 @@ $ docker buildx bake
|
|||||||
|
|
||||||
Or if the toolchain is already installed on your machine:
|
Or if the toolchain is already installed on your machine:
|
||||||
|
|
||||||
1 - Download the source.
|
1. Download the source.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ git clone https://github.com/docker/docker-credential-helpers.git
|
$ git clone https://github.com/docker/docker-credential-helpers.git
|
||||||
$ cd docker-credential-helpers
|
$ cd docker-credential-helpers
|
||||||
```
|
```
|
||||||
|
|
||||||
2 - Use `make` to build the program you want. That will leave an executable in the `bin` directory inside the repository.
|
2. Use `make` to build the program you want. That will leave an executable in the `bin` directory inside the repository.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ make osxkeychain
|
$ make osxkeychain
|
||||||
```
|
```
|
||||||
|
|
||||||
3 - Put that binary in your `$PATH`, so Docker can find it.
|
3. Put that binary in your `$PATH`, so Docker can find it.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cp bin/build/docker-credential-osxkeychain /usr/local/bin/
|
$ cp bin/build/docker-credential-osxkeychain /usr/local/bin/
|
||||||
|
|||||||
+5
-12
@@ -16,11 +16,9 @@ func isValidCredsMessage(msg string) error {
|
|||||||
if credentials.IsCredentialsMissingServerURLMessage(msg) {
|
if credentials.IsCredentialsMissingServerURLMessage(msg) {
|
||||||
return credentials.NewErrCredentialsMissingServerURL()
|
return credentials.NewErrCredentialsMissingServerURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
if credentials.IsCredentialsMissingUsernameMessage(msg) {
|
if credentials.IsCredentialsMissingUsernameMessage(msg) {
|
||||||
return credentials.NewErrCredentialsMissingUsername()
|
return credentials.NewErrCredentialsMissingUsername()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,13 +34,10 @@ func Store(program ProgramFunc, creds *credentials.Credentials) error {
|
|||||||
|
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t := strings.TrimSpace(string(out))
|
if isValidErr := isValidCredsMessage(string(out)); isValidErr != nil {
|
||||||
|
|
||||||
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
|
||||||
err = isValidErr
|
err = isValidErr
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, strings.TrimSpace(string(out)))
|
||||||
return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -55,17 +50,15 @@ func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error
|
|||||||
|
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t := strings.TrimSpace(string(out))
|
if credentials.IsErrCredentialsNotFoundMessage(string(out)) {
|
||||||
|
|
||||||
if credentials.IsErrCredentialsNotFoundMessage(t) {
|
|
||||||
return nil, credentials.NewErrCredentialsNotFound()
|
return nil, credentials.NewErrCredentialsNotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isValidErr := isValidCredsMessage(t); isValidErr != nil {
|
if isValidErr := isValidCredsMessage(string(out)); isValidErr != nil {
|
||||||
err = isValidErr
|
err = isValidErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t)
|
return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, strings.TrimSpace(string(out)))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &credentials.Credentials{
|
resp := &credentials.Credentials{
|
||||||
|
|||||||
+16
-13
@@ -15,27 +15,30 @@ type Program interface {
|
|||||||
// ProgramFunc is a type of function that initializes programs based on arguments.
|
// ProgramFunc is a type of function that initializes programs based on arguments.
|
||||||
type ProgramFunc func(args ...string) Program
|
type ProgramFunc func(args ...string) Program
|
||||||
|
|
||||||
// NewShellProgramFunc creates programs that are executed in a Shell.
|
// NewShellProgramFunc creates a [ProgramFunc] to run command in a [Shell].
|
||||||
func NewShellProgramFunc(name string) ProgramFunc {
|
func NewShellProgramFunc(command string) ProgramFunc {
|
||||||
return NewShellProgramFuncWithEnv(name, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewShellProgramFuncWithEnv creates programs that are executed in a Shell with environment variables
|
|
||||||
func NewShellProgramFuncWithEnv(name string, env *map[string]string) ProgramFunc {
|
|
||||||
return func(args ...string) Program {
|
return func(args ...string) Program {
|
||||||
return &Shell{cmd: createProgramCmdRedirectErr(name, args, env)}
|
return createProgramCmdRedirectErr(command, args, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createProgramCmdRedirectErr(commandName string, args []string, env *map[string]string) *exec.Cmd {
|
// NewShellProgramFuncWithEnv creates a [ProgramFunc] tu run command
|
||||||
programCmd := exec.Command(commandName, args...)
|
// in a [Shell] with the given environment variables.
|
||||||
|
func NewShellProgramFuncWithEnv(command string, env *map[string]string) ProgramFunc {
|
||||||
|
return func(args ...string) Program {
|
||||||
|
return createProgramCmdRedirectErr(command, args, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createProgramCmdRedirectErr(command string, args []string, env *map[string]string) *Shell {
|
||||||
|
ec := exec.Command(command, args...)
|
||||||
if env != nil {
|
if env != nil {
|
||||||
for k, v := range *env {
|
for k, v := range *env {
|
||||||
programCmd.Env = append(programCmd.Environ(), k+"="+v)
|
ec.Env = append(ec.Environ(), k+"="+v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
programCmd.Stderr = os.Stderr
|
ec.Stderr = os.Stderr
|
||||||
return programCmd
|
return &Shell{cmd: ec}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shell invokes shell commands to talk with a remote credentials-helper.
|
// Shell invokes shell commands to talk with a remote credentials-helper.
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package credentials
|
package credentials
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ErrCredentialsNotFound standardizes the not found error, so every helper returns
|
// ErrCredentialsNotFound standardizes the not found error, so every helper returns
|
||||||
@@ -47,7 +50,7 @@ func IsErrCredentialsNotFound(err error) bool {
|
|||||||
// This function helps to check messages returned by an
|
// This function helps to check messages returned by an
|
||||||
// external program via its standard output.
|
// external program via its standard output.
|
||||||
func IsErrCredentialsNotFoundMessage(err string) bool {
|
func IsErrCredentialsNotFoundMessage(err string) bool {
|
||||||
return err == errCredentialsNotFoundMessage
|
return strings.TrimSpace(err) == errCredentialsNotFoundMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// errCredentialsMissingServerURL represents an error raised
|
// errCredentialsMissingServerURL represents an error raised
|
||||||
@@ -104,7 +107,7 @@ func IsCredentialsMissingServerURL(err error) bool {
|
|||||||
// IsCredentialsMissingServerURLMessage checks for an
|
// IsCredentialsMissingServerURLMessage checks for an
|
||||||
// errCredentialsMissingServerURL in the error message.
|
// errCredentialsMissingServerURL in the error message.
|
||||||
func IsCredentialsMissingServerURLMessage(err string) bool {
|
func IsCredentialsMissingServerURLMessage(err string) bool {
|
||||||
return err == errCredentialsMissingServerURLMessage
|
return strings.TrimSpace(err) == errCredentialsMissingServerURLMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCredentialsMissingUsername returns true if the error
|
// IsCredentialsMissingUsername returns true if the error
|
||||||
@@ -117,5 +120,5 @@ func IsCredentialsMissingUsername(err error) bool {
|
|||||||
// IsCredentialsMissingUsernameMessage checks for an
|
// IsCredentialsMissingUsernameMessage checks for an
|
||||||
// errCredentialsMissingUsername in the error message.
|
// errCredentialsMissingUsername in the error message.
|
||||||
func IsCredentialsMissingUsernameMessage(err string) bool {
|
func IsCredentialsMissingUsernameMessage(err string) bool {
|
||||||
return err == errCredentialsMissingUsernameMessage
|
return strings.TrimSpace(err) == errCredentialsMissingUsernameMessage
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -1,14 +1,12 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.20.6
|
ARG GO_VERSION=1.25.8
|
||||||
ARG DISTRO=ubuntu
|
ARG DISTRO=ubuntu
|
||||||
ARG SUITE=focal
|
ARG SUITE=jammy
|
||||||
|
|
||||||
FROM golang:${GO_VERSION}-bullseye AS golang
|
FROM golang:${GO_VERSION}-bookworm AS golang
|
||||||
|
|
||||||
FROM ${DISTRO}:${SUITE}
|
FROM ${DISTRO}:${SUITE}
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
RUN apt-get update && apt-get install -yy debhelper dh-make libsecret-1-dev
|
RUN apt-get update && apt-get install -yy debhelper dh-make libsecret-1-dev
|
||||||
RUN mkdir -p /build
|
RUN mkdir -p /build
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
variable "GO_VERSION" {
|
variable "GO_VERSION" {
|
||||||
default = "1.20.6"
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Defines the output folder
|
# Defines the output folder
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
module github.com/docker/docker-credential-helpers
|
module github.com/docker/docker-credential-helpers
|
||||||
|
|
||||||
go 1.19
|
go 1.21
|
||||||
|
|
||||||
require github.com/danieljoos/wincred v1.2.0
|
retract (
|
||||||
|
v0.9.1 // osxkeychain: a regression caused backward-incompatibility with earlier versions
|
||||||
|
v0.9.0 // osxkeychain: a regression caused backward-incompatibility with earlier versions
|
||||||
|
)
|
||||||
|
|
||||||
require golang.org/x/sys v0.8.0 // indirect
|
require (
|
||||||
|
github.com/danieljoos/wincred v1.2.3
|
||||||
|
github.com/keybase/go-keychain v0.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.20.0 // indirect
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
|
||||||
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||||
|
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
#include "osxkeychain.h"
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#include <Foundation/NSValue.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
char *get_error(OSStatus status) {
|
|
||||||
char *buf = malloc(128);
|
|
||||||
CFStringRef str = SecCopyErrorMessageString(status, NULL);
|
|
||||||
int success = CFStringGetCString(str, buf, 128, kCFStringEncodingUTF8);
|
|
||||||
if (!success) {
|
|
||||||
strncpy(buf, "Unknown error", 128);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *keychain_add(struct Server *server, char *label, char *username, char *secret) {
|
|
||||||
SecKeychainItemRef item;
|
|
||||||
|
|
||||||
OSStatus status = SecKeychainAddInternetPassword(
|
|
||||||
NULL,
|
|
||||||
strlen(server->host), server->host,
|
|
||||||
0, NULL,
|
|
||||||
strlen(username), username,
|
|
||||||
strlen(server->path), server->path,
|
|
||||||
server->port,
|
|
||||||
server->proto,
|
|
||||||
kSecAuthenticationTypeDefault,
|
|
||||||
strlen(secret), secret,
|
|
||||||
&item
|
|
||||||
);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
SecKeychainAttribute attribute;
|
|
||||||
SecKeychainAttributeList attrs;
|
|
||||||
attribute.tag = kSecLabelItemAttr;
|
|
||||||
attribute.data = label;
|
|
||||||
attribute.length = strlen(label);
|
|
||||||
attrs.count = 1;
|
|
||||||
attrs.attr = &attribute;
|
|
||||||
|
|
||||||
status = SecKeychainItemModifyContent(item, &attrs, 0, NULL);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *keychain_get(struct Server *server, unsigned int *username_l, char **username, unsigned int *secret_l, char **secret) {
|
|
||||||
char *tmp;
|
|
||||||
SecKeychainItemRef item;
|
|
||||||
|
|
||||||
OSStatus status = SecKeychainFindInternetPassword(
|
|
||||||
NULL,
|
|
||||||
strlen(server->host), server->host,
|
|
||||||
0, NULL,
|
|
||||||
0, NULL,
|
|
||||||
strlen(server->path), server->path,
|
|
||||||
server->port,
|
|
||||||
server->proto,
|
|
||||||
kSecAuthenticationTypeDefault,
|
|
||||||
secret_l, (void **)&tmp,
|
|
||||||
&item);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
*secret = strdup(tmp);
|
|
||||||
SecKeychainItemFreeContent(NULL, tmp);
|
|
||||||
|
|
||||||
SecKeychainAttributeList list;
|
|
||||||
SecKeychainAttribute attr;
|
|
||||||
|
|
||||||
list.count = 1;
|
|
||||||
list.attr = &attr;
|
|
||||||
attr.tag = kSecAccountItemAttr;
|
|
||||||
|
|
||||||
status = SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL);
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
*username = strdup(attr.data);
|
|
||||||
*username_l = attr.length;
|
|
||||||
SecKeychainItemFreeContent(&list, NULL);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *keychain_delete(struct Server *server) {
|
|
||||||
SecKeychainItemRef item;
|
|
||||||
|
|
||||||
OSStatus status = SecKeychainFindInternetPassword(
|
|
||||||
NULL,
|
|
||||||
strlen(server->host), server->host,
|
|
||||||
0, NULL,
|
|
||||||
0, NULL,
|
|
||||||
strlen(server->path), server->path,
|
|
||||||
server->port,
|
|
||||||
server->proto,
|
|
||||||
kSecAuthenticationTypeDefault,
|
|
||||||
0, NULL,
|
|
||||||
&item);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
status = SecKeychainItemDelete(item);
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * CFStringToCharArr(CFStringRef aString) {
|
|
||||||
if (aString == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
CFIndex length = CFStringGetLength(aString);
|
|
||||||
CFIndex maxSize =
|
|
||||||
CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
|
||||||
char *buffer = (char *)malloc(maxSize);
|
|
||||||
if (CFStringGetCString(aString, buffer, maxSize,
|
|
||||||
kCFStringEncodingUTF8)) {
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *keychain_list(char *credsLabel, char *** paths, char *** accts, unsigned int *list_l) {
|
|
||||||
CFStringRef credsLabelCF = CFStringCreateWithCString(NULL, credsLabel, kCFStringEncodingUTF8);
|
|
||||||
CFMutableDictionaryRef query = CFDictionaryCreateMutable (NULL, 1, NULL, NULL);
|
|
||||||
CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
|
|
||||||
CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
|
|
||||||
CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
|
|
||||||
CFDictionaryAddValue(query, kSecAttrLabel, credsLabelCF);
|
|
||||||
//Use this query dictionary
|
|
||||||
CFTypeRef result= NULL;
|
|
||||||
OSStatus status = SecItemCopyMatching(
|
|
||||||
query,
|
|
||||||
&result);
|
|
||||||
|
|
||||||
CFRelease(credsLabelCF);
|
|
||||||
|
|
||||||
//Ran a search and store the results in result
|
|
||||||
if (status) {
|
|
||||||
return get_error(status);
|
|
||||||
}
|
|
||||||
CFIndex numKeys = CFArrayGetCount(result);
|
|
||||||
*paths = (char **) malloc((int)sizeof(char *)*numKeys);
|
|
||||||
*accts = (char **) malloc((int)sizeof(char *)*numKeys);
|
|
||||||
//result is of type CFArray
|
|
||||||
for(CFIndex i=0; i<numKeys; i++) {
|
|
||||||
CFDictionaryRef currKey = CFArrayGetValueAtIndex(result,i);
|
|
||||||
|
|
||||||
CFStringRef protocolTmp = CFDictionaryGetValue(currKey, CFSTR("ptcl"));
|
|
||||||
if (protocolTmp != NULL) {
|
|
||||||
CFStringRef protocolStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), protocolTmp);
|
|
||||||
if (CFStringCompare(protocolStr, CFSTR("htps"), 0) == kCFCompareEqualTo) {
|
|
||||||
protocolTmp = CFSTR("https://");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
protocolTmp = CFSTR("http://");
|
|
||||||
}
|
|
||||||
CFRelease(protocolStr);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
char * path = "0";
|
|
||||||
char * acct = "0";
|
|
||||||
(*paths)[i] = (char *) malloc(sizeof(char)*(strlen(path)));
|
|
||||||
memcpy((*paths)[i], path, sizeof(char)*(strlen(path)));
|
|
||||||
(*accts)[i] = (char *) malloc(sizeof(char)*(strlen(acct)));
|
|
||||||
memcpy((*accts)[i], acct, sizeof(char)*(strlen(acct)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 0, protocolTmp);
|
|
||||||
CFStringRef serverTmp = CFDictionaryGetValue(currKey, CFSTR("srvr"));
|
|
||||||
if (serverTmp != NULL) {
|
|
||||||
CFStringAppend(str, serverTmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
CFStringRef pathTmp = CFDictionaryGetValue(currKey, CFSTR("path"));
|
|
||||||
if (pathTmp != NULL) {
|
|
||||||
CFStringAppend(str, pathTmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
const NSNumber * portTmp = CFDictionaryGetValue(currKey, CFSTR("port"));
|
|
||||||
if (portTmp != NULL && portTmp.integerValue != 0) {
|
|
||||||
CFStringRef portStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), portTmp);
|
|
||||||
CFStringAppend(str, CFSTR(":"));
|
|
||||||
CFStringAppend(str, portStr);
|
|
||||||
CFRelease(portStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
CFStringRef acctTmp = CFDictionaryGetValue(currKey, CFSTR("acct"));
|
|
||||||
if (acctTmp == NULL) {
|
|
||||||
acctTmp = CFSTR("account not defined");
|
|
||||||
}
|
|
||||||
|
|
||||||
char * path = CFStringToCharArr(str);
|
|
||||||
char * acct = CFStringToCharArr(acctTmp);
|
|
||||||
|
|
||||||
//We now have all we need, username and servername. Now export this to .go
|
|
||||||
(*paths)[i] = (char *) malloc(sizeof(char)*(strlen(path)+1));
|
|
||||||
memcpy((*paths)[i], path, sizeof(char)*(strlen(path)+1));
|
|
||||||
(*accts)[i] = (char *) malloc(sizeof(char)*(strlen(acct)+1));
|
|
||||||
memcpy((*accts)[i], acct, sizeof(char)*(strlen(acct)+1));
|
|
||||||
|
|
||||||
CFRelease(str);
|
|
||||||
}
|
|
||||||
*list_l = (int)numKeys;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeListData(char *** data, unsigned int length) {
|
|
||||||
for(int i=0; i<length; i++) {
|
|
||||||
free((*data)[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+105
-112
@@ -3,30 +3,33 @@
|
|||||||
package osxkeychain
|
package osxkeychain
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo CFLAGS: -x objective-c
|
#cgo LDFLAGS: -framework Security -framework CoreFoundation
|
||||||
#cgo LDFLAGS: -framework Security -framework Foundation
|
|
||||||
|
|
||||||
#include "osxkeychain.h"
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <stdlib.h>
|
#include <Security/Security.h>
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/docker/docker-credential-helpers/credentials"
|
"github.com/docker/docker-credential-helpers/credentials"
|
||||||
"github.com/docker/docker-credential-helpers/registryurl"
|
"github.com/docker/docker-credential-helpers/registryurl"
|
||||||
|
"github.com/keybase/go-keychain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errCredentialsNotFound is the specific error message returned by OS X
|
// https://opensource.apple.com/source/Security/Security-55471/sec/Security/SecBase.h.auto.html
|
||||||
// when the credentials are not in the keychain.
|
const (
|
||||||
const errCredentialsNotFound = "The specified item could not be found in the keychain."
|
// errCredentialsNotFound is the specific error message returned by OS X
|
||||||
|
// when the credentials are not in the keychain.
|
||||||
// errCredentialsNotFound is the specific error message returned by OS X
|
errCredentialsNotFound = "The specified item could not be found in the keychain. (-25300)"
|
||||||
// when environment does not allow showing dialog to unlock keychain.
|
// errInteractionNotAllowed is the specific error message returned by OS X
|
||||||
const errInteractionNotAllowed = "User interaction is not allowed."
|
// when environment does not allow showing dialog to unlock keychain.
|
||||||
|
errInteractionNotAllowed = "User interaction is not allowed. (-25308)"
|
||||||
|
)
|
||||||
|
|
||||||
// ErrInteractionNotAllowed is returned if keychain password prompt can not be shown.
|
// ErrInteractionNotAllowed is returned if keychain password prompt can not be shown.
|
||||||
var ErrInteractionNotAllowed = errors.New(`keychain cannot be accessed because the current session does not allow user interaction. The keychain may be locked; unlock it by running "security -v unlock-keychain ~/Library/Keychains/login.keychain-db" and try again`)
|
var ErrInteractionNotAllowed = errors.New(`keychain cannot be accessed because the current session does not allow user interaction. The keychain may be locked; unlock it by running "security -v unlock-keychain ~/Library/Keychains/login.keychain-db" and try again`)
|
||||||
@@ -38,152 +41,142 @@ type Osxkeychain struct{}
|
|||||||
func (h Osxkeychain) Add(creds *credentials.Credentials) error {
|
func (h Osxkeychain) Add(creds *credentials.Credentials) error {
|
||||||
_ = h.Delete(creds.ServerURL) // ignore errors as existing credential may not exist.
|
_ = h.Delete(creds.ServerURL) // ignore errors as existing credential may not exist.
|
||||||
|
|
||||||
s, err := splitServer(creds.ServerURL)
|
item := keychain.NewItem()
|
||||||
if err != nil {
|
item.SetSecClass(keychain.SecClassInternetPassword)
|
||||||
|
item.SetLabel(credentials.CredsLabel)
|
||||||
|
item.SetAccount(creds.Username)
|
||||||
|
item.SetData([]byte(creds.Secret))
|
||||||
|
// Prior to v0.9, the credential helper was searching for credentials with
|
||||||
|
// the "dflt" authentication type (see [1]). Since v0.9.0, Get doesn't use
|
||||||
|
// that attribute anymore, and v0.9.0 - v0.9.2 were not setting it here
|
||||||
|
// either.
|
||||||
|
//
|
||||||
|
// In order to keep compatibility with older versions, we need to store
|
||||||
|
// credentials with this attribute set. This way, credentials stored with
|
||||||
|
// newer versions can be retrieved by older versions.
|
||||||
|
//
|
||||||
|
// [1]: https://github.com/docker/docker-credential-helpers/blob/v0.8.2/osxkeychain/osxkeychain.c#L66
|
||||||
|
item.SetAuthenticationType("dflt")
|
||||||
|
if err := splitServer(creds.ServerURL, item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer freeServer(s)
|
|
||||||
|
|
||||||
label := C.CString(credentials.CredsLabel)
|
return keychain.AddItem(item)
|
||||||
defer C.free(unsafe.Pointer(label))
|
|
||||||
username := C.CString(creds.Username)
|
|
||||||
defer C.free(unsafe.Pointer(username))
|
|
||||||
secret := C.CString(creds.Secret)
|
|
||||||
defer C.free(unsafe.Pointer(secret))
|
|
||||||
|
|
||||||
errMsg := C.keychain_add(s, label, username, secret)
|
|
||||||
if errMsg != nil {
|
|
||||||
defer C.free(unsafe.Pointer(errMsg))
|
|
||||||
return errors.New(C.GoString(errMsg))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes credentials from the keychain.
|
// Delete removes credentials from the keychain.
|
||||||
func (h Osxkeychain) Delete(serverURL string) error {
|
func (h Osxkeychain) Delete(serverURL string) error {
|
||||||
s, err := splitServer(serverURL)
|
item := keychain.NewItem()
|
||||||
if err != nil {
|
item.SetSecClass(keychain.SecClassInternetPassword)
|
||||||
|
if err := splitServer(serverURL, item); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer freeServer(s)
|
if err := keychain.DeleteItem(item); err != nil {
|
||||||
|
switch err.Error() {
|
||||||
if errMsg := C.keychain_delete(s); errMsg != nil {
|
|
||||||
defer C.free(unsafe.Pointer(errMsg))
|
|
||||||
switch goMsg := C.GoString(errMsg); goMsg {
|
|
||||||
case errCredentialsNotFound:
|
case errCredentialsNotFound:
|
||||||
return credentials.NewErrCredentialsNotFound()
|
return credentials.NewErrCredentialsNotFound()
|
||||||
case errInteractionNotAllowed:
|
case errInteractionNotAllowed:
|
||||||
return ErrInteractionNotAllowed
|
return ErrInteractionNotAllowed
|
||||||
default:
|
default:
|
||||||
return errors.New(goMsg)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the username and secret to use for a given registry server URL.
|
// Get returns the username and secret to use for a given registry server URL.
|
||||||
func (h Osxkeychain) Get(serverURL string) (string, string, error) {
|
func (h Osxkeychain) Get(serverURL string) (string, string, error) {
|
||||||
s, err := splitServer(serverURL)
|
item := keychain.NewItem()
|
||||||
if err != nil {
|
item.SetSecClass(keychain.SecClassInternetPassword)
|
||||||
|
item.SetMatchLimit(keychain.MatchLimitOne)
|
||||||
|
item.SetReturnAttributes(true)
|
||||||
|
item.SetReturnData(true)
|
||||||
|
if err := splitServer(serverURL, item); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
defer freeServer(s)
|
|
||||||
|
|
||||||
var usernameLen C.uint
|
res, err := keychain.QueryItem(item)
|
||||||
var username *C.char
|
if err != nil {
|
||||||
var secretLen C.uint
|
switch err.Error() {
|
||||||
var secret *C.char
|
|
||||||
defer C.free(unsafe.Pointer(username))
|
|
||||||
defer C.free(unsafe.Pointer(secret))
|
|
||||||
|
|
||||||
errMsg := C.keychain_get(s, &usernameLen, &username, &secretLen, &secret)
|
|
||||||
if errMsg != nil {
|
|
||||||
defer C.free(unsafe.Pointer(errMsg))
|
|
||||||
switch goMsg := C.GoString(errMsg); goMsg {
|
|
||||||
case errCredentialsNotFound:
|
case errCredentialsNotFound:
|
||||||
return "", "", credentials.NewErrCredentialsNotFound()
|
return "", "", credentials.NewErrCredentialsNotFound()
|
||||||
case errInteractionNotAllowed:
|
case errInteractionNotAllowed:
|
||||||
return "", "", ErrInteractionNotAllowed
|
return "", "", ErrInteractionNotAllowed
|
||||||
default:
|
default:
|
||||||
return "", "", errors.New(goMsg)
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
} else if len(res) == 0 {
|
||||||
|
return "", "", credentials.NewErrCredentialsNotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
user := C.GoStringN(username, C.int(usernameLen))
|
return res[0].Account, string(res[0].Data), nil
|
||||||
pass := C.GoStringN(secret, C.int(secretLen))
|
|
||||||
return user, pass, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns the stored URLs and corresponding usernames.
|
// List returns the stored URLs and corresponding usernames.
|
||||||
func (h Osxkeychain) List() (map[string]string, error) {
|
func (h Osxkeychain) List() (map[string]string, error) {
|
||||||
credsLabelC := C.CString(credentials.CredsLabel)
|
item := keychain.NewItem()
|
||||||
defer C.free(unsafe.Pointer(credsLabelC))
|
item.SetSecClass(keychain.SecClassInternetPassword)
|
||||||
|
item.SetMatchLimit(keychain.MatchLimitAll)
|
||||||
|
item.SetReturnAttributes(true)
|
||||||
|
item.SetLabel(credentials.CredsLabel)
|
||||||
|
|
||||||
var pathsC **C.char
|
res, err := keychain.QueryItem(item)
|
||||||
defer C.free(unsafe.Pointer(pathsC))
|
if err != nil {
|
||||||
var acctsC **C.char
|
switch err.Error() {
|
||||||
defer C.free(unsafe.Pointer(acctsC))
|
|
||||||
var listLenC C.uint
|
|
||||||
errMsg := C.keychain_list(credsLabelC, &pathsC, &acctsC, &listLenC)
|
|
||||||
defer C.freeListData(&pathsC, listLenC)
|
|
||||||
defer C.freeListData(&acctsC, listLenC)
|
|
||||||
if errMsg != nil {
|
|
||||||
defer C.free(unsafe.Pointer(errMsg))
|
|
||||||
switch goMsg := C.GoString(errMsg); goMsg {
|
|
||||||
case errCredentialsNotFound:
|
case errCredentialsNotFound:
|
||||||
return make(map[string]string), nil
|
return make(map[string]string), nil
|
||||||
case errInteractionNotAllowed:
|
case errInteractionNotAllowed:
|
||||||
return nil, ErrInteractionNotAllowed
|
return nil, ErrInteractionNotAllowed
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(goMsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var listLen int
|
|
||||||
listLen = int(listLenC)
|
|
||||||
pathTmp := (*[1 << 30]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen]
|
|
||||||
acctTmp := (*[1 << 30]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen]
|
|
||||||
// taking the array of c strings into go while ignoring all the stuff irrelevant to credentials-helper
|
|
||||||
resp := make(map[string]string)
|
|
||||||
for i := 0; i < listLen; i++ {
|
|
||||||
if C.GoString(pathTmp[i]) == "0" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i])
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitServer(serverURL string) (*C.struct_Server, error) {
|
|
||||||
u, err := registryurl.Parse(serverURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
proto := C.kSecProtocolTypeHTTPS
|
|
||||||
if u.Scheme == "http" {
|
|
||||||
proto = C.kSecProtocolTypeHTTP
|
|
||||||
}
|
|
||||||
var port int
|
|
||||||
p := u.Port()
|
|
||||||
if p != "" {
|
|
||||||
port, err = strconv.Atoi(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &C.struct_Server{
|
resp := make(map[string]string)
|
||||||
proto: C.SecProtocolType(proto),
|
for _, r := range res {
|
||||||
host: C.CString(u.Hostname()),
|
proto := "http"
|
||||||
port: C.uint(port),
|
if r.Protocol == kSecProtocolTypeHTTPS {
|
||||||
path: C.CString(u.Path),
|
proto = "https"
|
||||||
}, nil
|
}
|
||||||
|
host := r.Server
|
||||||
|
if r.Port != 0 {
|
||||||
|
host = net.JoinHostPort(host, strconv.Itoa(int(r.Port)))
|
||||||
|
}
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: proto,
|
||||||
|
Host: host,
|
||||||
|
Path: r.Path,
|
||||||
|
}
|
||||||
|
resp[u.String()] = r.Account
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func freeServer(s *C.struct_Server) {
|
const (
|
||||||
C.free(unsafe.Pointer(s.host))
|
// Hardcoded protocol types matching their Objective-C equivalents.
|
||||||
C.free(unsafe.Pointer(s.path))
|
// https://developer.apple.com/documentation/security/ksecattrprotocolhttps?language=objc
|
||||||
|
kSecProtocolTypeHTTPS = "htps" // This is NOT a typo.
|
||||||
|
// https://developer.apple.com/documentation/security/ksecattrprotocolhttp?language=objc
|
||||||
|
kSecProtocolTypeHTTP = "http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func splitServer(serverURL string, item keychain.Item) error {
|
||||||
|
u, err := registryurl.Parse(serverURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.SetProtocol(kSecProtocolTypeHTTPS)
|
||||||
|
if u.Scheme == "http" {
|
||||||
|
item.SetProtocol(kSecProtocolTypeHTTP)
|
||||||
|
}
|
||||||
|
item.SetServer(u.Hostname())
|
||||||
|
if p := u.Port(); p != "" {
|
||||||
|
port, err := strconv.Atoi(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.SetPort(int32(port))
|
||||||
|
}
|
||||||
|
item.SetPath(u.Path)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#include <Security/Security.h>
|
|
||||||
|
|
||||||
struct Server {
|
|
||||||
SecProtocolType proto;
|
|
||||||
char *host;
|
|
||||||
char *path;
|
|
||||||
unsigned int port;
|
|
||||||
};
|
|
||||||
|
|
||||||
char *keychain_add(struct Server *server, char *label, char *username, char *secret);
|
|
||||||
char *keychain_get(struct Server *server, unsigned int *username_l, char **username, unsigned int *secret_l, char **secret);
|
|
||||||
char *keychain_delete(struct Server *server);
|
|
||||||
char *keychain_list(char *credsLabel, char *** data, char *** accts, unsigned int *list_l);
|
|
||||||
void freeListData(char *** data, unsigned int length);
|
|
||||||
@@ -15,11 +15,6 @@ func TestOSXKeychainHelper(t *testing.T) {
|
|||||||
Username: "foobar",
|
Username: "foobar",
|
||||||
Secret: "foobarbaz",
|
Secret: "foobarbaz",
|
||||||
}
|
}
|
||||||
creds1 := &credentials.Credentials{
|
|
||||||
ServerURL: "https://foobar.example.com:2376/v2",
|
|
||||||
Username: "foobarbaz",
|
|
||||||
Secret: "foobar",
|
|
||||||
}
|
|
||||||
helper := Osxkeychain{}
|
helper := Osxkeychain{}
|
||||||
if err := helper.Add(creds); err != nil {
|
if err := helper.Add(creds); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -43,19 +38,49 @@ func TestOSXKeychainHelper(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.Add(creds1)
|
if _, ok := auths[creds.ServerURL]; !ok {
|
||||||
defer helper.Delete(creds1.ServerURL)
|
t.Fatalf("server %s not found in list, got: %+v", creds.ServerURL, auths)
|
||||||
newauths, err := helper.List()
|
|
||||||
if len(newauths)-len(auths) != 1 {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Error: len(newauths): %d, len(auths): %d", len(newauths), len(auths))
|
|
||||||
}
|
|
||||||
t.Fatalf("Error: len(newauths): %d, len(auths): %d\n Error= %v", len(newauths), len(auths), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert another token and check if it is in the list
|
||||||
|
creds1 := &credentials.Credentials{
|
||||||
|
ServerURL: "https://foobar.example.com:2376/v2",
|
||||||
|
Username: "foobarbaz",
|
||||||
|
Secret: "foobar",
|
||||||
|
}
|
||||||
|
helper.Add(creds1)
|
||||||
|
defer helper.Delete(creds1.ServerURL)
|
||||||
|
|
||||||
|
auths, err = helper.List()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("operation List failed: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := auths[creds.ServerURL]; !ok {
|
||||||
|
t.Fatalf("server %s not found in list, got: %+v", creds.ServerURL, auths)
|
||||||
|
}
|
||||||
|
if _, ok := auths[creds1.ServerURL]; !ok {
|
||||||
|
t.Fatalf("server %s not found in list, got: %+v", creds1.ServerURL, auths)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the 1st token inserted
|
||||||
if err := helper.Delete(creds.ServerURL); err != nil {
|
if err := helper.Delete(creds.ServerURL); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auths, err = helper.List()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("operation List failed: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First token should have been deleted
|
||||||
|
if _, ok := auths[creds.ServerURL]; ok {
|
||||||
|
t.Fatalf("server %s was not deleted, got: %+v", creds.ServerURL, auths)
|
||||||
|
}
|
||||||
|
// Second token should still be there
|
||||||
|
if _, ok := auths[creds1.ServerURL]; !ok {
|
||||||
|
t.Fatalf("server %s not found in list, got: %+v", creds1.ServerURL, auths)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestOSXKeychainHelperRetrieveAliases verifies that secrets can be accessed
|
// TestOSXKeychainHelperRetrieveAliases verifies that secrets can be accessed
|
||||||
@@ -107,7 +132,7 @@ func TestOSXKeychainHelperRetrieveAliases(t *testing.T) {
|
|||||||
t.Fatalf("Error: failed to store secret for URL %q: %s", tc.storeURL, err)
|
t.Fatalf("Error: failed to store secret for URL %q: %s", tc.storeURL, err)
|
||||||
}
|
}
|
||||||
if _, _, err := helper.Get(tc.readURL); err != nil {
|
if _, _, err := helper.Get(tc.readURL); err != nil {
|
||||||
t.Errorf("Error: failed to read secret for URL %q using %q", tc.storeURL, tc.readURL)
|
t.Errorf("Error: failed to read secret for URL %q using %q: %s", tc.storeURL, tc.readURL, err)
|
||||||
}
|
}
|
||||||
if err := helper.Delete(tc.storeURL); err != nil {
|
if err := helper.Delete(tc.storeURL); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@@ -116,6 +141,39 @@ func TestOSXKeychainHelperRetrieveAliases(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOSXKeychainHelperStoreWithUncleanPath(t *testing.T) {
|
||||||
|
helper := Osxkeychain{}
|
||||||
|
creds := &credentials.Credentials{
|
||||||
|
ServerURL: "https://::1:8080//////location/../../hello",
|
||||||
|
Username: "testuser",
|
||||||
|
Secret: "testsecret",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean store before and after the test.
|
||||||
|
defer helper.Delete(creds.ServerURL)
|
||||||
|
if err := helper.Delete(creds.ServerURL); err != nil && !credentials.IsErrCredentialsNotFound(err) {
|
||||||
|
t.Errorf("prepare: failed to delete '%s': %v", creds.ServerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the credentials
|
||||||
|
if err := helper.Add(creds); err != nil {
|
||||||
|
t.Fatalf("Error: failed to store credentials with unclean path %q: %s", creds.ServerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve and verify credentials
|
||||||
|
username, secret, err := helper.Get(creds.ServerURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error: failed to retrieve credentials with unclean path %q: %s", creds.ServerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != creds.Username {
|
||||||
|
t.Errorf("Error: expected username %s, got %s", creds.Username, username)
|
||||||
|
}
|
||||||
|
if secret != creds.Secret {
|
||||||
|
t.Errorf("Error: expected secret %s, got %s", creds.Secret, secret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestOSXKeychainHelperRetrieveStrict verifies that only matching secrets are
|
// TestOSXKeychainHelperRetrieveStrict verifies that only matching secrets are
|
||||||
// returned.
|
// returned.
|
||||||
func TestOSXKeychainHelperRetrieveStrict(t *testing.T) {
|
func TestOSXKeychainHelperRetrieveStrict(t *testing.T) {
|
||||||
|
|||||||
+23
-17
@@ -87,8 +87,7 @@ func (p Pass) Add(creds *credentials.Credentials) error {
|
|||||||
return errors.New("missing credentials")
|
return errors.New("missing credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded := base64.URLEncoding.EncodeToString([]byte(creds.ServerURL))
|
encoded := encodeServerURL(creds.ServerURL)
|
||||||
|
|
||||||
_, err := p.runPass(creds.Secret, "insert", "-f", "-m", path.Join(PASS_FOLDER, encoded, creds.Username))
|
_, err := p.runPass(creds.Secret, "insert", "-f", "-m", path.Join(PASS_FOLDER, encoded, creds.Username))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ func (p Pass) Delete(serverURL string) error {
|
|||||||
return errors.New("missing server url")
|
return errors.New("missing server url")
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))
|
encoded := encodeServerURL(serverURL)
|
||||||
_, err := p.runPass("", "rm", "-rf", path.Join(PASS_FOLDER, encoded))
|
_, err := p.runPass("", "rm", "-rf", path.Join(PASS_FOLDER, encoded))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -142,23 +141,14 @@ func (p Pass) Get(serverURL string) (string, string, error) {
|
|||||||
return "", "", errors.New("missing server url")
|
return "", "", errors.New("missing server url")
|
||||||
}
|
}
|
||||||
|
|
||||||
encoded := base64.URLEncoding.EncodeToString([]byte(serverURL))
|
encoded := encodeServerURL(serverURL)
|
||||||
|
|
||||||
if _, err := os.Stat(path.Join(getPassDir(), PASS_FOLDER, encoded)); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return "", "", credentials.NewErrCredentialsNotFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
usernames, err := listPassDir(encoded)
|
usernames, err := listPassDir(encoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(usernames) < 1 {
|
if len(usernames) < 1 {
|
||||||
return "", "", fmt.Errorf("no usernames for %s", serverURL)
|
return "", "", credentials.NewErrCredentialsNotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := strings.TrimSuffix(usernames[0].Name(), ".gpg")
|
actual := strings.TrimSuffix(usernames[0].Name(), ".gpg")
|
||||||
@@ -180,7 +170,7 @@ func (p Pass) List() (map[string]string, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
serverURL, err := base64.URLEncoding.DecodeString(server.Name())
|
serverURL, err := decodeServerURL(server.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -191,11 +181,27 @@ func (p Pass) List() (map[string]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(usernames) < 1 {
|
if len(usernames) < 1 {
|
||||||
return nil, fmt.Errorf("no usernames for %s", serverURL)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
resp[string(serverURL)] = strings.TrimSuffix(usernames[0].Name(), ".gpg")
|
resp[serverURL] = strings.TrimSuffix(usernames[0].Name(), ".gpg")
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodeServerURL returns the serverURL in base64-URL encoding to use
|
||||||
|
// as directory-name in pass storage.
|
||||||
|
func encodeServerURL(serverURL string) string {
|
||||||
|
return base64.URLEncoding.EncodeToString([]byte(serverURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeServerURL decodes base64-URL encoded serverURL. ServerURLs are
|
||||||
|
// used in encoded format for directory-names in pass storage.
|
||||||
|
func decodeServerURL(encodedServerURL string) (string, error) {
|
||||||
|
serverURL, err := base64.URLEncoding.DecodeString(encodedServerURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(serverURL), nil
|
||||||
|
}
|
||||||
|
|||||||
+73
-2
@@ -3,6 +3,8 @@
|
|||||||
package pass
|
package pass
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -81,10 +83,10 @@ func TestPassHelperList(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
for server, username := range credsList {
|
for server, username := range credsList {
|
||||||
if !(strings.HasSuffix(server, "2376/v1") || strings.HasSuffix(server, "2375/v1")) {
|
if !strings.HasSuffix(server, "2376/v1") && !strings.HasSuffix(server, "2375/v1") {
|
||||||
t.Errorf("invalid url: %s", server)
|
t.Errorf("invalid url: %s", server)
|
||||||
}
|
}
|
||||||
if !(username == "foo" || username == "bar") {
|
if username != "foo" && username != "bar" {
|
||||||
t.Errorf("invalid username: %v", username)
|
t.Errorf("invalid username: %v", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +118,75 @@ func TestPassHelperList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPassHelperWithEmptyServer verifies that empty directories (servers
|
||||||
|
// without credentials) are ignored, but still returns credentials for other
|
||||||
|
// servers.
|
||||||
|
func TestPassHelperWithEmptyServer(t *testing.T) {
|
||||||
|
helper := Pass{}
|
||||||
|
if err := helper.checkInitialized(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
creds := []*credentials.Credentials{
|
||||||
|
{
|
||||||
|
ServerURL: "https://myreqistry.example.com:2375/v1",
|
||||||
|
Username: "foo",
|
||||||
|
Secret: "isthebestmeshuggahalbum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ServerURL: "https://index.example.com/v1//access-token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
for _, cred := range creds {
|
||||||
|
_ = helper.Delete(cred.ServerURL)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, cred := range creds {
|
||||||
|
if cred.Username != "" {
|
||||||
|
if err := helper.Add(cred); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No credentials; create an empty directory for this server.
|
||||||
|
serverURL := encodeServerURL(cred.ServerURL)
|
||||||
|
p := path.Join(getPassDir(), PASS_FOLDER, serverURL)
|
||||||
|
if err := os.Mkdir(p, 0o755); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
credsList, err := helper.List()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if len(credsList) == 0 {
|
||||||
|
t.Error("expected credentials to be returned, but got none")
|
||||||
|
}
|
||||||
|
for _, cred := range creds {
|
||||||
|
if cred.Username != "" {
|
||||||
|
userName, secret, err := helper.Get(cred.ServerURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if userName != cred.Username {
|
||||||
|
t.Errorf("expected username %q, actual: %q", cred.Username, userName)
|
||||||
|
}
|
||||||
|
if secret != cred.Secret {
|
||||||
|
t.Errorf("expected secret %q, actual: %q", cred.Secret, secret)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, _, err := helper.Get(cred.ServerURL)
|
||||||
|
if !credentials.IsErrCredentialsNotFound(err) {
|
||||||
|
t.Errorf("expected credentials not found, actual: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMissingCred(t *testing.T) {
|
func TestMissingCred(t *testing.T) {
|
||||||
helper := Pass{}
|
helper := Pass{}
|
||||||
if _, _, err := helper.Get("garbage"); !credentials.IsErrCredentialsNotFound(err) {
|
if _, _, err := helper.Get("garbage"); !credentials.IsErrCredentialsNotFound(err) {
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ const SecretSchema *docker_get_schema(void)
|
|||||||
return &docker_schema;
|
return &docker_schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
GError *add(char *label, char *server, char *username, char *secret) {
|
GError *add(char *label, char *server, char *username, char *secret, char *displaylabel) {
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
|
|
||||||
secret_password_store_sync (DOCKER_SCHEMA, SECRET_COLLECTION_DEFAULT,
|
secret_password_store_sync (DOCKER_SCHEMA, SECRET_COLLECTION_DEFAULT,
|
||||||
server, secret, NULL, &err,
|
displaylabel, secret, NULL, &err,
|
||||||
"label", label,
|
"label", label,
|
||||||
"server", server,
|
"server", server,
|
||||||
"username", username,
|
"username", username,
|
||||||
@@ -83,6 +83,9 @@ GError *get(char *server, char **username, char **secret) {
|
|||||||
}
|
}
|
||||||
g_free(value);
|
g_free(value);
|
||||||
secretValue = secret_item_get_secret(l->data);
|
secretValue = secret_item_get_secret(l->data);
|
||||||
|
if (secretValue == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (secret != NULL) {
|
if (secret != NULL) {
|
||||||
*secret = strdup(secret_value_get(secretValue, &length));
|
*secret = strdup(secret_value_get(secretValue, &length));
|
||||||
secret_value_unref(secretValue);
|
secret_value_unref(secretValue);
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ func (h Secretservice) Add(creds *credentials.Credentials) error {
|
|||||||
defer C.free(unsafe.Pointer(username))
|
defer C.free(unsafe.Pointer(username))
|
||||||
secret := C.CString(creds.Secret)
|
secret := C.CString(creds.Secret)
|
||||||
defer C.free(unsafe.Pointer(secret))
|
defer C.free(unsafe.Pointer(secret))
|
||||||
|
displayLabel := C.CString("Registry credentials for " + creds.ServerURL)
|
||||||
|
defer C.free(unsafe.Pointer(displayLabel))
|
||||||
|
|
||||||
if err := C.add(credsLabel, server, username, secret); err != nil {
|
if err := C.add(credsLabel, server, username, secret, displayLabel); err != nil {
|
||||||
defer C.g_error_free(err)
|
defer C.g_error_free(err)
|
||||||
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
errMsg := (*C.char)(unsafe.Pointer(err.message))
|
||||||
return errors.New(C.GoString(errMsg))
|
return errors.New(C.GoString(errMsg))
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const SecretSchema *docker_get_schema(void) G_GNUC_CONST;
|
|||||||
|
|
||||||
#define DOCKER_SCHEMA docker_get_schema()
|
#define DOCKER_SCHEMA docker_get_schema()
|
||||||
|
|
||||||
GError *add(char *label, char *server, char *username, char *secret);
|
GError *add(char *label, char *server, char *username, char *secret, char *displaylabel);
|
||||||
GError *delete(char *server);
|
GError *delete(char *server);
|
||||||
GError *get(char *server, char **username, char **secret);
|
GError *get(char *server, char **username, char **secret);
|
||||||
GError *list(char *label, char *** paths, char *** accts, unsigned int *list_l);
|
GError *list(char *label, char *** paths, char *** accts, unsigned int *list_l);
|
||||||
|
|||||||
+7
-13
@@ -31,13 +31,13 @@ func utf16FromString(str string) []uint16 {
|
|||||||
|
|
||||||
// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`).
|
// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`).
|
||||||
// This function avoids having cgo as dependency.
|
// This function avoids having cgo as dependency.
|
||||||
func goBytes(src uintptr, len uint32) []byte {
|
func goBytes(src *byte, len uint32) []byte {
|
||||||
if src == uintptr(0) {
|
if src == nil || len == 0 {
|
||||||
return []byte{}
|
return []byte{}
|
||||||
}
|
}
|
||||||
rv := make([]byte, len)
|
rv := make([]byte, len)
|
||||||
copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||||
Data: src,
|
Data: uintptr(unsafe.Pointer(src)),
|
||||||
Len: int(len),
|
Len: int(len),
|
||||||
Cap: int(len),
|
Cap: int(len),
|
||||||
})))
|
})))
|
||||||
@@ -59,7 +59,7 @@ func sysToCredential(cred *sysCREDENTIAL) (result *Credential) {
|
|||||||
result.CredentialBlob = goBytes(cred.CredentialBlob, cred.CredentialBlobSize)
|
result.CredentialBlob = goBytes(cred.CredentialBlob, cred.CredentialBlobSize)
|
||||||
result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
|
result.Attributes = make([]CredentialAttribute, cred.AttributeCount)
|
||||||
attrSlice := *(*[]sysCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&reflect.SliceHeader{
|
attrSlice := *(*[]sysCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&reflect.SliceHeader{
|
||||||
Data: cred.Attributes,
|
Data: uintptr(unsafe.Pointer(cred.Attributes)),
|
||||||
Len: int(cred.AttributeCount),
|
Len: int(cred.AttributeCount),
|
||||||
Cap: int(cred.AttributeCount),
|
Cap: int(cred.AttributeCount),
|
||||||
}))
|
}))
|
||||||
@@ -85,17 +85,13 @@ func sysFromCredential(cred *Credential) (result *sysCREDENTIAL) {
|
|||||||
result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
|
result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano())
|
||||||
result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
|
result.CredentialBlobSize = uint32(len(cred.CredentialBlob))
|
||||||
if len(cred.CredentialBlob) > 0 {
|
if len(cred.CredentialBlob) > 0 {
|
||||||
result.CredentialBlob = uintptr(unsafe.Pointer(&cred.CredentialBlob[0]))
|
result.CredentialBlob = &cred.CredentialBlob[0]
|
||||||
} else {
|
|
||||||
result.CredentialBlob = 0
|
|
||||||
}
|
}
|
||||||
result.Persist = uint32(cred.Persist)
|
result.Persist = uint32(cred.Persist)
|
||||||
result.AttributeCount = uint32(len(cred.Attributes))
|
result.AttributeCount = uint32(len(cred.Attributes))
|
||||||
attributes := make([]sysCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
|
attributes := make([]sysCREDENTIAL_ATTRIBUTE, len(cred.Attributes))
|
||||||
if len(attributes) > 0 {
|
if len(attributes) > 0 {
|
||||||
result.Attributes = uintptr(unsafe.Pointer(&attributes[0]))
|
result.Attributes = &attributes[0]
|
||||||
} else {
|
|
||||||
result.Attributes = 0
|
|
||||||
}
|
}
|
||||||
for i := range cred.Attributes {
|
for i := range cred.Attributes {
|
||||||
inAttr := &cred.Attributes[i]
|
inAttr := &cred.Attributes[i]
|
||||||
@@ -104,9 +100,7 @@ func sysFromCredential(cred *Credential) (result *sysCREDENTIAL) {
|
|||||||
outAttr.Flags = 0
|
outAttr.Flags = 0
|
||||||
outAttr.ValueSize = uint32(len(inAttr.Value))
|
outAttr.ValueSize = uint32(len(inAttr.Value))
|
||||||
if len(inAttr.Value) > 0 {
|
if len(inAttr.Value) > 0 {
|
||||||
outAttr.Value = uintptr(unsafe.Pointer(&inAttr.Value[0]))
|
outAttr.Value = &inAttr.Value[0]
|
||||||
} else {
|
|
||||||
outAttr.Value = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
|
result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias)
|
||||||
|
|||||||
+7
-3
@@ -5,6 +5,7 @@ package wincred
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -33,10 +34,10 @@ type sysCREDENTIAL struct {
|
|||||||
Comment *uint16
|
Comment *uint16
|
||||||
LastWritten windows.Filetime
|
LastWritten windows.Filetime
|
||||||
CredentialBlobSize uint32
|
CredentialBlobSize uint32
|
||||||
CredentialBlob uintptr
|
CredentialBlob *byte
|
||||||
Persist uint32
|
Persist uint32
|
||||||
AttributeCount uint32
|
AttributeCount uint32
|
||||||
Attributes uintptr
|
Attributes *sysCREDENTIAL_ATTRIBUTE
|
||||||
TargetAlias *uint16
|
TargetAlias *uint16
|
||||||
UserName *uint16
|
UserName *uint16
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ type sysCREDENTIAL_ATTRIBUTE struct {
|
|||||||
Keyword *uint16
|
Keyword *uint16
|
||||||
Flags uint32
|
Flags uint32
|
||||||
ValueSize uint32
|
ValueSize uint32
|
||||||
Value uintptr
|
Value *byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
|
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
|
||||||
@@ -63,6 +64,7 @@ const (
|
|||||||
// https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
|
// https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes
|
||||||
sysERROR_NOT_FOUND = windows.Errno(1168)
|
sysERROR_NOT_FOUND = windows.Errno(1168)
|
||||||
sysERROR_INVALID_PARAMETER = windows.Errno(87)
|
sysERROR_INVALID_PARAMETER = windows.Errno(87)
|
||||||
|
sysERROR_BAD_USERNAME = windows.Errno(2202)
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
|
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
|
||||||
@@ -92,6 +94,8 @@ func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error {
|
|||||||
uintptr(unsafe.Pointer(ncred)),
|
uintptr(unsafe.Pointer(ncred)),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
// Make sure everything reachable from ncred stays alive through the call.
|
||||||
|
runtime.KeepAlive(ncred)
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package wincred
|
package wincred
|
||||||
@@ -17,6 +18,7 @@ const (
|
|||||||
|
|
||||||
sysERROR_NOT_FOUND = syscall.Errno(1)
|
sysERROR_NOT_FOUND = syscall.Errno(1)
|
||||||
sysERROR_INVALID_PARAMETER = syscall.Errno(1)
|
sysERROR_INVALID_PARAMETER = syscall.Errno(1)
|
||||||
|
sysERROR_BAD_USERNAME = syscall.Errno(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
func sysCredRead(...interface{}) (*Credential, error) {
|
func sysCredRead(...interface{}) (*Credential, error) {
|
||||||
|
|||||||
+3
@@ -16,6 +16,9 @@ const (
|
|||||||
// This error constant can be used to check if the given function parameters were invalid.
|
// This error constant can be used to check if the given function parameters were invalid.
|
||||||
// For example when trying to create a new generic credential with an empty target name.
|
// For example when trying to create a new generic credential with an empty target name.
|
||||||
ErrInvalidParameter = sysERROR_INVALID_PARAMETER
|
ErrInvalidParameter = sysERROR_INVALID_PARAMETER
|
||||||
|
|
||||||
|
// ErrBadUsername is returned when the credential's username is invalid.
|
||||||
|
ErrBadUsername = sysERROR_BAD_USERNAME
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetGenericCredential fetches the generic credential with the given name from Windows credential manager.
|
// GetGenericCredential fetches the generic credential with the given name from Windows credential manager.
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
vendor
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
linters-settings:
|
||||||
|
gocritic:
|
||||||
|
disabled-checks:
|
||||||
|
- ifElseChain
|
||||||
|
- elseif
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- gocritic
|
||||||
|
- unconvert
|
||||||
|
- revive
|
||||||
|
- govet
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Keybase
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
# Go Keychain
|
||||||
|
|
||||||
|
[](https://github.com/keybase/go-keychain/actions)
|
||||||
|
|
||||||
|
A library for accessing the Keychain for macOS, iOS, and Linux in Go (golang).
|
||||||
|
|
||||||
|
Requires macOS 10.9 or greater and iOS 8 or greater. On Linux, communicates to
|
||||||
|
a provider of the DBUS SecretService spec like gnome-keyring or ksecretservice.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/keybase/go-keychain"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mac/iOS Usage
|
||||||
|
|
||||||
|
The API is meant to mirror the macOS/iOS Keychain API and is not necessarily idiomatic go.
|
||||||
|
|
||||||
|
#### Add Item
|
||||||
|
|
||||||
|
```go
|
||||||
|
item := keychain.NewItem()
|
||||||
|
item.SetSecClass(keychain.SecClassGenericPassword)
|
||||||
|
item.SetService("MyService")
|
||||||
|
item.SetAccount("gabriel")
|
||||||
|
item.SetLabel("A label")
|
||||||
|
item.SetAccessGroup("A123456789.group.com.mycorp")
|
||||||
|
item.SetData([]byte("toomanysecrets"))
|
||||||
|
item.SetSynchronizable(keychain.SynchronizableNo)
|
||||||
|
item.SetAccessible(keychain.AccessibleWhenUnlocked)
|
||||||
|
err := keychain.AddItem(item)
|
||||||
|
|
||||||
|
if err == keychain.ErrorDuplicateItem {
|
||||||
|
// Duplicate
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Query Item
|
||||||
|
|
||||||
|
Query for multiple results, returning attributes:
|
||||||
|
|
||||||
|
```go
|
||||||
|
query := keychain.NewItem()
|
||||||
|
query.SetSecClass(keychain.SecClassGenericPassword)
|
||||||
|
query.SetService(service)
|
||||||
|
query.SetAccount(account)
|
||||||
|
query.SetAccessGroup(accessGroup)
|
||||||
|
query.SetMatchLimit(keychain.MatchLimitAll)
|
||||||
|
query.SetReturnAttributes(true)
|
||||||
|
results, err := keychain.QueryItem(query)
|
||||||
|
if err != nil {
|
||||||
|
// Error
|
||||||
|
} else {
|
||||||
|
for _, r := range results {
|
||||||
|
fmt.Printf("%#v\n", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Query for a single result, returning data:
|
||||||
|
|
||||||
|
```go
|
||||||
|
query := keychain.NewItem()
|
||||||
|
query.SetSecClass(keychain.SecClassGenericPassword)
|
||||||
|
query.SetService(service)
|
||||||
|
query.SetAccount(account)
|
||||||
|
query.SetAccessGroup(accessGroup)
|
||||||
|
query.SetMatchLimit(keychain.MatchLimitOne)
|
||||||
|
query.SetReturnData(true)
|
||||||
|
results, err := keychain.QueryItem(query)
|
||||||
|
if err != nil {
|
||||||
|
// Error
|
||||||
|
} else if len(results) != 1 {
|
||||||
|
// Not found
|
||||||
|
} else {
|
||||||
|
password := string(results[0].Data)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Delete Item
|
||||||
|
|
||||||
|
Delete a generic password item with service and account:
|
||||||
|
|
||||||
|
```go
|
||||||
|
item := keychain.NewItem()
|
||||||
|
item.SetSecClass(keychain.SecClassGenericPassword)
|
||||||
|
item.SetService(service)
|
||||||
|
item.SetAccount(account)
|
||||||
|
err := keychain.DeleteItem(item)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
There are some convenience methods for generic password:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create generic password item with service, account, label, password, access group
|
||||||
|
item := keychain.NewGenericPassword("MyService", "gabriel", "A label", []byte("toomanysecrets"), "A123456789.group.com.mycorp")
|
||||||
|
item.SetSynchronizable(keychain.SynchronizableNo)
|
||||||
|
item.SetAccessible(keychain.AccessibleWhenUnlocked)
|
||||||
|
err := keychain.AddItem(item)
|
||||||
|
if err == keychain.ErrorDuplicateItem {
|
||||||
|
// Duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
password, err := keychain.GetGenericPassword("MyService", "gabriel", "A label", "A123456789.group.com.mycorp")
|
||||||
|
|
||||||
|
accounts, err := keychain.GetGenericPasswordAccounts("MyService")
|
||||||
|
// Should have 1 account == "gabriel"
|
||||||
|
|
||||||
|
err := keychain.DeleteGenericPasswordItem("MyService", "gabriel")
|
||||||
|
if err == keychain.ErrorItemNotFound {
|
||||||
|
// Not found
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## iOS
|
||||||
|
|
||||||
|
Bindable package in `bind`. iOS project in `ios`. Run that project to test iOS.
|
||||||
|
|
||||||
|
To re-generate framework:
|
||||||
|
|
||||||
|
```
|
||||||
|
(cd bind && gomobile bind -target=ios -tags=ios -o ../ios/bind.framework)
|
||||||
|
```
|
||||||
|
|
||||||
|
Post issues to: https://github.com/keybase/keybase-issues
|
||||||
+370
@@ -0,0 +1,370 @@
|
|||||||
|
//go:build darwin || ios
|
||||||
|
// +build darwin ios
|
||||||
|
|
||||||
|
package keychain
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
|
// Can't cast a *uintptr to *unsafe.Pointer in Go, and casting
|
||||||
|
// C.CFTypeRef to unsafe.Pointer is unsafe in Go, so have shim functions to
|
||||||
|
// do the casting in C (where it's safe).
|
||||||
|
|
||||||
|
// We add a suffix to the C functions below, because we copied this
|
||||||
|
// file from go-kext, which means that any project that depends on this
|
||||||
|
// package and go-kext would run into duplicate symbol errors otherwise.
|
||||||
|
//
|
||||||
|
// TODO: Move this file into its own package depended on by go-kext
|
||||||
|
// and this package.
|
||||||
|
|
||||||
|
CFDictionaryRef CFDictionaryCreateSafe2(CFAllocatorRef allocator, const uintptr_t *keys, const uintptr_t *values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) {
|
||||||
|
return CFDictionaryCreate(allocator, (const void **)keys, (const void **)values, numValues, keyCallBacks, valueCallBacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFArrayRef CFArrayCreateSafe2(CFAllocatorRef allocator, const uintptr_t *values, CFIndex numValues, const CFArrayCallBacks *callBacks) {
|
||||||
|
return CFArrayCreate(allocator, (const void **)values, numValues, callBacks);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"unicode/utf8"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Release releases memory pointed to by a CFTypeRef.
|
||||||
|
func Release(ref C.CFTypeRef) {
|
||||||
|
C.CFRelease(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToCFData will return a CFDataRef and if non-nil, must be released with
|
||||||
|
// Release(ref).
|
||||||
|
func BytesToCFData(b []byte) (C.CFDataRef, error) {
|
||||||
|
if uint64(len(b)) > math.MaxUint32 {
|
||||||
|
return 0, errors.New("Data is too large")
|
||||||
|
}
|
||||||
|
var p *C.UInt8
|
||||||
|
if len(b) > 0 {
|
||||||
|
p = (*C.UInt8)(&b[0])
|
||||||
|
}
|
||||||
|
cfData := C.CFDataCreate(C.kCFAllocatorDefault, p, C.CFIndex(len(b)))
|
||||||
|
if cfData == 0 {
|
||||||
|
return 0, fmt.Errorf("CFDataCreate failed")
|
||||||
|
}
|
||||||
|
return cfData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFDataToBytes converts CFData to bytes.
|
||||||
|
func CFDataToBytes(cfData C.CFDataRef) ([]byte, error) {
|
||||||
|
return C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(cfData)), C.int(C.CFDataGetLength(cfData))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapToCFDictionary will return a CFDictionaryRef and if non-nil, must be
|
||||||
|
// released with Release(ref).
|
||||||
|
func MapToCFDictionary(m map[C.CFTypeRef]C.CFTypeRef) (C.CFDictionaryRef, error) {
|
||||||
|
var keys, values []C.uintptr_t
|
||||||
|
for key, value := range m {
|
||||||
|
keys = append(keys, C.uintptr_t(key))
|
||||||
|
values = append(values, C.uintptr_t(value))
|
||||||
|
}
|
||||||
|
numValues := len(values)
|
||||||
|
var keysPointer, valuesPointer *C.uintptr_t
|
||||||
|
if numValues > 0 {
|
||||||
|
keysPointer = &keys[0]
|
||||||
|
valuesPointer = &values[0]
|
||||||
|
}
|
||||||
|
cfDict := C.CFDictionaryCreateSafe2(C.kCFAllocatorDefault, keysPointer, valuesPointer, C.CFIndex(numValues),
|
||||||
|
&C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) //nolint
|
||||||
|
if cfDict == 0 {
|
||||||
|
return 0, fmt.Errorf("CFDictionaryCreate failed")
|
||||||
|
}
|
||||||
|
return cfDict, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFDictionaryToMap converts CFDictionaryRef to a map.
|
||||||
|
func CFDictionaryToMap(cfDict C.CFDictionaryRef) (m map[C.CFTypeRef]C.CFTypeRef) {
|
||||||
|
count := C.CFDictionaryGetCount(cfDict)
|
||||||
|
if count > 0 {
|
||||||
|
keys := make([]C.CFTypeRef, count)
|
||||||
|
values := make([]C.CFTypeRef, count)
|
||||||
|
C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(unsafe.Pointer(&keys[0])), (*unsafe.Pointer)(unsafe.Pointer(&values[0])))
|
||||||
|
m = make(map[C.CFTypeRef]C.CFTypeRef, count)
|
||||||
|
for i := C.CFIndex(0); i < count; i++ {
|
||||||
|
m[keys[i]] = values[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32ToCFNumber will return a CFNumberRef, must be released with Release(ref).
|
||||||
|
func Int32ToCFNumber(u int32) C.CFNumberRef {
|
||||||
|
sint := C.SInt32(u)
|
||||||
|
p := unsafe.Pointer(&sint)
|
||||||
|
return C.CFNumberCreate(C.kCFAllocatorDefault, C.kCFNumberSInt32Type, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToCFString will return a CFStringRef and if non-nil, must be released with
|
||||||
|
// Release(ref).
|
||||||
|
func StringToCFString(s string) (C.CFStringRef, error) {
|
||||||
|
if !utf8.ValidString(s) {
|
||||||
|
return 0, errors.New("Invalid UTF-8 string")
|
||||||
|
}
|
||||||
|
if uint64(len(s)) > math.MaxUint32 {
|
||||||
|
return 0, errors.New("String is too large")
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := []byte(s)
|
||||||
|
var p *C.UInt8
|
||||||
|
if len(bytes) > 0 {
|
||||||
|
p = (*C.UInt8)(&bytes[0])
|
||||||
|
}
|
||||||
|
return C.CFStringCreateWithBytes(C.kCFAllocatorDefault, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFStringToString converts a CFStringRef to a string.
|
||||||
|
func CFStringToString(s C.CFStringRef) string {
|
||||||
|
p := C.CFStringGetCStringPtr(s, C.kCFStringEncodingUTF8)
|
||||||
|
if p != nil {
|
||||||
|
return C.GoString(p)
|
||||||
|
}
|
||||||
|
length := C.CFStringGetLength(s)
|
||||||
|
if length == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
maxBufLen := C.CFStringGetMaximumSizeForEncoding(length, C.kCFStringEncodingUTF8)
|
||||||
|
if maxBufLen == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
buf := make([]byte, maxBufLen)
|
||||||
|
var usedBufLen C.CFIndex
|
||||||
|
_ = C.CFStringGetBytes(s, C.CFRange{0, length}, C.kCFStringEncodingUTF8, C.UInt8(0), C.false, (*C.UInt8)(&buf[0]), maxBufLen, &usedBufLen)
|
||||||
|
return string(buf[:usedBufLen])
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayToCFArray will return a CFArrayRef and if non-nil, must be released with
|
||||||
|
// Release(ref).
|
||||||
|
func ArrayToCFArray(a []C.CFTypeRef) C.CFArrayRef {
|
||||||
|
var values []C.uintptr_t
|
||||||
|
for _, value := range a {
|
||||||
|
values = append(values, C.uintptr_t(value))
|
||||||
|
}
|
||||||
|
numValues := len(values)
|
||||||
|
var valuesPointer *C.uintptr_t
|
||||||
|
if numValues > 0 {
|
||||||
|
valuesPointer = &values[0]
|
||||||
|
}
|
||||||
|
return C.CFArrayCreateSafe2(C.kCFAllocatorDefault, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) //nolint
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFArrayToArray converts a CFArrayRef to an array of CFTypes.
|
||||||
|
func CFArrayToArray(cfArray C.CFArrayRef) (a []C.CFTypeRef) {
|
||||||
|
count := C.CFArrayGetCount(cfArray)
|
||||||
|
if count > 0 {
|
||||||
|
a = make([]C.CFTypeRef, count)
|
||||||
|
C.CFArrayGetValues(cfArray, C.CFRange{0, count}, (*unsafe.Pointer)(unsafe.Pointer(&a[0])))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertable knows how to convert an instance to a CFTypeRef.
|
||||||
|
type Convertable interface {
|
||||||
|
Convert() (C.CFTypeRef, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertMapToCFDictionary converts a map to a CFDictionary and if non-nil,
|
||||||
|
// must be released with Release(ref).
|
||||||
|
func ConvertMapToCFDictionary(attr map[string]interface{}) (C.CFDictionaryRef, error) {
|
||||||
|
m := make(map[C.CFTypeRef]C.CFTypeRef)
|
||||||
|
for key, i := range attr {
|
||||||
|
var valueRef C.CFTypeRef
|
||||||
|
switch val := i.(type) {
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("Unsupported value type: %v", reflect.TypeOf(i))
|
||||||
|
case C.CFTypeRef:
|
||||||
|
valueRef = val
|
||||||
|
case bool:
|
||||||
|
if val {
|
||||||
|
valueRef = C.CFTypeRef(C.kCFBooleanTrue)
|
||||||
|
} else {
|
||||||
|
valueRef = C.CFTypeRef(C.kCFBooleanFalse)
|
||||||
|
}
|
||||||
|
case int32:
|
||||||
|
valueRef = C.CFTypeRef(Int32ToCFNumber(val))
|
||||||
|
defer Release(valueRef)
|
||||||
|
case []byte:
|
||||||
|
bytesRef, err := BytesToCFData(val)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
valueRef = C.CFTypeRef(bytesRef)
|
||||||
|
defer Release(valueRef)
|
||||||
|
case string:
|
||||||
|
stringRef, err := StringToCFString(val)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
valueRef = C.CFTypeRef(stringRef)
|
||||||
|
defer Release(valueRef)
|
||||||
|
case Convertable:
|
||||||
|
convertedRef, err := val.Convert()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
valueRef = convertedRef
|
||||||
|
defer Release(valueRef)
|
||||||
|
}
|
||||||
|
keyRef, err := StringToCFString(key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
m[C.CFTypeRef(keyRef)] = valueRef
|
||||||
|
defer Release(C.CFTypeRef(keyRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
cfDict, err := MapToCFDictionary(m)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return cfDict, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFTypeDescription returns type string for CFTypeRef.
|
||||||
|
func CFTypeDescription(ref C.CFTypeRef) string {
|
||||||
|
typeID := C.CFGetTypeID(ref)
|
||||||
|
typeDesc := C.CFCopyTypeIDDescription(typeID)
|
||||||
|
defer Release(C.CFTypeRef(typeDesc))
|
||||||
|
return CFStringToString(typeDesc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert converts a CFTypeRef to a go instance.
|
||||||
|
func Convert(ref C.CFTypeRef) (interface{}, error) {
|
||||||
|
typeID := C.CFGetTypeID(ref)
|
||||||
|
if typeID == C.CFStringGetTypeID() {
|
||||||
|
return CFStringToString(C.CFStringRef(ref)), nil
|
||||||
|
} else if typeID == C.CFDictionaryGetTypeID() {
|
||||||
|
return ConvertCFDictionary(C.CFDictionaryRef(ref))
|
||||||
|
} else if typeID == C.CFArrayGetTypeID() {
|
||||||
|
arr := CFArrayToArray(C.CFArrayRef(ref))
|
||||||
|
results := make([]interface{}, 0, len(arr))
|
||||||
|
for _, ref := range arr {
|
||||||
|
v, err := Convert(ref)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, v)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
} else if typeID == C.CFDataGetTypeID() {
|
||||||
|
b, err := CFDataToBytes(C.CFDataRef(ref))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
} else if typeID == C.CFNumberGetTypeID() {
|
||||||
|
return CFNumberToInterface(C.CFNumberRef(ref)), nil
|
||||||
|
} else if typeID == C.CFBooleanGetTypeID() {
|
||||||
|
if C.CFBooleanGetValue(C.CFBooleanRef(ref)) != 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Invalid type: %s", CFTypeDescription(ref))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertCFDictionary converts a CFDictionary to map (deep).
|
||||||
|
func ConvertCFDictionary(d C.CFDictionaryRef) (map[interface{}]interface{}, error) {
|
||||||
|
m := CFDictionaryToMap(d)
|
||||||
|
result := make(map[interface{}]interface{})
|
||||||
|
|
||||||
|
for k, v := range m {
|
||||||
|
gk, err := Convert(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gv, err := Convert(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[gk] = gv
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFNumberToInterface converts the CFNumberRef to the most appropriate numeric
|
||||||
|
// type.
|
||||||
|
// This code is from github.com/kballard/go-osx-plist.
|
||||||
|
func CFNumberToInterface(cfNumber C.CFNumberRef) interface{} {
|
||||||
|
typ := C.CFNumberGetType(cfNumber)
|
||||||
|
switch typ {
|
||||||
|
case C.kCFNumberSInt8Type:
|
||||||
|
var sint C.SInt8
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
|
||||||
|
return int8(sint)
|
||||||
|
case C.kCFNumberSInt16Type:
|
||||||
|
var sint C.SInt16
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
|
||||||
|
return int16(sint)
|
||||||
|
case C.kCFNumberSInt32Type:
|
||||||
|
var sint C.SInt32
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
|
||||||
|
return int32(sint)
|
||||||
|
case C.kCFNumberSInt64Type:
|
||||||
|
var sint C.SInt64
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&sint)) //nolint
|
||||||
|
return int64(sint)
|
||||||
|
case C.kCFNumberFloat32Type:
|
||||||
|
var float C.Float32
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
|
||||||
|
return float32(float)
|
||||||
|
case C.kCFNumberFloat64Type:
|
||||||
|
var float C.Float64
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
|
||||||
|
return float64(float)
|
||||||
|
case C.kCFNumberCharType:
|
||||||
|
var char C.char
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&char)) //nolint
|
||||||
|
return byte(char)
|
||||||
|
case C.kCFNumberShortType:
|
||||||
|
var short C.short
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&short)) //nolint
|
||||||
|
return int16(short)
|
||||||
|
case C.kCFNumberIntType:
|
||||||
|
var i C.int
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&i)) //nolint
|
||||||
|
return int32(i)
|
||||||
|
case C.kCFNumberLongType:
|
||||||
|
var long C.long
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&long)) //nolint
|
||||||
|
return int(long)
|
||||||
|
case C.kCFNumberLongLongType:
|
||||||
|
// This is the only type that may actually overflow us
|
||||||
|
var longlong C.longlong
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&longlong)) //nolint
|
||||||
|
return int64(longlong)
|
||||||
|
case C.kCFNumberFloatType:
|
||||||
|
var float C.float
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&float)) //nolint
|
||||||
|
return float32(float)
|
||||||
|
case C.kCFNumberDoubleType:
|
||||||
|
var double C.double
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&double)) //nolint
|
||||||
|
return float64(double)
|
||||||
|
case C.kCFNumberCFIndexType:
|
||||||
|
// CFIndex is a long
|
||||||
|
var index C.CFIndex
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&index)) //nolint
|
||||||
|
return int(index)
|
||||||
|
case C.kCFNumberNSIntegerType:
|
||||||
|
// We don't have a definition of NSInteger, but we know it's either an int or a long
|
||||||
|
var nsInt C.long
|
||||||
|
C.CFNumberGetValue(cfNumber, typ, unsafe.Pointer(&nsInt)) //nolint
|
||||||
|
return int(nsInt)
|
||||||
|
}
|
||||||
|
panic("Unknown CFNumber type")
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
//go:build darwin || ios
|
||||||
|
// +build darwin ios
|
||||||
|
|
||||||
|
package keychain
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const nsPerSec = 1000 * 1000 * 1000
|
||||||
|
|
||||||
|
// absoluteTimeIntervalSince1970() returns the number of seconds from
|
||||||
|
// the Unix epoch (1970-01-01T00:00:00+00:00) to the Core Foundation
|
||||||
|
// absolute reference date (2001-01-01T00:00:00+00:00). It should be
|
||||||
|
// exactly 978307200.
|
||||||
|
func absoluteTimeIntervalSince1970() int64 {
|
||||||
|
return int64(C.kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unixToAbsoluteTime(s int64, ns int64) C.CFAbsoluteTime {
|
||||||
|
// Subtract as int64s first before converting to floating
|
||||||
|
// point to minimize precision loss (assuming the given time
|
||||||
|
// isn't much earlier than the Core Foundation absolute
|
||||||
|
// reference date).
|
||||||
|
abs := s - absoluteTimeIntervalSince1970()
|
||||||
|
return C.CFAbsoluteTime(abs) + C.CFTimeInterval(ns)/nsPerSec
|
||||||
|
}
|
||||||
|
|
||||||
|
func absoluteTimeToUnix(abs C.CFAbsoluteTime) (int64, int64) {
|
||||||
|
i, frac := math.Modf(float64(abs))
|
||||||
|
return int64(i) + absoluteTimeIntervalSince1970(), int64(frac * nsPerSec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeToCFDate will convert the given time.Time to a CFDateRef, which
|
||||||
|
// must be released with Release(ref).
|
||||||
|
func TimeToCFDate(t time.Time) C.CFDateRef {
|
||||||
|
s := t.Unix()
|
||||||
|
ns := int64(t.Nanosecond())
|
||||||
|
abs := unixToAbsoluteTime(s, ns)
|
||||||
|
return C.CFDateCreate(C.kCFAllocatorDefault, abs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFDateToTime will convert the given CFDateRef to a time.Time.
|
||||||
|
func CFDateToTime(d C.CFDateRef) time.Time {
|
||||||
|
abs := C.CFDateGetAbsoluteTime(d)
|
||||||
|
s, ns := absoluteTimeToUnix(abs)
|
||||||
|
return time.Unix(s, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrappers around C functions for testing.
|
||||||
|
|
||||||
|
func cfDateToAbsoluteTime(d C.CFDateRef) C.CFAbsoluteTime {
|
||||||
|
return C.CFDateGetAbsoluteTime(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func absoluteTimeToCFDate(abs C.CFAbsoluteTime) C.CFDateRef {
|
||||||
|
return C.CFDateCreate(C.kCFAllocatorDefault, abs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseCFDate(d C.CFDateRef) {
|
||||||
|
Release(C.CFTypeRef(d))
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
//go:build darwin && ios
|
||||||
|
// +build darwin,ios
|
||||||
|
|
||||||
|
package keychain
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
var AccessibleKey = attrKey(C.CFTypeRef(C.kSecAttrAccessible))
|
||||||
|
var accessibleTypeRef = map[Accessible]C.CFTypeRef{
|
||||||
|
AccessibleWhenUnlocked: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlocked),
|
||||||
|
AccessibleAfterFirstUnlock: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlock),
|
||||||
|
AccessibleAlways: C.CFTypeRef(C.kSecAttrAccessibleAlways),
|
||||||
|
AccessibleWhenPasscodeSetThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
|
||||||
|
AccessibleWhenUnlockedThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||||
|
AccessibleAfterFirstUnlockThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly),
|
||||||
|
AccessibleAccessibleAlwaysThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAlwaysThisDeviceOnly),
|
||||||
|
}
|
||||||
+653
@@ -0,0 +1,653 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package keychain
|
||||||
|
|
||||||
|
// See https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html for the APIs used below.
|
||||||
|
|
||||||
|
// Also see https://developer.apple.com/library/ios/documentation/Security/Conceptual/keychainServConcepts/01introduction/introduction.html .
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error defines keychain errors
|
||||||
|
type Error int
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrorUnimplemented corresponds to errSecUnimplemented result code
|
||||||
|
ErrorUnimplemented = Error(C.errSecUnimplemented)
|
||||||
|
// ErrorParam corresponds to errSecParam result code
|
||||||
|
ErrorParam = Error(C.errSecParam)
|
||||||
|
// ErrorAllocate corresponds to errSecAllocate result code
|
||||||
|
ErrorAllocate = Error(C.errSecAllocate)
|
||||||
|
// ErrorNotAvailable corresponds to errSecNotAvailable result code
|
||||||
|
ErrorNotAvailable = Error(C.errSecNotAvailable)
|
||||||
|
// ErrorAuthFailed corresponds to errSecAuthFailed result code
|
||||||
|
ErrorAuthFailed = Error(C.errSecAuthFailed)
|
||||||
|
// ErrorDuplicateItem corresponds to errSecDuplicateItem result code
|
||||||
|
ErrorDuplicateItem = Error(C.errSecDuplicateItem)
|
||||||
|
// ErrorItemNotFound corresponds to errSecItemNotFound result code
|
||||||
|
ErrorItemNotFound = Error(C.errSecItemNotFound)
|
||||||
|
// ErrorInteractionNotAllowed corresponds to errSecInteractionNotAllowed result code
|
||||||
|
ErrorInteractionNotAllowed = Error(C.errSecInteractionNotAllowed)
|
||||||
|
// ErrorDecode corresponds to errSecDecode result code
|
||||||
|
ErrorDecode = Error(C.errSecDecode)
|
||||||
|
// ErrorNoSuchKeychain corresponds to errSecNoSuchKeychain result code
|
||||||
|
ErrorNoSuchKeychain = Error(C.errSecNoSuchKeychain)
|
||||||
|
// ErrorNoAccessForItem corresponds to errSecNoAccessForItem result code
|
||||||
|
ErrorNoAccessForItem = Error(C.errSecNoAccessForItem)
|
||||||
|
// ErrorReadOnly corresponds to errSecReadOnly result code
|
||||||
|
ErrorReadOnly = Error(C.errSecReadOnly)
|
||||||
|
// ErrorInvalidKeychain corresponds to errSecInvalidKeychain result code
|
||||||
|
ErrorInvalidKeychain = Error(C.errSecInvalidKeychain)
|
||||||
|
// ErrorDuplicateKeyChain corresponds to errSecDuplicateKeychain result code
|
||||||
|
ErrorDuplicateKeyChain = Error(C.errSecDuplicateKeychain)
|
||||||
|
// ErrorWrongVersion corresponds to errSecWrongSecVersion result code
|
||||||
|
ErrorWrongVersion = Error(C.errSecWrongSecVersion)
|
||||||
|
// ErrorReadonlyAttribute corresponds to errSecReadOnlyAttr result code
|
||||||
|
ErrorReadonlyAttribute = Error(C.errSecReadOnlyAttr)
|
||||||
|
// ErrorInvalidSearchRef corresponds to errSecInvalidSearchRef result code
|
||||||
|
ErrorInvalidSearchRef = Error(C.errSecInvalidSearchRef)
|
||||||
|
// ErrorInvalidItemRef corresponds to errSecInvalidItemRef result code
|
||||||
|
ErrorInvalidItemRef = Error(C.errSecInvalidItemRef)
|
||||||
|
// ErrorDataNotAvailable corresponds to errSecDataNotAvailable result code
|
||||||
|
ErrorDataNotAvailable = Error(C.errSecDataNotAvailable)
|
||||||
|
// ErrorDataNotModifiable corresponds to errSecDataNotModifiable result code
|
||||||
|
ErrorDataNotModifiable = Error(C.errSecDataNotModifiable)
|
||||||
|
// ErrorInvalidOwnerEdit corresponds to errSecInvalidOwnerEdit result code
|
||||||
|
ErrorInvalidOwnerEdit = Error(C.errSecInvalidOwnerEdit)
|
||||||
|
// ErrorUserCanceled corresponds to errSecUserCanceled result code
|
||||||
|
ErrorUserCanceled = Error(C.errSecUserCanceled)
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkError(errCode C.OSStatus) error {
|
||||||
|
if errCode == C.errSecSuccess {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Error(errCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Error) Error() (msg string) {
|
||||||
|
// SecCopyErrorMessageString is only available on OSX, so derive manually.
|
||||||
|
// Messages derived from `$ security error $errcode`.
|
||||||
|
switch k {
|
||||||
|
case ErrorUnimplemented:
|
||||||
|
msg = "Function or operation not implemented."
|
||||||
|
case ErrorParam:
|
||||||
|
msg = "One or more parameters passed to the function were not valid."
|
||||||
|
case ErrorAllocate:
|
||||||
|
msg = "Failed to allocate memory."
|
||||||
|
case ErrorNotAvailable:
|
||||||
|
msg = "No keychain is available. You may need to restart your computer."
|
||||||
|
case ErrorAuthFailed:
|
||||||
|
msg = "The user name or passphrase you entered is not correct."
|
||||||
|
case ErrorDuplicateItem:
|
||||||
|
msg = "The specified item already exists in the keychain."
|
||||||
|
case ErrorItemNotFound:
|
||||||
|
msg = "The specified item could not be found in the keychain."
|
||||||
|
case ErrorInteractionNotAllowed:
|
||||||
|
msg = "User interaction is not allowed."
|
||||||
|
case ErrorDecode:
|
||||||
|
msg = "Unable to decode the provided data."
|
||||||
|
case ErrorNoSuchKeychain:
|
||||||
|
msg = "The specified keychain could not be found."
|
||||||
|
case ErrorNoAccessForItem:
|
||||||
|
msg = "The specified item has no access control."
|
||||||
|
case ErrorReadOnly:
|
||||||
|
msg = "Read-only error."
|
||||||
|
case ErrorReadonlyAttribute:
|
||||||
|
msg = "The attribute is read-only."
|
||||||
|
case ErrorInvalidKeychain:
|
||||||
|
msg = "The keychain is not valid."
|
||||||
|
case ErrorDuplicateKeyChain:
|
||||||
|
msg = "A keychain with the same name already exists."
|
||||||
|
case ErrorWrongVersion:
|
||||||
|
msg = "The version is incorrect."
|
||||||
|
case ErrorInvalidItemRef:
|
||||||
|
msg = "The item reference is invalid."
|
||||||
|
case ErrorInvalidSearchRef:
|
||||||
|
msg = "The search reference is invalid."
|
||||||
|
case ErrorDataNotAvailable:
|
||||||
|
msg = "The data is not available."
|
||||||
|
case ErrorDataNotModifiable:
|
||||||
|
msg = "The data is not modifiable."
|
||||||
|
case ErrorInvalidOwnerEdit:
|
||||||
|
msg = "An invalid attempt to change the owner of an item."
|
||||||
|
case ErrorUserCanceled:
|
||||||
|
msg = "User canceled the operation."
|
||||||
|
default:
|
||||||
|
msg = "Keychain Error."
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (%d)", msg, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecClass is the items class code
|
||||||
|
type SecClass int
|
||||||
|
|
||||||
|
// Keychain Item Classes
|
||||||
|
var (
|
||||||
|
/*
|
||||||
|
kSecClassGenericPassword item attributes:
|
||||||
|
kSecAttrAccess (OS X only)
|
||||||
|
kSecAttrAccessGroup (iOS; also OS X if kSecAttrSynchronizable specified)
|
||||||
|
kSecAttrAccessible (iOS; also OS X if kSecAttrSynchronizable specified)
|
||||||
|
kSecAttrAccount
|
||||||
|
kSecAttrService
|
||||||
|
*/
|
||||||
|
SecClassGenericPassword SecClass = 1
|
||||||
|
SecClassInternetPassword SecClass = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecClassKey is the key type for SecClass
|
||||||
|
var SecClassKey = attrKey(C.CFTypeRef(C.kSecClass))
|
||||||
|
var secClassTypeRef = map[SecClass]C.CFTypeRef{
|
||||||
|
SecClassGenericPassword: C.CFTypeRef(C.kSecClassGenericPassword),
|
||||||
|
SecClassInternetPassword: C.CFTypeRef(C.kSecClassInternetPassword),
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ServiceKey is for kSecAttrService
|
||||||
|
ServiceKey = attrKey(C.CFTypeRef(C.kSecAttrService))
|
||||||
|
|
||||||
|
// ServerKey is for kSecAttrServer
|
||||||
|
ServerKey = attrKey(C.CFTypeRef(C.kSecAttrServer))
|
||||||
|
// ProtocolKey is for kSecAttrProtocol
|
||||||
|
ProtocolKey = attrKey(C.CFTypeRef(C.kSecAttrProtocol))
|
||||||
|
// AuthenticationTypeKey is for kSecAttrAuthenticationType
|
||||||
|
AuthenticationTypeKey = attrKey(C.CFTypeRef(C.kSecAttrAuthenticationType))
|
||||||
|
// PortKey is for kSecAttrPort
|
||||||
|
PortKey = attrKey(C.CFTypeRef(C.kSecAttrPort))
|
||||||
|
// PathKey is for kSecAttrPath
|
||||||
|
PathKey = attrKey(C.CFTypeRef(C.kSecAttrPath))
|
||||||
|
|
||||||
|
// LabelKey is for kSecAttrLabel
|
||||||
|
LabelKey = attrKey(C.CFTypeRef(C.kSecAttrLabel))
|
||||||
|
// AccountKey is for kSecAttrAccount
|
||||||
|
AccountKey = attrKey(C.CFTypeRef(C.kSecAttrAccount))
|
||||||
|
// AccessGroupKey is for kSecAttrAccessGroup
|
||||||
|
AccessGroupKey = attrKey(C.CFTypeRef(C.kSecAttrAccessGroup))
|
||||||
|
// DataKey is for kSecValueData
|
||||||
|
DataKey = attrKey(C.CFTypeRef(C.kSecValueData))
|
||||||
|
// DescriptionKey is for kSecAttrDescription
|
||||||
|
DescriptionKey = attrKey(C.CFTypeRef(C.kSecAttrDescription))
|
||||||
|
// CommentKey is for kSecAttrComment
|
||||||
|
CommentKey = attrKey(C.CFTypeRef(C.kSecAttrComment))
|
||||||
|
// CreationDateKey is for kSecAttrCreationDate
|
||||||
|
CreationDateKey = attrKey(C.CFTypeRef(C.kSecAttrCreationDate))
|
||||||
|
// ModificationDateKey is for kSecAttrModificationDate
|
||||||
|
ModificationDateKey = attrKey(C.CFTypeRef(C.kSecAttrModificationDate))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Synchronizable is the items synchronizable status
|
||||||
|
type Synchronizable int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SynchronizableDefault is the default setting
|
||||||
|
SynchronizableDefault Synchronizable = 0
|
||||||
|
// SynchronizableAny is for kSecAttrSynchronizableAny
|
||||||
|
SynchronizableAny = 1
|
||||||
|
// SynchronizableYes enables synchronization
|
||||||
|
SynchronizableYes = 2
|
||||||
|
// SynchronizableNo disables synchronization
|
||||||
|
SynchronizableNo = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SynchronizableKey is the key type for Synchronizable
|
||||||
|
var SynchronizableKey = attrKey(C.CFTypeRef(C.kSecAttrSynchronizable))
|
||||||
|
var syncTypeRef = map[Synchronizable]C.CFTypeRef{
|
||||||
|
SynchronizableAny: C.CFTypeRef(C.kSecAttrSynchronizableAny),
|
||||||
|
SynchronizableYes: C.CFTypeRef(C.kCFBooleanTrue),
|
||||||
|
SynchronizableNo: C.CFTypeRef(C.kCFBooleanFalse),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessible is the items accessibility
|
||||||
|
type Accessible int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AccessibleDefault is the default
|
||||||
|
AccessibleDefault Accessible = 0
|
||||||
|
// AccessibleWhenUnlocked is when unlocked
|
||||||
|
AccessibleWhenUnlocked = 1
|
||||||
|
// AccessibleAfterFirstUnlock is after first unlock
|
||||||
|
AccessibleAfterFirstUnlock = 2
|
||||||
|
// AccessibleAlways is always
|
||||||
|
AccessibleAlways = 3
|
||||||
|
// AccessibleWhenPasscodeSetThisDeviceOnly is when passcode is set
|
||||||
|
AccessibleWhenPasscodeSetThisDeviceOnly = 4
|
||||||
|
// AccessibleWhenUnlockedThisDeviceOnly is when unlocked for this device only
|
||||||
|
AccessibleWhenUnlockedThisDeviceOnly = 5
|
||||||
|
// AccessibleAfterFirstUnlockThisDeviceOnly is after first unlock for this device only
|
||||||
|
AccessibleAfterFirstUnlockThisDeviceOnly = 6
|
||||||
|
// AccessibleAccessibleAlwaysThisDeviceOnly is always for this device only
|
||||||
|
AccessibleAccessibleAlwaysThisDeviceOnly = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatchLimit is whether to limit results on query
|
||||||
|
type MatchLimit int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MatchLimitDefault is the default
|
||||||
|
MatchLimitDefault MatchLimit = 0
|
||||||
|
// MatchLimitOne limits to one result
|
||||||
|
MatchLimitOne = 1
|
||||||
|
// MatchLimitAll is no limit
|
||||||
|
MatchLimitAll = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatchLimitKey is key type for MatchLimit
|
||||||
|
var MatchLimitKey = attrKey(C.CFTypeRef(C.kSecMatchLimit))
|
||||||
|
var matchTypeRef = map[MatchLimit]C.CFTypeRef{
|
||||||
|
MatchLimitOne: C.CFTypeRef(C.kSecMatchLimitOne),
|
||||||
|
MatchLimitAll: C.CFTypeRef(C.kSecMatchLimitAll),
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnAttributesKey is key type for kSecReturnAttributes
|
||||||
|
var ReturnAttributesKey = attrKey(C.CFTypeRef(C.kSecReturnAttributes))
|
||||||
|
|
||||||
|
// ReturnDataKey is key type for kSecReturnData
|
||||||
|
var ReturnDataKey = attrKey(C.CFTypeRef(C.kSecReturnData))
|
||||||
|
|
||||||
|
// ReturnRefKey is key type for kSecReturnRef
|
||||||
|
var ReturnRefKey = attrKey(C.CFTypeRef(C.kSecReturnRef))
|
||||||
|
|
||||||
|
// Item for adding, querying or deleting.
|
||||||
|
type Item struct {
|
||||||
|
// Values can be string, []byte, Convertable or CFTypeRef (constant).
|
||||||
|
attr map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSecClass sets the security class
|
||||||
|
func (k *Item) SetSecClass(sc SecClass) {
|
||||||
|
k.attr[SecClassKey] = secClassTypeRef[sc]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt32 sets an int32 attribute for a string key
|
||||||
|
func (k *Item) SetInt32(key string, v int32) {
|
||||||
|
if v != 0 {
|
||||||
|
k.attr[key] = v
|
||||||
|
} else {
|
||||||
|
delete(k.attr, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetString sets a string attibute for a string key
|
||||||
|
func (k *Item) SetString(key string, s string) {
|
||||||
|
if s != "" {
|
||||||
|
k.attr[key] = s
|
||||||
|
} else {
|
||||||
|
delete(k.attr, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetService sets the service attribute (for generic application items)
|
||||||
|
func (k *Item) SetService(s string) {
|
||||||
|
k.SetString(ServiceKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetServer sets the server attribute (for internet password items)
|
||||||
|
func (k *Item) SetServer(s string) {
|
||||||
|
k.SetString(ServerKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProtocol sets the protocol attribute (for internet password items)
|
||||||
|
// Example values are: "htps", "http", "smb "
|
||||||
|
func (k *Item) SetProtocol(s string) {
|
||||||
|
k.SetString(ProtocolKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthenticationType sets the authentication type attribute (for internet password items)
|
||||||
|
func (k *Item) SetAuthenticationType(s string) {
|
||||||
|
k.SetString(AuthenticationTypeKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPort sets the port attribute (for internet password items)
|
||||||
|
func (k *Item) SetPort(v int32) {
|
||||||
|
k.SetInt32(PortKey, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPath sets the path attribute (for internet password items)
|
||||||
|
func (k *Item) SetPath(s string) {
|
||||||
|
k.SetString(PathKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccount sets the account attribute
|
||||||
|
func (k *Item) SetAccount(a string) {
|
||||||
|
k.SetString(AccountKey, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabel sets the label attribute
|
||||||
|
func (k *Item) SetLabel(l string) {
|
||||||
|
k.SetString(LabelKey, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDescription sets the description attribute
|
||||||
|
func (k *Item) SetDescription(s string) {
|
||||||
|
k.SetString(DescriptionKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetComment sets the comment attribute
|
||||||
|
func (k *Item) SetComment(s string) {
|
||||||
|
k.SetString(CommentKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetData sets the data attribute
|
||||||
|
func (k *Item) SetData(b []byte) {
|
||||||
|
if b != nil {
|
||||||
|
k.attr[DataKey] = b
|
||||||
|
} else {
|
||||||
|
delete(k.attr, DataKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccessGroup sets the access group attribute
|
||||||
|
func (k *Item) SetAccessGroup(ag string) {
|
||||||
|
k.SetString(AccessGroupKey, ag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSynchronizable sets the synchronizable attribute
|
||||||
|
func (k *Item) SetSynchronizable(sync Synchronizable) {
|
||||||
|
if sync != SynchronizableDefault {
|
||||||
|
k.attr[SynchronizableKey] = syncTypeRef[sync]
|
||||||
|
} else {
|
||||||
|
delete(k.attr, SynchronizableKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccessible sets the accessible attribute
|
||||||
|
func (k *Item) SetAccessible(accessible Accessible) {
|
||||||
|
if accessible != AccessibleDefault {
|
||||||
|
k.attr[AccessibleKey] = accessibleTypeRef[accessible]
|
||||||
|
} else {
|
||||||
|
delete(k.attr, AccessibleKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMatchLimit sets the match limit
|
||||||
|
func (k *Item) SetMatchLimit(matchLimit MatchLimit) {
|
||||||
|
if matchLimit != MatchLimitDefault {
|
||||||
|
k.attr[MatchLimitKey] = matchTypeRef[matchLimit]
|
||||||
|
} else {
|
||||||
|
delete(k.attr, MatchLimitKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReturnAttributes sets the return value type on query
|
||||||
|
func (k *Item) SetReturnAttributes(b bool) {
|
||||||
|
k.attr[ReturnAttributesKey] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReturnData enables returning data on query
|
||||||
|
func (k *Item) SetReturnData(b bool) {
|
||||||
|
k.attr[ReturnDataKey] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReturnRef enables returning references on query
|
||||||
|
func (k *Item) SetReturnRef(b bool) {
|
||||||
|
k.attr[ReturnRefKey] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewItem is a new empty keychain item
|
||||||
|
func NewItem() Item {
|
||||||
|
return Item{make(map[string]interface{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGenericPassword creates a generic password item with the default keychain. This is a convenience method.
|
||||||
|
func NewGenericPassword(service string, account string, label string, data []byte, accessGroup string) Item {
|
||||||
|
item := NewItem()
|
||||||
|
item.SetSecClass(SecClassGenericPassword)
|
||||||
|
item.SetService(service)
|
||||||
|
item.SetAccount(account)
|
||||||
|
item.SetLabel(label)
|
||||||
|
item.SetData(data)
|
||||||
|
item.SetAccessGroup(accessGroup)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddItem adds a Item to a Keychain
|
||||||
|
func AddItem(item Item) error {
|
||||||
|
cfDict, err := ConvertMapToCFDictionary(item.attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer Release(C.CFTypeRef(cfDict))
|
||||||
|
|
||||||
|
errCode := C.SecItemAdd(cfDict, nil)
|
||||||
|
err = checkError(errCode)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateItem updates the queryItem with the parameters from updateItem
|
||||||
|
func UpdateItem(queryItem Item, updateItem Item) error {
|
||||||
|
cfDict, err := ConvertMapToCFDictionary(queryItem.attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer Release(C.CFTypeRef(cfDict))
|
||||||
|
cfDictUpdate, err := ConvertMapToCFDictionary(updateItem.attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer Release(C.CFTypeRef(cfDictUpdate))
|
||||||
|
errCode := C.SecItemUpdate(cfDict, cfDictUpdate)
|
||||||
|
err = checkError(errCode)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryResult stores all possible results from queries.
|
||||||
|
// Not all fields are applicable all the time. Results depend on query.
|
||||||
|
type QueryResult struct {
|
||||||
|
// For generic application items
|
||||||
|
Service string
|
||||||
|
|
||||||
|
// For internet password items
|
||||||
|
Server string
|
||||||
|
Protocol string
|
||||||
|
AuthenticationType string
|
||||||
|
Port int32
|
||||||
|
Path string
|
||||||
|
|
||||||
|
Account string
|
||||||
|
AccessGroup string
|
||||||
|
Label string
|
||||||
|
Description string
|
||||||
|
Comment string
|
||||||
|
Data []byte
|
||||||
|
CreationDate time.Time
|
||||||
|
ModificationDate time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryItemRef returns query result as CFTypeRef. You must release it when you are done.
|
||||||
|
func QueryItemRef(item Item) (C.CFTypeRef, error) {
|
||||||
|
cfDict, err := ConvertMapToCFDictionary(item.attr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer Release(C.CFTypeRef(cfDict))
|
||||||
|
|
||||||
|
var resultsRef C.CFTypeRef
|
||||||
|
errCode := C.SecItemCopyMatching(cfDict, &resultsRef) //nolint
|
||||||
|
if Error(errCode) == ErrorItemNotFound {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
err = checkError(errCode)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return resultsRef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryItem returns a list of query results.
|
||||||
|
func QueryItem(item Item) ([]QueryResult, error) {
|
||||||
|
resultsRef, err := QueryItemRef(item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resultsRef == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
defer Release(resultsRef)
|
||||||
|
|
||||||
|
results := make([]QueryResult, 0, 1)
|
||||||
|
|
||||||
|
typeID := C.CFGetTypeID(resultsRef)
|
||||||
|
if typeID == C.CFArrayGetTypeID() {
|
||||||
|
arr := CFArrayToArray(C.CFArrayRef(resultsRef))
|
||||||
|
for _, ref := range arr {
|
||||||
|
elementTypeID := C.CFGetTypeID(ref)
|
||||||
|
if elementTypeID == C.CFDictionaryGetTypeID() {
|
||||||
|
item, err := convertResult(C.CFDictionaryRef(ref))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, *item)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid result type (If you SetReturnRef(true) you should use QueryItemRef directly)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if typeID == C.CFDictionaryGetTypeID() {
|
||||||
|
item, err := convertResult(C.CFDictionaryRef(resultsRef))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, *item)
|
||||||
|
} else if typeID == C.CFDataGetTypeID() {
|
||||||
|
b, err := CFDataToBytes(C.CFDataRef(resultsRef))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
item := QueryResult{Data: b}
|
||||||
|
results = append(results, item)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Invalid result type: %s", CFTypeDescription(resultsRef))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func attrKey(ref C.CFTypeRef) string {
|
||||||
|
return CFStringToString(C.CFStringRef(ref))
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertResult(d C.CFDictionaryRef) (*QueryResult, error) {
|
||||||
|
m := CFDictionaryToMap(d)
|
||||||
|
result := QueryResult{}
|
||||||
|
for k, v := range m {
|
||||||
|
switch attrKey(k) {
|
||||||
|
case ServiceKey:
|
||||||
|
result.Service = CFStringToString(C.CFStringRef(v))
|
||||||
|
case ServerKey:
|
||||||
|
result.Server = CFStringToString(C.CFStringRef(v))
|
||||||
|
case ProtocolKey:
|
||||||
|
result.Protocol = CFStringToString(C.CFStringRef(v))
|
||||||
|
case AuthenticationTypeKey:
|
||||||
|
result.AuthenticationType = CFStringToString(C.CFStringRef(v))
|
||||||
|
case PortKey:
|
||||||
|
val := CFNumberToInterface(C.CFNumberRef(v))
|
||||||
|
result.Port = val.(int32)
|
||||||
|
case PathKey:
|
||||||
|
result.Path = CFStringToString(C.CFStringRef(v))
|
||||||
|
case AccountKey:
|
||||||
|
result.Account = CFStringToString(C.CFStringRef(v))
|
||||||
|
case AccessGroupKey:
|
||||||
|
result.AccessGroup = CFStringToString(C.CFStringRef(v))
|
||||||
|
case LabelKey:
|
||||||
|
result.Label = CFStringToString(C.CFStringRef(v))
|
||||||
|
case DescriptionKey:
|
||||||
|
result.Description = CFStringToString(C.CFStringRef(v))
|
||||||
|
case CommentKey:
|
||||||
|
result.Comment = CFStringToString(C.CFStringRef(v))
|
||||||
|
case DataKey:
|
||||||
|
b, err := CFDataToBytes(C.CFDataRef(v))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.Data = b
|
||||||
|
case CreationDateKey:
|
||||||
|
result.CreationDate = CFDateToTime(C.CFDateRef(v))
|
||||||
|
case ModificationDateKey:
|
||||||
|
result.ModificationDate = CFDateToTime(C.CFDateRef(v))
|
||||||
|
// default:
|
||||||
|
// fmt.Printf("Unhandled key in conversion: %v = %v\n", cfTypeValue(k), cfTypeValue(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGenericPasswordItem removes a generic password item.
|
||||||
|
func DeleteGenericPasswordItem(service string, account string) error {
|
||||||
|
item := NewItem()
|
||||||
|
item.SetSecClass(SecClassGenericPassword)
|
||||||
|
item.SetService(service)
|
||||||
|
item.SetAccount(account)
|
||||||
|
return DeleteItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteItem removes a Item
|
||||||
|
func DeleteItem(item Item) error {
|
||||||
|
cfDict, err := ConvertMapToCFDictionary(item.attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer Release(C.CFTypeRef(cfDict))
|
||||||
|
|
||||||
|
errCode := C.SecItemDelete(cfDict)
|
||||||
|
return checkError(errCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccountsForService is deprecated
|
||||||
|
func GetAccountsForService(service string) ([]string, error) {
|
||||||
|
return GetGenericPasswordAccounts(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGenericPasswordAccounts returns generic password accounts for service. This is a convenience method.
|
||||||
|
func GetGenericPasswordAccounts(service string) ([]string, error) {
|
||||||
|
query := NewItem()
|
||||||
|
query.SetSecClass(SecClassGenericPassword)
|
||||||
|
query.SetService(service)
|
||||||
|
query.SetMatchLimit(MatchLimitAll)
|
||||||
|
query.SetReturnAttributes(true)
|
||||||
|
results, err := QueryItem(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts := make([]string, 0, len(results))
|
||||||
|
for _, r := range results {
|
||||||
|
accounts = append(accounts, r.Account)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGenericPassword returns password data for service and account. This is a convenience method.
|
||||||
|
// If item is not found returns nil, nil.
|
||||||
|
func GetGenericPassword(service string, account string, label string, accessGroup string) ([]byte, error) {
|
||||||
|
query := NewItem()
|
||||||
|
query.SetSecClass(SecClassGenericPassword)
|
||||||
|
query.SetService(service)
|
||||||
|
query.SetAccount(account)
|
||||||
|
query.SetLabel(label)
|
||||||
|
query.SetAccessGroup(accessGroup)
|
||||||
|
query.SetMatchLimit(MatchLimitOne)
|
||||||
|
query.SetReturnData(true)
|
||||||
|
results, err := QueryItem(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(results) > 1 {
|
||||||
|
return nil, fmt.Errorf("Too many results")
|
||||||
|
}
|
||||||
|
if len(results) == 1 {
|
||||||
|
return results[0].Data, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
//go:build darwin && !ios
|
||||||
|
// +build darwin,!ios
|
||||||
|
|
||||||
|
package keychain
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// AccessibleKey is key for kSecAttrAccessible
|
||||||
|
var AccessibleKey = attrKey(C.CFTypeRef(C.kSecAttrAccessible))
|
||||||
|
var accessibleTypeRef = map[Accessible]C.CFTypeRef{
|
||||||
|
AccessibleWhenUnlocked: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlocked),
|
||||||
|
AccessibleAfterFirstUnlock: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlock),
|
||||||
|
AccessibleAlways: C.CFTypeRef(C.kSecAttrAccessibleAlways),
|
||||||
|
AccessibleWhenUnlockedThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||||
|
AccessibleAfterFirstUnlockThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly),
|
||||||
|
AccessibleAccessibleAlwaysThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleAlwaysThisDeviceOnly),
|
||||||
|
|
||||||
|
// Only available in 10.10
|
||||||
|
//AccessibleWhenPasscodeSetThisDeviceOnly: C.CFTypeRef(C.kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly),
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package keychain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base32"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var randRead = rand.Read
|
||||||
|
|
||||||
|
// RandomID returns random ID (base32) string with prefix, using 256 bits as
|
||||||
|
// recommended by tptacek: https://gist.github.com/tqbf/be58d2d39690c3b366ad
|
||||||
|
func RandomID(prefix string) (string, error) {
|
||||||
|
buf, err := RandBytes(32)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str := base32.StdEncoding.EncodeToString(buf)
|
||||||
|
str = strings.ReplaceAll(str, "=", "")
|
||||||
|
str = prefix + str
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandBytes returns random bytes of length
|
||||||
|
func RandBytes(length int) ([]byte, error) {
|
||||||
|
buf := make([]byte, length)
|
||||||
|
if _, err := randRead(buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2020 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package unsafeheader contains header declarations for the Go runtime's
|
|
||||||
// slice and string implementations.
|
|
||||||
//
|
|
||||||
// This package allows x/sys to use types equivalent to
|
|
||||||
// reflect.SliceHeader and reflect.StringHeader without introducing
|
|
||||||
// a dependency on the (relatively heavy) "reflect" package.
|
|
||||||
package unsafeheader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Slice is the runtime representation of a slice.
|
|
||||||
// It cannot be used safely or portably and its representation may change in a later release.
|
|
||||||
type Slice struct {
|
|
||||||
Data unsafe.Pointer
|
|
||||||
Len int
|
|
||||||
Cap int
|
|
||||||
}
|
|
||||||
|
|
||||||
// String is the runtime representation of a string.
|
|
||||||
// It cannot be used safely or portably and its representation may change in a later release.
|
|
||||||
type String struct {
|
|
||||||
Data unsafe.Pointer
|
|
||||||
Len int
|
|
||||||
}
|
|
||||||
+1
-2
@@ -2,8 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows && go1.9
|
//go:build windows
|
||||||
// +build windows,go1.9
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !go1.12
|
|
||||||
// +build !go1.12
|
|
||||||
|
|
||||||
// This file is here to allow bodyless functions with go:linkname for Go 1.11
|
|
||||||
// and earlier (see https://golang.org/issue/23311).
|
|
||||||
+10
-7
@@ -37,14 +37,17 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer DestroyEnvironmentBlock(block)
|
defer DestroyEnvironmentBlock(block)
|
||||||
blockp := unsafe.Pointer(block)
|
size := unsafe.Sizeof(*block)
|
||||||
for {
|
for *block != 0 {
|
||||||
entry := UTF16PtrToString((*uint16)(blockp))
|
// find NUL terminator
|
||||||
if len(entry) == 0 {
|
end := unsafe.Pointer(block)
|
||||||
break
|
for *(*uint16)(end) != 0 {
|
||||||
|
end = unsafe.Add(end, size)
|
||||||
}
|
}
|
||||||
env = append(env, entry)
|
|
||||||
blockp = unsafe.Add(blockp, 2*(len(entry)+1))
|
entry := unsafe.Slice(block, (uintptr(end)-uintptr(unsafe.Pointer(block)))/size)
|
||||||
|
env = append(env, UTF16ToString(entry))
|
||||||
|
block = (*uint16)(unsafe.Add(end, size))
|
||||||
}
|
}
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
+78
-13
@@ -22,7 +22,7 @@ import (
|
|||||||
// but only if there is space or tab inside s.
|
// but only if there is space or tab inside s.
|
||||||
func EscapeArg(s string) string {
|
func EscapeArg(s string) string {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return "\"\""
|
return `""`
|
||||||
}
|
}
|
||||||
n := len(s)
|
n := len(s)
|
||||||
hasSpace := false
|
hasSpace := false
|
||||||
@@ -35,7 +35,7 @@ func EscapeArg(s string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if hasSpace {
|
if hasSpace {
|
||||||
n += 2
|
n += 2 // Reserve space for quotes.
|
||||||
}
|
}
|
||||||
if n == len(s) {
|
if n == len(s) {
|
||||||
return s
|
return s
|
||||||
@@ -82,20 +82,68 @@ func EscapeArg(s string) string {
|
|||||||
// in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
|
// in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
|
||||||
// or any program that uses CommandLineToArgv.
|
// or any program that uses CommandLineToArgv.
|
||||||
func ComposeCommandLine(args []string) string {
|
func ComposeCommandLine(args []string) string {
|
||||||
var commandLine string
|
if len(args) == 0 {
|
||||||
for i := range args {
|
return ""
|
||||||
if i > 0 {
|
|
||||||
commandLine += " "
|
|
||||||
}
|
|
||||||
commandLine += EscapeArg(args[i])
|
|
||||||
}
|
}
|
||||||
return commandLine
|
|
||||||
|
// Per https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw:
|
||||||
|
// “This function accepts command lines that contain a program name; the
|
||||||
|
// program name can be enclosed in quotation marks or not.”
|
||||||
|
//
|
||||||
|
// Unfortunately, it provides no means of escaping interior quotation marks
|
||||||
|
// within that program name, and we have no way to report them here.
|
||||||
|
prog := args[0]
|
||||||
|
mustQuote := len(prog) == 0
|
||||||
|
for i := 0; i < len(prog); i++ {
|
||||||
|
c := prog[i]
|
||||||
|
if c <= ' ' || (c == '"' && i == 0) {
|
||||||
|
// Force quotes for not only the ASCII space and tab as described in the
|
||||||
|
// MSDN article, but also ASCII control characters.
|
||||||
|
// The documentation for CommandLineToArgvW doesn't say what happens when
|
||||||
|
// the first argument is not a valid program name, but it empirically
|
||||||
|
// seems to drop unquoted control characters.
|
||||||
|
mustQuote = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var commandLine []byte
|
||||||
|
if mustQuote {
|
||||||
|
commandLine = make([]byte, 0, len(prog)+2)
|
||||||
|
commandLine = append(commandLine, '"')
|
||||||
|
for i := 0; i < len(prog); i++ {
|
||||||
|
c := prog[i]
|
||||||
|
if c == '"' {
|
||||||
|
// This quote would interfere with our surrounding quotes.
|
||||||
|
// We have no way to report an error, so just strip out
|
||||||
|
// the offending character instead.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
commandLine = append(commandLine, c)
|
||||||
|
}
|
||||||
|
commandLine = append(commandLine, '"')
|
||||||
|
} else {
|
||||||
|
if len(args) == 1 {
|
||||||
|
// args[0] is a valid command line representing itself.
|
||||||
|
// No need to allocate a new slice or string for it.
|
||||||
|
return prog
|
||||||
|
}
|
||||||
|
commandLine = []byte(prog)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range args[1:] {
|
||||||
|
commandLine = append(commandLine, ' ')
|
||||||
|
// TODO(bcmills): since we're already appending to a slice, it would be nice
|
||||||
|
// to avoid the intermediate allocations of EscapeArg.
|
||||||
|
// Perhaps we can factor out an appendEscapedArg function.
|
||||||
|
commandLine = append(commandLine, EscapeArg(arg)...)
|
||||||
|
}
|
||||||
|
return string(commandLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
|
// DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
|
||||||
// as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
|
// as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
|
||||||
// command lines are passed around.
|
// command lines are passed around.
|
||||||
// DecomposeCommandLine returns error if commandLine contains NUL.
|
// DecomposeCommandLine returns an error if commandLine contains NUL.
|
||||||
func DecomposeCommandLine(commandLine string) ([]string, error) {
|
func DecomposeCommandLine(commandLine string) ([]string, error) {
|
||||||
if len(commandLine) == 0 {
|
if len(commandLine) == 0 {
|
||||||
return []string{}, nil
|
return []string{}, nil
|
||||||
@@ -105,18 +153,35 @@ func DecomposeCommandLine(commandLine string) ([]string, error) {
|
|||||||
return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine")
|
return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine")
|
||||||
}
|
}
|
||||||
var argc int32
|
var argc int32
|
||||||
argv, err := CommandLineToArgv(&utf16CommandLine[0], &argc)
|
argv, err := commandLineToArgv(&utf16CommandLine[0], &argc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer LocalFree(Handle(unsafe.Pointer(argv)))
|
defer LocalFree(Handle(unsafe.Pointer(argv)))
|
||||||
|
|
||||||
var args []string
|
var args []string
|
||||||
for _, v := range (*argv)[:argc] {
|
for _, p := range unsafe.Slice(argv, argc) {
|
||||||
args = append(args, UTF16ToString((*v)[:]))
|
args = append(args, UTF16PtrToString(p))
|
||||||
}
|
}
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommandLineToArgv parses a Unicode command line string and sets
|
||||||
|
// argc to the number of parsed arguments.
|
||||||
|
//
|
||||||
|
// The returned memory should be freed using a single call to LocalFree.
|
||||||
|
//
|
||||||
|
// Note that although the return type of CommandLineToArgv indicates 8192
|
||||||
|
// entries of up to 8192 characters each, the actual count of parsed arguments
|
||||||
|
// may exceed 8192, and the documentation for CommandLineToArgvW does not mention
|
||||||
|
// any bound on the lengths of the individual argument strings.
|
||||||
|
// (See https://go.dev/issue/63236.)
|
||||||
|
func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) {
|
||||||
|
argp, err := commandLineToArgv(cmd, argc)
|
||||||
|
argv = (*[8192]*[8192]uint16)(unsafe.Pointer(argp))
|
||||||
|
return argv, err
|
||||||
|
}
|
||||||
|
|
||||||
func CloseOnExec(fd Handle) {
|
func CloseOnExec(fd Handle) {
|
||||||
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build generate
|
//go:build generate
|
||||||
// +build generate
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows && race
|
//go:build windows && race
|
||||||
// +build windows,race
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows && !race
|
//go:build windows && !race
|
||||||
// +build windows,!race
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
+6
-15
@@ -7,8 +7,6 @@ package windows
|
|||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/internal/unsafeheader"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -1341,21 +1339,14 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor()
|
|||||||
sdLen = min
|
sdLen = min
|
||||||
}
|
}
|
||||||
|
|
||||||
var src []byte
|
src := unsafe.Slice((*byte)(unsafe.Pointer(selfRelativeSD)), sdLen)
|
||||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&src))
|
// SECURITY_DESCRIPTOR has pointers in it, which means checkptr expects for it to
|
||||||
h.Data = unsafe.Pointer(selfRelativeSD)
|
// be aligned properly. When we're copying a Windows-allocated struct to a
|
||||||
h.Len = sdLen
|
// Go-allocated one, make sure that the Go allocation is aligned to the
|
||||||
h.Cap = sdLen
|
// pointer size.
|
||||||
|
|
||||||
const psize = int(unsafe.Sizeof(uintptr(0)))
|
const psize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
var dst []byte
|
|
||||||
h = (*unsafeheader.Slice)(unsafe.Pointer(&dst))
|
|
||||||
alloc := make([]uintptr, (sdLen+psize-1)/psize)
|
alloc := make([]uintptr, (sdLen+psize-1)/psize)
|
||||||
h.Data = (*unsafeheader.Slice)(unsafe.Pointer(&alloc)).Data
|
dst := unsafe.Slice((*byte)(unsafe.Pointer(&alloc[0])), sdLen)
|
||||||
h.Len = sdLen
|
|
||||||
h.Cap = sdLen
|
|
||||||
|
|
||||||
copy(dst, src)
|
copy(dst, src)
|
||||||
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0]))
|
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0]))
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
@@ -218,6 +217,10 @@ type SERVICE_FAILURE_ACTIONS struct {
|
|||||||
Actions *SC_ACTION
|
Actions *SC_ACTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SERVICE_FAILURE_ACTIONS_FLAG struct {
|
||||||
|
FailureActionsOnNonCrashFailures int32
|
||||||
|
}
|
||||||
|
|
||||||
type SC_ACTION struct {
|
type SC_ACTION struct {
|
||||||
Type uint32
|
Type uint32
|
||||||
Delay uint32
|
Delay uint32
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
|
|||||||
-1
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
// Package windows contains an interface to the low-level operating system
|
// Package windows contains an interface to the low-level operating system
|
||||||
// primitives. OS details vary depending on the underlying system, and
|
// primitives. OS details vary depending on the underlying system, and
|
||||||
|
|||||||
+136
-26
@@ -15,8 +15,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/internal/unsafeheader"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handle uintptr
|
type Handle uintptr
|
||||||
@@ -127,22 +125,21 @@ func UTF16PtrToString(p *uint16) string {
|
|||||||
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
|
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
|
||||||
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
|
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
|
||||||
}
|
}
|
||||||
|
return UTF16ToString(unsafe.Slice(p, n))
|
||||||
return string(utf16.Decode(unsafe.Slice(p, n)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Getpagesize() int { return 4096 }
|
func Getpagesize() int { return 4096 }
|
||||||
|
|
||||||
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
|
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
|
||||||
// This is useful when interoperating with Windows code requiring callbacks.
|
// This is useful when interoperating with Windows code requiring callbacks.
|
||||||
// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
|
// The argument is expected to be a function with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
|
||||||
func NewCallback(fn interface{}) uintptr {
|
func NewCallback(fn interface{}) uintptr {
|
||||||
return syscall.NewCallback(fn)
|
return syscall.NewCallback(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCallbackCDecl converts a Go function to a function pointer conforming to the cdecl calling convention.
|
// NewCallbackCDecl converts a Go function to a function pointer conforming to the cdecl calling convention.
|
||||||
// This is useful when interoperating with Windows code requiring callbacks.
|
// This is useful when interoperating with Windows code requiring callbacks.
|
||||||
// The argument is expected to be a function with with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
|
// The argument is expected to be a function with one uintptr-sized result. The function must not have arguments with size larger than the size of uintptr.
|
||||||
func NewCallbackCDecl(fn interface{}) uintptr {
|
func NewCallbackCDecl(fn interface{}) uintptr {
|
||||||
return syscall.NewCallbackCDecl(fn)
|
return syscall.NewCallbackCDecl(fn)
|
||||||
}
|
}
|
||||||
@@ -157,6 +154,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
|
//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
|
||||||
//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
|
//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
|
||||||
//sys SetDefaultDllDirectories(directoryFlags uint32) (err error)
|
//sys SetDefaultDllDirectories(directoryFlags uint32) (err error)
|
||||||
|
//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory
|
||||||
|
//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory
|
||||||
//sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
|
//sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW
|
||||||
//sys GetVersion() (ver uint32, err error)
|
//sys GetVersion() (ver uint32, err error)
|
||||||
//sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
|
//sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
|
||||||
@@ -166,6 +165,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW
|
//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile Handle) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW
|
||||||
//sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW
|
//sys CreateNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *SecurityAttributes) (handle Handle, err error) [failretval==InvalidHandle] = CreateNamedPipeW
|
||||||
//sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error)
|
//sys ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error)
|
||||||
|
//sys DisconnectNamedPipe(pipe Handle) (err error)
|
||||||
//sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error)
|
//sys GetNamedPipeInfo(pipe Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error)
|
||||||
//sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
//sys GetNamedPipeHandleState(pipe Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
//sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState
|
//sys SetNamedPipeHandleState(pipe Handle, state *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32) (err error) = SetNamedPipeHandleState
|
||||||
@@ -194,6 +194,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
|
//sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
|
||||||
//sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
|
//sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
|
||||||
//sys SetEndOfFile(handle Handle) (err error)
|
//sys SetEndOfFile(handle Handle) (err error)
|
||||||
|
//sys SetFileValidData(handle Handle, validDataLength int64) (err error)
|
||||||
//sys GetSystemTimeAsFileTime(time *Filetime)
|
//sys GetSystemTimeAsFileTime(time *Filetime)
|
||||||
//sys GetSystemTimePreciseAsFileTime(time *Filetime)
|
//sys GetSystemTimePreciseAsFileTime(time *Filetime)
|
||||||
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff]
|
//sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) [failretval==0xffffffff]
|
||||||
@@ -216,7 +217,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) = shell32.SHGetKnownFolderPath
|
//sys shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) = shell32.SHGetKnownFolderPath
|
||||||
//sys TerminateProcess(handle Handle, exitcode uint32) (err error)
|
//sys TerminateProcess(handle Handle, exitcode uint32) (err error)
|
||||||
//sys GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)
|
//sys GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)
|
||||||
//sys GetStartupInfo(startupInfo *StartupInfo) (err error) = GetStartupInfoW
|
//sys getStartupInfo(startupInfo *StartupInfo) = GetStartupInfoW
|
||||||
//sys GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error)
|
//sys GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error)
|
||||||
//sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error)
|
//sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error)
|
||||||
//sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) [failretval==0xffffffff]
|
//sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) [failretval==0xffffffff]
|
||||||
@@ -235,12 +236,13 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
|
//sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
|
||||||
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
|
//sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
|
||||||
//sys getTickCount64() (ms uint64) = kernel32.GetTickCount64
|
//sys getTickCount64() (ms uint64) = kernel32.GetTickCount64
|
||||||
|
//sys GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
|
||||||
//sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
|
//sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
|
||||||
//sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
|
//sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
|
||||||
//sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW
|
//sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW
|
||||||
//sys GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) = kernel32.GetFileAttributesExW
|
//sys GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) = kernel32.GetFileAttributesExW
|
||||||
//sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW
|
//sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW
|
||||||
//sys CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) [failretval==nil] = shell32.CommandLineToArgvW
|
//sys commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) [failretval==nil] = shell32.CommandLineToArgvW
|
||||||
//sys LocalFree(hmem Handle) (handle Handle, err error) [failretval!=0]
|
//sys LocalFree(hmem Handle) (handle Handle, err error) [failretval!=0]
|
||||||
//sys LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error)
|
//sys LocalAlloc(flags uint32, length uint32) (ptr uintptr, err error)
|
||||||
//sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error)
|
//sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error)
|
||||||
@@ -299,12 +301,15 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, event Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue
|
//sys RegNotifyChangeKeyValue(key Handle, watchSubtree bool, notifyFilter uint32, event Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue
|
||||||
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
|
//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
|
||||||
//sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
|
//sys ProcessIdToSessionId(pid uint32, sessionid *uint32) (err error) = kernel32.ProcessIdToSessionId
|
||||||
|
//sys ClosePseudoConsole(console Handle) = kernel32.ClosePseudoConsole
|
||||||
|
//sys createPseudoConsole(size uint32, in Handle, out Handle, flags uint32, pconsole *Handle) (hr error) = kernel32.CreatePseudoConsole
|
||||||
//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
|
//sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
|
||||||
//sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
|
//sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
|
||||||
//sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
|
//sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
|
||||||
//sys setConsoleCursorPosition(console Handle, position uint32) (err error) = kernel32.SetConsoleCursorPosition
|
//sys setConsoleCursorPosition(console Handle, position uint32) (err error) = kernel32.SetConsoleCursorPosition
|
||||||
//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
|
//sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW
|
||||||
//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
|
//sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW
|
||||||
|
//sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole
|
||||||
//sys CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot
|
//sys CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot
|
||||||
//sys Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW
|
//sys Module32First(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32FirstW
|
||||||
//sys Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32NextW
|
//sys Module32Next(snapshot Handle, moduleEntry *ModuleEntry32) (err error) = kernel32.Module32NextW
|
||||||
@@ -344,8 +349,19 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
|
//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
|
||||||
//sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32)
|
//sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32)
|
||||||
//sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error)
|
//sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error)
|
||||||
|
//sys ClearCommBreak(handle Handle) (err error)
|
||||||
|
//sys ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error)
|
||||||
|
//sys EscapeCommFunction(handle Handle, dwFunc uint32) (err error)
|
||||||
|
//sys GetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||||
|
//sys GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error)
|
||||||
//sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
//sys GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||||
|
//sys PurgeComm(handle Handle, dwFlags uint32) (err error)
|
||||||
|
//sys SetCommBreak(handle Handle) (err error)
|
||||||
|
//sys SetCommMask(handle Handle, dwEvtMask uint32) (err error)
|
||||||
|
//sys SetCommState(handle Handle, lpDCB *DCB) (err error)
|
||||||
//sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
//sys SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error)
|
||||||
|
//sys SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error)
|
||||||
|
//sys WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error)
|
||||||
//sys GetActiveProcessorCount(groupNumber uint16) (ret uint32)
|
//sys GetActiveProcessorCount(groupNumber uint16) (ret uint32)
|
||||||
//sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32)
|
//sys GetMaximumProcessorCount(groupNumber uint16) (ret uint32)
|
||||||
//sys EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) = user32.EnumWindows
|
//sys EnumWindows(enumFunc uintptr, param unsafe.Pointer) (err error) = user32.EnumWindows
|
||||||
@@ -405,7 +421,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) = version.VerQueryValueW
|
//sys VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) = version.VerQueryValueW
|
||||||
|
|
||||||
// Process Status API (PSAPI)
|
// Process Status API (PSAPI)
|
||||||
//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
|
//sys enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
|
||||||
//sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules
|
//sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules
|
||||||
//sys EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) = psapi.EnumProcessModulesEx
|
//sys EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) = psapi.EnumProcessModulesEx
|
||||||
//sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation
|
//sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation
|
||||||
@@ -437,6 +453,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
|||||||
//sys DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmGetWindowAttribute
|
//sys DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmGetWindowAttribute
|
||||||
//sys DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmSetWindowAttribute
|
//sys DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmSetWindowAttribute
|
||||||
|
|
||||||
|
// Windows Multimedia API
|
||||||
|
//sys TimeBeginPeriod (period uint32) (err error) [failretval != 0] = winmm.timeBeginPeriod
|
||||||
|
//sys TimeEndPeriod (period uint32) (err error) [failretval != 0] = winmm.timeEndPeriod
|
||||||
|
|
||||||
// syscall interface implementation for other packages
|
// syscall interface implementation for other packages
|
||||||
|
|
||||||
// GetCurrentProcess returns the handle for the current process.
|
// GetCurrentProcess returns the handle for the current process.
|
||||||
@@ -964,7 +984,8 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, int32, error) {
|
|||||||
if n > 0 {
|
if n > 0 {
|
||||||
sl += int32(n) + 1
|
sl += int32(n) + 1
|
||||||
}
|
}
|
||||||
if sa.raw.Path[0] == '@' {
|
if sa.raw.Path[0] == '@' || (sa.raw.Path[0] == 0 && sl > 3) {
|
||||||
|
// Check sl > 3 so we don't change unnamed socket behavior.
|
||||||
sa.raw.Path[0] = 0
|
sa.raw.Path[0] = 0
|
||||||
// Don't count trailing NUL for abstract address.
|
// Don't count trailing NUL for abstract address.
|
||||||
sl--
|
sl--
|
||||||
@@ -1354,6 +1375,17 @@ func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) {
|
|||||||
return syscall.EWINDOWS
|
return syscall.EWINDOWS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EnumProcesses(processIds []uint32, bytesReturned *uint32) error {
|
||||||
|
// EnumProcesses syscall expects the size parameter to be in bytes, but the code generated with mksyscall uses
|
||||||
|
// the length of the processIds slice instead. Hence, this wrapper function is added to fix the discrepancy.
|
||||||
|
var p *uint32
|
||||||
|
if len(processIds) > 0 {
|
||||||
|
p = &processIds[0]
|
||||||
|
}
|
||||||
|
size := uint32(len(processIds) * 4)
|
||||||
|
return enumProcesses(p, size, bytesReturned)
|
||||||
|
}
|
||||||
|
|
||||||
func Getpid() (pid int) { return int(GetCurrentProcessId()) }
|
func Getpid() (pid int) { return int(GetCurrentProcessId()) }
|
||||||
|
|
||||||
func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
|
func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
|
||||||
@@ -1613,6 +1645,11 @@ func SetConsoleCursorPosition(console Handle, position Coord) error {
|
|||||||
return setConsoleCursorPosition(console, *((*uint32)(unsafe.Pointer(&position))))
|
return setConsoleCursorPosition(console, *((*uint32)(unsafe.Pointer(&position))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetStartupInfo(startupInfo *StartupInfo) error {
|
||||||
|
getStartupInfo(startupInfo)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s NTStatus) Errno() syscall.Errno {
|
func (s NTStatus) Errno() syscall.Errno {
|
||||||
return rtlNtStatusToDosErrorNoTeb(s)
|
return rtlNtStatusToDosErrorNoTeb(s)
|
||||||
}
|
}
|
||||||
@@ -1647,12 +1684,8 @@ func NewNTUnicodeString(s string) (*NTUnicodeString, error) {
|
|||||||
|
|
||||||
// Slice returns a uint16 slice that aliases the data in the NTUnicodeString.
|
// Slice returns a uint16 slice that aliases the data in the NTUnicodeString.
|
||||||
func (s *NTUnicodeString) Slice() []uint16 {
|
func (s *NTUnicodeString) Slice() []uint16 {
|
||||||
var slice []uint16
|
slice := unsafe.Slice(s.Buffer, s.MaximumLength)
|
||||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&slice))
|
return slice[:s.Length]
|
||||||
hdr.Data = unsafe.Pointer(s.Buffer)
|
|
||||||
hdr.Len = int(s.Length)
|
|
||||||
hdr.Cap = int(s.MaximumLength)
|
|
||||||
return slice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NTUnicodeString) String() string {
|
func (s *NTUnicodeString) String() string {
|
||||||
@@ -1675,12 +1708,8 @@ func NewNTString(s string) (*NTString, error) {
|
|||||||
|
|
||||||
// Slice returns a byte slice that aliases the data in the NTString.
|
// Slice returns a byte slice that aliases the data in the NTString.
|
||||||
func (s *NTString) Slice() []byte {
|
func (s *NTString) Slice() []byte {
|
||||||
var slice []byte
|
slice := unsafe.Slice(s.Buffer, s.MaximumLength)
|
||||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&slice))
|
return slice[:s.Length]
|
||||||
hdr.Data = unsafe.Pointer(s.Buffer)
|
|
||||||
hdr.Len = int(s.Length)
|
|
||||||
hdr.Cap = int(s.MaximumLength)
|
|
||||||
return slice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NTString) String() string {
|
func (s *NTString) String() string {
|
||||||
@@ -1732,10 +1761,7 @@ func LoadResourceData(module, resInfo Handle) (data []byte, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&data))
|
data = unsafe.Slice((*byte)(unsafe.Pointer(ptr)), size)
|
||||||
h.Data = unsafe.Pointer(ptr)
|
|
||||||
h.Len = int(size)
|
|
||||||
h.Cap = int(size)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1806,3 +1832,87 @@ type PSAPI_WORKING_SET_EX_INFORMATION struct {
|
|||||||
// A PSAPI_WORKING_SET_EX_BLOCK union that indicates the attributes of the page at VirtualAddress.
|
// A PSAPI_WORKING_SET_EX_BLOCK union that indicates the attributes of the page at VirtualAddress.
|
||||||
VirtualAttributes PSAPI_WORKING_SET_EX_BLOCK
|
VirtualAttributes PSAPI_WORKING_SET_EX_BLOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreatePseudoConsole creates a windows pseudo console.
|
||||||
|
func CreatePseudoConsole(size Coord, in Handle, out Handle, flags uint32, pconsole *Handle) error {
|
||||||
|
// We need this wrapper to manually cast Coord to uint32. The autogenerated wrappers only
|
||||||
|
// accept arguments that can be casted to uintptr, and Coord can't.
|
||||||
|
return createPseudoConsole(*((*uint32)(unsafe.Pointer(&size))), in, out, flags, pconsole)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizePseudoConsole resizes the internal buffers of the pseudo console to the width and height specified in `size`.
|
||||||
|
func ResizePseudoConsole(pconsole Handle, size Coord) error {
|
||||||
|
// We need this wrapper to manually cast Coord to uint32. The autogenerated wrappers only
|
||||||
|
// accept arguments that can be casted to uintptr, and Coord can't.
|
||||||
|
return resizePseudoConsole(pconsole, *((*uint32)(unsafe.Pointer(&size))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DCB constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-dcb.
|
||||||
|
const (
|
||||||
|
CBR_110 = 110
|
||||||
|
CBR_300 = 300
|
||||||
|
CBR_600 = 600
|
||||||
|
CBR_1200 = 1200
|
||||||
|
CBR_2400 = 2400
|
||||||
|
CBR_4800 = 4800
|
||||||
|
CBR_9600 = 9600
|
||||||
|
CBR_14400 = 14400
|
||||||
|
CBR_19200 = 19200
|
||||||
|
CBR_38400 = 38400
|
||||||
|
CBR_57600 = 57600
|
||||||
|
CBR_115200 = 115200
|
||||||
|
CBR_128000 = 128000
|
||||||
|
CBR_256000 = 256000
|
||||||
|
|
||||||
|
DTR_CONTROL_DISABLE = 0x00000000
|
||||||
|
DTR_CONTROL_ENABLE = 0x00000010
|
||||||
|
DTR_CONTROL_HANDSHAKE = 0x00000020
|
||||||
|
|
||||||
|
RTS_CONTROL_DISABLE = 0x00000000
|
||||||
|
RTS_CONTROL_ENABLE = 0x00001000
|
||||||
|
RTS_CONTROL_HANDSHAKE = 0x00002000
|
||||||
|
RTS_CONTROL_TOGGLE = 0x00003000
|
||||||
|
|
||||||
|
NOPARITY = 0
|
||||||
|
ODDPARITY = 1
|
||||||
|
EVENPARITY = 2
|
||||||
|
MARKPARITY = 3
|
||||||
|
SPACEPARITY = 4
|
||||||
|
|
||||||
|
ONESTOPBIT = 0
|
||||||
|
ONE5STOPBITS = 1
|
||||||
|
TWOSTOPBITS = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// EscapeCommFunction constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-escapecommfunction.
|
||||||
|
const (
|
||||||
|
SETXOFF = 1
|
||||||
|
SETXON = 2
|
||||||
|
SETRTS = 3
|
||||||
|
CLRRTS = 4
|
||||||
|
SETDTR = 5
|
||||||
|
CLRDTR = 6
|
||||||
|
SETBREAK = 8
|
||||||
|
CLRBREAK = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
// PurgeComm constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-purgecomm.
|
||||||
|
const (
|
||||||
|
PURGE_TXABORT = 0x0001
|
||||||
|
PURGE_RXABORT = 0x0002
|
||||||
|
PURGE_TXCLEAR = 0x0004
|
||||||
|
PURGE_RXCLEAR = 0x0008
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetCommMask constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask.
|
||||||
|
const (
|
||||||
|
EV_RXCHAR = 0x0001
|
||||||
|
EV_RXFLAG = 0x0002
|
||||||
|
EV_TXEMPTY = 0x0004
|
||||||
|
EV_CTS = 0x0008
|
||||||
|
EV_DSR = 0x0010
|
||||||
|
EV_RLSD = 0x0020
|
||||||
|
EV_BREAK = 0x0040
|
||||||
|
EV_ERR = 0x0080
|
||||||
|
EV_RING = 0x0100
|
||||||
|
)
|
||||||
|
|||||||
+58
-1
@@ -247,6 +247,7 @@ const (
|
|||||||
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = 0x00020007
|
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = 0x00020007
|
||||||
PROC_THREAD_ATTRIBUTE_UMS_THREAD = 0x00030006
|
PROC_THREAD_ATTRIBUTE_UMS_THREAD = 0x00030006
|
||||||
PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL = 0x0002000b
|
PROC_THREAD_ATTRIBUTE_PROTECTION_LEVEL = 0x0002000b
|
||||||
|
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x00020016
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -1093,7 +1094,33 @@ const (
|
|||||||
|
|
||||||
SOMAXCONN = 0x7fffffff
|
SOMAXCONN = 0x7fffffff
|
||||||
|
|
||||||
TCP_NODELAY = 1
|
TCP_NODELAY = 1
|
||||||
|
TCP_EXPEDITED_1122 = 2
|
||||||
|
TCP_KEEPALIVE = 3
|
||||||
|
TCP_MAXSEG = 4
|
||||||
|
TCP_MAXRT = 5
|
||||||
|
TCP_STDURG = 6
|
||||||
|
TCP_NOURG = 7
|
||||||
|
TCP_ATMARK = 8
|
||||||
|
TCP_NOSYNRETRIES = 9
|
||||||
|
TCP_TIMESTAMPS = 10
|
||||||
|
TCP_OFFLOAD_PREFERENCE = 11
|
||||||
|
TCP_CONGESTION_ALGORITHM = 12
|
||||||
|
TCP_DELAY_FIN_ACK = 13
|
||||||
|
TCP_MAXRTMS = 14
|
||||||
|
TCP_FASTOPEN = 15
|
||||||
|
TCP_KEEPCNT = 16
|
||||||
|
TCP_KEEPIDLE = TCP_KEEPALIVE
|
||||||
|
TCP_KEEPINTVL = 17
|
||||||
|
TCP_FAIL_CONNECT_ON_ICMP_ERROR = 18
|
||||||
|
TCP_ICMP_ERROR_INFO = 19
|
||||||
|
|
||||||
|
UDP_NOCHECKSUM = 1
|
||||||
|
UDP_SEND_MSG_SIZE = 2
|
||||||
|
UDP_RECV_MAX_COALESCED_SIZE = 3
|
||||||
|
UDP_CHECKSUM_COVERAGE = 20
|
||||||
|
|
||||||
|
UDP_COALESCED_INFO = 3
|
||||||
|
|
||||||
SHUT_RD = 0
|
SHUT_RD = 0
|
||||||
SHUT_WR = 1
|
SHUT_WR = 1
|
||||||
@@ -2139,6 +2166,12 @@ const (
|
|||||||
ENABLE_LVB_GRID_WORLDWIDE = 0x10
|
ENABLE_LVB_GRID_WORLDWIDE = 0x10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Pseudo console related constants used for the flags parameter to
|
||||||
|
// CreatePseudoConsole. See: https://learn.microsoft.com/en-us/windows/console/createpseudoconsole
|
||||||
|
const (
|
||||||
|
PSEUDOCONSOLE_INHERIT_CURSOR = 0x1
|
||||||
|
)
|
||||||
|
|
||||||
type Coord struct {
|
type Coord struct {
|
||||||
X int16
|
X int16
|
||||||
Y int16
|
Y int16
|
||||||
@@ -3347,3 +3380,27 @@ type BLOB struct {
|
|||||||
Size uint32
|
Size uint32
|
||||||
BlobData *byte
|
BlobData *byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ComStat struct {
|
||||||
|
Flags uint32
|
||||||
|
CBInQue uint32
|
||||||
|
CBOutQue uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type DCB struct {
|
||||||
|
DCBlength uint32
|
||||||
|
BaudRate uint32
|
||||||
|
Flags uint32
|
||||||
|
wReserved uint16
|
||||||
|
XonLim uint16
|
||||||
|
XoffLim uint16
|
||||||
|
ByteSize uint8
|
||||||
|
Parity uint8
|
||||||
|
StopBits uint8
|
||||||
|
XonChar byte
|
||||||
|
XoffChar byte
|
||||||
|
ErrorChar byte
|
||||||
|
EofChar byte
|
||||||
|
EvtChar byte
|
||||||
|
wReserved1 uint16
|
||||||
|
}
|
||||||
|
|||||||
+194
-13
@@ -55,6 +55,7 @@ var (
|
|||||||
moduser32 = NewLazySystemDLL("user32.dll")
|
moduser32 = NewLazySystemDLL("user32.dll")
|
||||||
moduserenv = NewLazySystemDLL("userenv.dll")
|
moduserenv = NewLazySystemDLL("userenv.dll")
|
||||||
modversion = NewLazySystemDLL("version.dll")
|
modversion = NewLazySystemDLL("version.dll")
|
||||||
|
modwinmm = NewLazySystemDLL("winmm.dll")
|
||||||
modwintrust = NewLazySystemDLL("wintrust.dll")
|
modwintrust = NewLazySystemDLL("wintrust.dll")
|
||||||
modws2_32 = NewLazySystemDLL("ws2_32.dll")
|
modws2_32 = NewLazySystemDLL("ws2_32.dll")
|
||||||
modwtsapi32 = NewLazySystemDLL("wtsapi32.dll")
|
modwtsapi32 = NewLazySystemDLL("wtsapi32.dll")
|
||||||
@@ -183,10 +184,14 @@ var (
|
|||||||
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
|
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
|
||||||
procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx")
|
procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx")
|
||||||
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
|
procGetIfEntry = modiphlpapi.NewProc("GetIfEntry")
|
||||||
|
procAddDllDirectory = modkernel32.NewProc("AddDllDirectory")
|
||||||
procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject")
|
procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject")
|
||||||
procCancelIo = modkernel32.NewProc("CancelIo")
|
procCancelIo = modkernel32.NewProc("CancelIo")
|
||||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procClearCommBreak = modkernel32.NewProc("ClearCommBreak")
|
||||||
|
procClearCommError = modkernel32.NewProc("ClearCommError")
|
||||||
procCloseHandle = modkernel32.NewProc("CloseHandle")
|
procCloseHandle = modkernel32.NewProc("CloseHandle")
|
||||||
|
procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole")
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
|
procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW")
|
||||||
procCreateEventExW = modkernel32.NewProc("CreateEventExW")
|
procCreateEventExW = modkernel32.NewProc("CreateEventExW")
|
||||||
@@ -201,6 +206,7 @@ var (
|
|||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
procCreatePipe = modkernel32.NewProc("CreatePipe")
|
procCreatePipe = modkernel32.NewProc("CreatePipe")
|
||||||
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
procCreateProcessW = modkernel32.NewProc("CreateProcessW")
|
||||||
|
procCreatePseudoConsole = modkernel32.NewProc("CreatePseudoConsole")
|
||||||
procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW")
|
procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW")
|
||||||
procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
|
procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot")
|
||||||
procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW")
|
procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW")
|
||||||
@@ -208,7 +214,9 @@ var (
|
|||||||
procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList")
|
procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList")
|
||||||
procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW")
|
procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW")
|
||||||
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
procDeviceIoControl = modkernel32.NewProc("DeviceIoControl")
|
||||||
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
|
||||||
procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
|
procDuplicateHandle = modkernel32.NewProc("DuplicateHandle")
|
||||||
|
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
|
||||||
procExitProcess = modkernel32.NewProc("ExitProcess")
|
procExitProcess = modkernel32.NewProc("ExitProcess")
|
||||||
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||||
procFindClose = modkernel32.NewProc("FindClose")
|
procFindClose = modkernel32.NewProc("FindClose")
|
||||||
@@ -232,6 +240,8 @@ var (
|
|||||||
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
|
procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent")
|
||||||
procGetACP = modkernel32.NewProc("GetACP")
|
procGetACP = modkernel32.NewProc("GetACP")
|
||||||
procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount")
|
procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount")
|
||||||
|
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus")
|
||||||
|
procGetCommState = modkernel32.NewProc("GetCommState")
|
||||||
procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts")
|
procGetCommTimeouts = modkernel32.NewProc("GetCommTimeouts")
|
||||||
procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
|
procGetCommandLineW = modkernel32.NewProc("GetCommandLineW")
|
||||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||||
@@ -250,6 +260,7 @@ var (
|
|||||||
procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
|
procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW")
|
||||||
procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
|
procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle")
|
||||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procGetFileTime = modkernel32.NewProc("GetFileTime")
|
||||||
procGetFileType = modkernel32.NewProc("GetFileType")
|
procGetFileType = modkernel32.NewProc("GetFileType")
|
||||||
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
|
||||||
procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
|
procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW")
|
||||||
@@ -317,6 +328,7 @@ var (
|
|||||||
procProcess32NextW = modkernel32.NewProc("Process32NextW")
|
procProcess32NextW = modkernel32.NewProc("Process32NextW")
|
||||||
procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
|
procProcessIdToSessionId = modkernel32.NewProc("ProcessIdToSessionId")
|
||||||
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
procPulseEvent = modkernel32.NewProc("PulseEvent")
|
||||||
|
procPurgeComm = modkernel32.NewProc("PurgeComm")
|
||||||
procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW")
|
procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW")
|
||||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||||
procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject")
|
procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject")
|
||||||
@@ -326,8 +338,13 @@ var (
|
|||||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||||
procReleaseMutex = modkernel32.NewProc("ReleaseMutex")
|
procReleaseMutex = modkernel32.NewProc("ReleaseMutex")
|
||||||
procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
|
procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW")
|
||||||
|
procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory")
|
||||||
procResetEvent = modkernel32.NewProc("ResetEvent")
|
procResetEvent = modkernel32.NewProc("ResetEvent")
|
||||||
|
procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole")
|
||||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||||
|
procSetCommBreak = modkernel32.NewProc("SetCommBreak")
|
||||||
|
procSetCommMask = modkernel32.NewProc("SetCommMask")
|
||||||
|
procSetCommState = modkernel32.NewProc("SetCommState")
|
||||||
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
|
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
|
||||||
procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition")
|
procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition")
|
||||||
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
|
procSetConsoleMode = modkernel32.NewProc("SetConsoleMode")
|
||||||
@@ -343,6 +360,7 @@ var (
|
|||||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||||
procSetFilePointer = modkernel32.NewProc("SetFilePointer")
|
procSetFilePointer = modkernel32.NewProc("SetFilePointer")
|
||||||
procSetFileTime = modkernel32.NewProc("SetFileTime")
|
procSetFileTime = modkernel32.NewProc("SetFileTime")
|
||||||
|
procSetFileValidData = modkernel32.NewProc("SetFileValidData")
|
||||||
procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
|
procSetHandleInformation = modkernel32.NewProc("SetHandleInformation")
|
||||||
procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject")
|
procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject")
|
||||||
procSetNamedPipeHandleState = modkernel32.NewProc("SetNamedPipeHandleState")
|
procSetNamedPipeHandleState = modkernel32.NewProc("SetNamedPipeHandleState")
|
||||||
@@ -353,6 +371,7 @@ var (
|
|||||||
procSetStdHandle = modkernel32.NewProc("SetStdHandle")
|
procSetStdHandle = modkernel32.NewProc("SetStdHandle")
|
||||||
procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW")
|
procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW")
|
||||||
procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW")
|
procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW")
|
||||||
|
procSetupComm = modkernel32.NewProc("SetupComm")
|
||||||
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
||||||
procSleepEx = modkernel32.NewProc("SleepEx")
|
procSleepEx = modkernel32.NewProc("SleepEx")
|
||||||
procTerminateJobObject = modkernel32.NewProc("TerminateJobObject")
|
procTerminateJobObject = modkernel32.NewProc("TerminateJobObject")
|
||||||
@@ -371,6 +390,7 @@ var (
|
|||||||
procVirtualQueryEx = modkernel32.NewProc("VirtualQueryEx")
|
procVirtualQueryEx = modkernel32.NewProc("VirtualQueryEx")
|
||||||
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
procVirtualUnlock = modkernel32.NewProc("VirtualUnlock")
|
||||||
procWTSGetActiveConsoleSessionId = modkernel32.NewProc("WTSGetActiveConsoleSessionId")
|
procWTSGetActiveConsoleSessionId = modkernel32.NewProc("WTSGetActiveConsoleSessionId")
|
||||||
|
procWaitCommEvent = modkernel32.NewProc("WaitCommEvent")
|
||||||
procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects")
|
procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects")
|
||||||
procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
|
procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject")
|
||||||
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
|
procWriteConsoleW = modkernel32.NewProc("WriteConsoleW")
|
||||||
@@ -468,6 +488,8 @@ var (
|
|||||||
procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW")
|
procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW")
|
||||||
procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW")
|
procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW")
|
||||||
procVerQueryValueW = modversion.NewProc("VerQueryValueW")
|
procVerQueryValueW = modversion.NewProc("VerQueryValueW")
|
||||||
|
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
|
||||||
|
proctimeEndPeriod = modwinmm.NewProc("timeEndPeriod")
|
||||||
procWinVerifyTrustEx = modwintrust.NewProc("WinVerifyTrustEx")
|
procWinVerifyTrustEx = modwintrust.NewProc("WinVerifyTrustEx")
|
||||||
procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
|
procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
|
||||||
procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
|
procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
|
||||||
@@ -1598,6 +1620,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddDllDirectory(path *uint16) (cookie uintptr, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
|
||||||
|
cookie = uintptr(r0)
|
||||||
|
if cookie == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func AssignProcessToJobObject(job Handle, process Handle) (err error) {
|
func AssignProcessToJobObject(job Handle, process Handle) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0)
|
r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -1622,6 +1653,22 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClearCommBreak(handle Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearCommError(handle Handle, lpErrors *uint32, lpStat *ComStat) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat)))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func CloseHandle(handle Handle) (err error) {
|
func CloseHandle(handle Handle) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
|
r1, _, e1 := syscall.Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -1630,6 +1677,11 @@ func CloseHandle(handle Handle) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClosePseudoConsole(console Handle) {
|
||||||
|
syscall.Syscall(procClosePseudoConsole.Addr(), 1, uintptr(console), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) {
|
func ConnectNamedPipe(pipe Handle, overlapped *Overlapped) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(overlapped)), 0)
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(overlapped)), 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -1759,6 +1811,14 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createPseudoConsole(size uint32, in Handle, out Handle, flags uint32, pconsole *Handle) (hr error) {
|
||||||
|
r0, _, _ := syscall.Syscall6(procCreatePseudoConsole.Addr(), 5, uintptr(size), uintptr(in), uintptr(out), uintptr(flags), uintptr(unsafe.Pointer(pconsole)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
hr = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) {
|
func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procCreateSymbolicLinkW.Addr(), 3, uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags))
|
r1, _, e1 := syscall.Syscall(procCreateSymbolicLinkW.Addr(), 3, uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags))
|
||||||
if r1&0xff == 0 {
|
if r1&0xff == 0 {
|
||||||
@@ -1813,6 +1873,14 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DisconnectNamedPipe(pipe Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(pipe), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) {
|
func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if bInheritHandle {
|
if bInheritHandle {
|
||||||
@@ -1825,6 +1893,14 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EscapeCommFunction(handle Handle, dwFunc uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(dwFunc), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ExitProcess(exitcode uint32) {
|
func ExitProcess(exitcode uint32) {
|
||||||
syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0)
|
syscall.Syscall(procExitProcess.Addr(), 1, uintptr(exitcode), 0, 0)
|
||||||
return
|
return
|
||||||
@@ -2026,6 +2102,22 @@ func GetActiveProcessorCount(groupNumber uint16) (ret uint32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCommModemStatus(handle Handle, lpModemStat *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpModemStat)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
func GetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
r1, _, e1 := syscall.Syscall(procGetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -2166,6 +2258,14 @@ func GetFileInformationByHandleEx(handle Handle, class uint32, outBuffer *byte,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func GetFileType(filehandle Handle) (n uint32, err error) {
|
func GetFileType(filehandle Handle) (n uint32, err error) {
|
||||||
r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0)
|
r0, _, e1 := syscall.Syscall(procGetFileType.Addr(), 1, uintptr(filehandle), 0, 0)
|
||||||
n = uint32(r0)
|
n = uint32(r0)
|
||||||
@@ -2367,11 +2467,8 @@ func GetShortPathName(longpath *uint16, shortpath *uint16, buflen uint32) (n uin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStartupInfo(startupInfo *StartupInfo) (err error) {
|
func getStartupInfo(startupInfo *StartupInfo) {
|
||||||
r1, _, e1 := syscall.Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
|
syscall.Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
|
||||||
if r1 == 0 {
|
|
||||||
err = errnoErr(e1)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2773,6 +2870,14 @@ func PulseEvent(event Handle) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PurgeComm(handle Handle, dwFlags uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(dwFlags), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) {
|
func QueryDosDevice(deviceName *uint16, targetPath *uint16, max uint32) (n uint32, err error) {
|
||||||
r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max))
|
r0, _, e1 := syscall.Syscall(procQueryDosDeviceW.Addr(), 3, uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)), uintptr(max))
|
||||||
n = uint32(r0)
|
n = uint32(r0)
|
||||||
@@ -2854,6 +2959,14 @@ func RemoveDirectory(path *uint16) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveDllDirectory(cookie uintptr) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ResetEvent(event Handle) (err error) {
|
func ResetEvent(event Handle) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0)
|
r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -2862,6 +2975,14 @@ func ResetEvent(event Handle) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resizePseudoConsole(pconsole Handle, size uint32) (hr error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procResizePseudoConsole.Addr(), 2, uintptr(pconsole), uintptr(size), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
hr = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ResumeThread(thread Handle) (ret uint32, err error) {
|
func ResumeThread(thread Handle) (ret uint32, err error) {
|
||||||
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(thread), 0, 0)
|
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(thread), 0, 0)
|
||||||
ret = uint32(r0)
|
ret = uint32(r0)
|
||||||
@@ -2871,6 +2992,30 @@ func ResumeThread(thread Handle) (ret uint32, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetCommBreak(handle Handle) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCommMask(handle Handle, dwEvtMask uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(dwEvtMask), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCommState(handle Handle, lpDCB *DCB) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(lpDCB)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -2999,6 +3144,14 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetFileValidData(handle Handle, validDataLength int64) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetFileValidData.Addr(), 2, uintptr(handle), uintptr(validDataLength), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
|
func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) {
|
||||||
r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
|
r1, _, e1 := syscall.Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
@@ -3084,6 +3237,14 @@ func SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetupComm(handle Handle, dwInQueue uint32, dwOutQueue uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetupComm.Addr(), 3, uintptr(handle), uintptr(dwInQueue), uintptr(dwOutQueue))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) {
|
func SizeofResource(module Handle, resInfo Handle) (size uint32, err error) {
|
||||||
r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
||||||
size = uint32(r0)
|
size = uint32(r0)
|
||||||
@@ -3230,6 +3391,14 @@ func WTSGetActiveConsoleSessionId() (sessionID uint32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WaitCommEvent(handle Handle, lpEvtMask *uint32, lpOverlapped *Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitCommEvent.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpEvtMask)), uintptr(unsafe.Pointer(lpOverlapped)))
|
||||||
|
if r1 == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMilliseconds uint32) (event uint32, err error) {
|
func waitForMultipleObjects(count uint32, handles uintptr, waitAll bool, waitMilliseconds uint32) (event uint32, err error) {
|
||||||
var _p0 uint32
|
var _p0 uint32
|
||||||
if waitAll {
|
if waitAll {
|
||||||
@@ -3516,12 +3685,8 @@ func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *u
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) {
|
func enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) {
|
||||||
var _p0 *uint32
|
r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(nSize), uintptr(unsafe.Pointer(bytesReturned)))
|
||||||
if len(processIds) > 0 {
|
|
||||||
_p0 = &processIds[0]
|
|
||||||
}
|
|
||||||
r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(_p0)), uintptr(len(processIds)), uintptr(unsafe.Pointer(bytesReturned)))
|
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -3824,9 +3989,9 @@ func setupUninstallOEMInf(infFileName *uint16, flags SUOI, reserved uintptr) (er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) {
|
func commandLineToArgv(cmd *uint16, argc *int32) (argv **uint16, err error) {
|
||||||
r0, _, e1 := syscall.Syscall(procCommandLineToArgvW.Addr(), 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0)
|
r0, _, e1 := syscall.Syscall(procCommandLineToArgvW.Addr(), 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0)
|
||||||
argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0))
|
argv = (**uint16)(unsafe.Pointer(r0))
|
||||||
if argv == nil {
|
if argv == nil {
|
||||||
err = errnoErr(e1)
|
err = errnoErr(e1)
|
||||||
}
|
}
|
||||||
@@ -4021,6 +4186,22 @@ func _VerQueryValue(block unsafe.Pointer, subBlock *uint16, pointerToBufferPoint
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TimeBeginPeriod(period uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||||
|
if r1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TimeEndPeriod(period uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(proctimeEndPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||||
|
if r1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error) {
|
func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error) {
|
||||||
r0, _, _ := syscall.Syscall(procWinVerifyTrustEx.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data)))
|
r0, _, _ := syscall.Syscall(procWinVerifyTrustEx.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data)))
|
||||||
if r0 != 0 {
|
if r0 != 0 {
|
||||||
|
|||||||
Vendored
+6
-4
@@ -1,7 +1,9 @@
|
|||||||
# github.com/danieljoos/wincred v1.2.0
|
# github.com/danieljoos/wincred v1.2.3
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/danieljoos/wincred
|
github.com/danieljoos/wincred
|
||||||
# golang.org/x/sys v0.8.0
|
# github.com/keybase/go-keychain v0.0.1
|
||||||
## explicit; go 1.17
|
## explicit; go 1.21
|
||||||
golang.org/x/sys/internal/unsafeheader
|
github.com/keybase/go-keychain
|
||||||
|
# golang.org/x/sys v0.20.0
|
||||||
|
## explicit; go 1.18
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
|
|||||||
Reference in New Issue
Block a user