gitlabhq/gitlab-shell

View on GitHub
internal/sshd/gssapi.go

Summary

Maintainability
A
35 mins
Test Coverage
//go:build gssapi

package sshd

import (
    "fmt"
    "sync"

    "github.com/openshift/gssapi"

    "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"

    "gitlab.com/gitlab-org/labkit/log"
)

func NewGSSAPIServer(c *config.GSSAPIConfig) (*OSGSSAPIServer, error) {
    lib, err := loadGSSAPILib(c)
    if err != nil {
        return nil, err
    }

    s := &OSGSSAPIServer{
        ServicePrincipalName: c.ServicePrincipalName,
        lib:                  lib,
    }

    return s, nil
}

func loadGSSAPILib(config *config.GSSAPIConfig) (*gssapi.Lib, error) {
    var err error
    var lib *gssapi.Lib

    if config.Enabled {
        options := &gssapi.Options{
            Krb5Ktname: config.Keytab,
        }

        if config.LibPath != "" {
            options.LibPath = config.LibPath
        }

        lib, err = gssapi.Load(options)

        if err != nil {
            log.WithError(err).Error("Unable to load GSSAPI library, gssapi-with-mic is disabled")
            config.Enabled = false
        }
    }

    return lib, err
}

type OSGSSAPIServer struct {
    Keytab               string
    ServicePrincipalName string

    mutex     sync.RWMutex
    lib       *gssapi.Lib
    contextId *gssapi.CtxId
}

func (server *OSGSSAPIServer) str2name(str string) (*gssapi.Name, error) {
    strBuffer, err := server.lib.MakeBufferString(str)
    if err != nil {
        return nil, err
    }
    defer strBuffer.Release()

    return strBuffer.Name(server.lib.GSS_C_NO_OID)
}

func (server *OSGSSAPIServer) AcceptSecContext(
    token []byte,
) (
    outputToken []byte,
    srcName string,
    needContinue bool,
    err error,
) {
    server.mutex.Lock()
    defer server.mutex.Unlock()

    tokenBuffer, err := server.lib.MakeBufferBytes(token)
    if err != nil {
        return
    }
    defer tokenBuffer.Release()

    var spn *gssapi.CredId = server.lib.GSS_C_NO_CREDENTIAL
    if server.ServicePrincipalName != "" {
        var name *gssapi.Name
        name, err = server.str2name(server.ServicePrincipalName)
        if err != nil {
            return
        }
        defer name.Release()

        var actualMech *gssapi.OIDSet
        spn, actualMech, _, err = server.lib.AcquireCred(name, 0, server.lib.GSS_C_NO_OID_SET, gssapi.GSS_C_ACCEPT)
        if err != nil {
            return
        }
        defer spn.Release()
        defer actualMech.Release()
    }

    ctxOut, srcNameName, _, outputTokenBuffer, _, _, _, err := server.lib.AcceptSecContext(
        server.contextId,
        spn,
        tokenBuffer,
        nil,
    )
    if err == gssapi.ErrContinueNeeded {
        needContinue = true
        err = nil
    } else if err != nil {
        return
    }
    defer outputTokenBuffer.Release()
    defer srcNameName.Release()

    outputToken = outputTokenBuffer.Bytes()
    server.contextId = ctxOut

    return outputToken, srcNameName.String(), needContinue, err
}

func (server *OSGSSAPIServer) VerifyMIC(
    micField []byte,
    micToken []byte,
) error {
    server.mutex.Lock()
    defer server.mutex.Unlock()

    if server.contextId == nil {
        return fmt.Errorf("gssapi: uninitialized contextId")
    }

    micFieldBuffer, err := server.lib.MakeBufferBytes(micField)
    if err != nil {
        return err
    }
    defer micFieldBuffer.Release()
    micTokenBuffer, err := server.lib.MakeBufferBytes(micToken)
    if err != nil {
        return err
    }
    defer micTokenBuffer.Release()

    _, err = server.contextId.VerifyMIC(micFieldBuffer, micTokenBuffer)
    return err

}

func (server *OSGSSAPIServer) DeleteSecContext() error {
    server.mutex.Lock()
    defer server.mutex.Unlock()

    if server.contextId == nil {
        return nil
    }

    err := server.contextId.DeleteSecContext()
    if err == nil {
        server.contextId = server.lib.GSS_C_NO_CONTEXT
    }
    return err
}