mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-20 05:39:07 +02:00
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package pkcs11
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// DefaultModule is p11-kit's proxy, which exposes every module the system has registered,
|
|
// tpm2-pkcs11 included, so a URI without module-path works on a stock p11-kit setup.
|
|
const DefaultModule = "p11-kit-proxy.so"
|
|
|
|
// URI is the subset of an RFC 7512 PKCS#11 URI this client understands: the token label,
|
|
// the module to load and where the user PIN comes from. Unknown attributes are ignored.
|
|
type URI struct {
|
|
Token string
|
|
ModulePath string
|
|
pinValue *string
|
|
pinSource string
|
|
}
|
|
|
|
func ParseURI(raw string) (*URI, error) {
|
|
rest, ok := strings.CutPrefix(raw, "pkcs11:")
|
|
if !ok {
|
|
return nil, errors.New("PKCS#11 URI must start with pkcs11:")
|
|
}
|
|
path, query, _ := strings.Cut(rest, "?")
|
|
|
|
u := &URI{}
|
|
if err := eachAttribute(path, ";", func(name, value string) {
|
|
if name == "token" {
|
|
u.Token = value
|
|
}
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
err := eachAttribute(query, "&", func(name, value string) {
|
|
switch name {
|
|
case "module-path":
|
|
u.ModulePath = value
|
|
case "module-name":
|
|
u.ModulePath = "lib" + value + ".so"
|
|
case "pin-value":
|
|
u.pinValue = &value
|
|
case "pin-source":
|
|
u.pinSource = value
|
|
}
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func eachAttribute(list, sep string, fn func(name, value string)) error {
|
|
if list == "" {
|
|
return nil
|
|
}
|
|
for _, pair := range strings.Split(list, sep) {
|
|
name, value, ok := strings.Cut(pair, "=")
|
|
if !ok {
|
|
return fmt.Errorf("PKCS#11 URI attribute %q has no value", pair)
|
|
}
|
|
value, err := url.PathUnescape(value)
|
|
if err != nil {
|
|
return fmt.Errorf("PKCS#11 URI attribute %s: %w", name, err)
|
|
}
|
|
fn(name, value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Module is the library to load, DefaultModule when the URI names none.
|
|
func (u *URI) Module() string {
|
|
if u.ModulePath == "" {
|
|
return DefaultModule
|
|
}
|
|
return u.ModulePath
|
|
}
|
|
|
|
// PIN returns the user PIN, or nil when the URI carries none and no login should happen.
|
|
// A pin-source names a file whose single line is the PIN.
|
|
func (u *URI) PIN() ([]byte, error) {
|
|
if u.pinValue != nil {
|
|
return []byte(*u.pinValue), nil
|
|
}
|
|
if u.pinSource == "" {
|
|
return nil, nil
|
|
}
|
|
path := strings.TrimPrefix(strings.TrimPrefix(u.pinSource, "file://"), "file:")
|
|
pin, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read PIN: %w", err)
|
|
}
|
|
return []byte(strings.TrimRight(string(pin), "\r\n")), nil
|
|
}
|