3
0
mirror of https://github.com/ergochat/ergo.git synced 2026-03-14 03:08:11 +01:00
Check certfp in API /v1/check_auth
This commit is contained in:
Shivaram Lingamneni 2026-03-11 22:41:49 -07:00 committed by GitHub
parent c61859927f
commit 4e65b76c67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 29 deletions

View File

@ -46,6 +46,9 @@ This endpoint verifies the credentials of a NickServ account; this allows Ergo t
* `accountName`: string, name of the account
* `passphrase`: string, alleged passphrase of the account
* `certfp`: string, alleged certificate fingerprint (hex-encoded SHA-256 checksum of the decoded raw certificate) associated with the account
Each individual field is optional, since a user may be authenticated either by account-passphrase pair or by certificate.
The response is a JSON object with fields:

View File

@ -9,6 +9,7 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"net"
"sort"
"strconv"
"strings"
@ -2039,12 +2040,6 @@ func (am *AccountManager) AuthenticateByCertificate(client *Client, certfp strin
defer func() {
if err != nil {
return
} else if !clientAccount.Verified {
err = errAccountUnverified
return
} else if clientAccount.Suspended != nil {
err = errAccountSuspended
return
}
// TODO(#1109) clean this check up?
if client.registered {
@ -2057,11 +2052,34 @@ func (am *AccountManager) AuthenticateByCertificate(client *Client, certfp strin
return
}()
clientAccount, err = am.checkCertAuth(client.IP(), certfp, peerCerts, authzid)
return err
}
func (am *AccountManager) checkCertAuth(ip net.IP, certfp string, peerCerts []*x509.Certificate, authzid string) (clientAccount ClientAccount, err error) {
defer func() {
if err != nil {
return
} else if !clientAccount.Verified {
err = errAccountUnverified
return
} else if clientAccount.Suspended != nil {
err = errAccountSuspended
return
}
}()
config := am.server.Config()
if config.Accounts.AuthScript.Enabled {
var output AuthScriptOutput
output, err = CheckAuthScript(am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
AuthScriptInput{Certfp: certfp, IP: client.IP().String(), peerCerts: peerCerts})
var ipString string
if ip != nil {
ipString = ip.String()
}
output, err = CheckAuthScript(
am.server.semaphores.AuthScript, config.Accounts.AuthScript.ScriptConfig,
AuthScriptInput{Certfp: certfp, IP: ipString, peerCerts: peerCerts},
)
if err != nil {
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
} else if output.Success && output.AccountName != "" {
@ -2082,18 +2100,19 @@ func (am *AccountManager) AuthenticateByCertificate(client *Client, certfp strin
})
if err != nil {
return err
return
}
if authzid != "" {
if cfAuthzid, err := CasefoldName(authzid); err != nil || cfAuthzid != account {
return errAuthzidAuthcidMismatch
if cfAuthzid, cErr := CasefoldName(authzid); cErr != nil || cfAuthzid != account {
err = errAuthzidAuthcidMismatch
return
}
}
// ok, we found an account corresponding to their certificate
clientAccount, err = am.LoadAccount(account)
return err
return
}
type settingsMunger func(input AccountSettings) (output AccountSettings, err error)

View File

@ -130,25 +130,29 @@ func (a *ergoAPI) handleCheckAuth(w http.ResponseWriter, r *http.Request) {
var response apiCheckAuthResponse
// try passphrase if present
var account ClientAccount
var err error
// try whatever credentials are present
if request.AccountName != "" && request.Passphrase != "" {
account, err := a.server.accounts.checkPassphrase(request.AccountName, request.Passphrase)
switch err {
case nil:
// success, no error
response.Success = true
response.AccountName = account.Name
case errAccountDoesNotExist, errAccountInvalidCredentials, errAccountUnverified, errAccountSuspended:
// fail, no error
response.Success = false
default:
response.Success = false
response.Error = err.Error()
}
account, err = a.server.accounts.checkPassphrase(request.AccountName, request.Passphrase)
} else if request.Certfp != "" {
account, err = a.server.accounts.checkCertAuth(nil, request.Certfp, nil, "")
} else {
err = errAccountInvalidCredentials
}
// try certfp if present
if !response.Success && request.Certfp != "" {
// TODO support cerftp
switch err {
case nil:
// success, no error
response.Success = true
response.AccountName = account.Name
case errAccountDoesNotExist, errAccountInvalidCredentials, errAccountUnverified, errAccountSuspended:
// fail, no error
response.Success = false
default:
response.Success = false
response.Error = err.Error()
}
a.writeJSONResponse(response, w, r)