From 3c2baa6cb83bd7e26b955b87da62b8407bf648b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Fri, 21 Jul 2023 08:27:23 +0200 Subject: [PATCH] pam: In GDM mode, send the broker selection using a choice list --- pam/pam.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 7 deletions(-) diff --git a/pam/pam.go b/pam/pam.go index d5c45868e..3443e8ea2 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -18,6 +18,7 @@ import ( "syscall" "time" + "github.com/elliotchance/orderedmap/v2" "github.com/skip2/go-qrcode" "github.com/ubuntu/authd" "github.com/ubuntu/authd/internal/brokers" @@ -47,6 +48,7 @@ const ( ClientTypeNone ClientType = iota + 1 ClientTypeTerminal ClientTypeStandard + ClientTypeGdm ) type PAMClient interface { @@ -84,6 +86,10 @@ type PAMClientTerminal struct { PAMClientBase } +type PAMClientGdm struct { + PAMClientBase +} + func NewPAMClientBase(clientType ClientType, pamh pamHandle, client authd.PAMClient) *PAMClientBase { return &PAMClientBase{clientType, pamh, client, @@ -94,6 +100,10 @@ func NewPAMClientTerminal(pamh pamHandle, client authd.PAMClient) *PAMClientTerm return &PAMClientTerminal{*NewPAMClientBase(ClientTypeTerminal, pamh, client)} } +func NewPAMClientGdm(pamh pamHandle, client authd.PAMClient) *PAMClientGdm { + return &PAMClientGdm{*NewPAMClientBase(ClientTypeGdm, pamh, client)} +} + func (base *PAMClientBase) getClientType() ClientType { return base.clientType } @@ -166,6 +176,8 @@ func pam_sm_authenticate(pamh *C.pam_handle_t, flags, argc C.int, argv **C.char) var pamClient PAMClient if term.IsTerminal(int(os.Stdin.Fd())) { pamClient = NewPAMClientTerminal(pamh, client) + } else if gdmChoiceListSupported() { + pamClient = NewPAMClientGdm(pamh, client) } else { pamClient = NewPAMClientBase(ClientTypeStandard, pamh, client) } @@ -179,6 +191,8 @@ func pam_sm_authenticate(pamh *C.pam_handle_t, flags, argc C.int, argv **C.char) return C.PAM_AUTH_ERR } + // IF BROKER DISCONNECTED... fail! + brokersInfo, err := client.AvailableBrokers(context.TODO(), &authd.ABRequest{ UserName: &user, }) @@ -339,15 +353,27 @@ func pam_sm_authenticate(pamh *C.pam_handle_t, flags, argc C.int, argv **C.char) // selectBroker allows interactive broker selection. // Only one choice will be returned immediately. -func (pamClient *PAMClientBase) selectBrokerInteractive( - brokersInfo []*authd.ABResponse_BrokerInfo) (brokerID, brokerName string, err error) { +func maybeGetBroker(brokersInfo []*authd.ABResponse_BrokerInfo) ( + *authd.ABResponse_BrokerInfo, error) { if len(brokersInfo) < 1 { - return "", "", errors.New("no broker found") + return nil, errors.New("no broker found") } // Default choice for one possibility. if len(brokersInfo) == 1 { - return brokersInfo[0].GetId(), brokersInfo[0].GetName(), nil + return brokersInfo[0], nil + } + + return nil, nil +} + +func (pamClient *PAMClientBase) selectBrokerInteractive( + brokersInfo []*authd.ABResponse_BrokerInfo) (brokerID, brokerName string, err error) { + broker, err := maybeGetBroker(brokersInfo) + if err != nil { + return "", "", err + } else if broker != nil { + return broker.GetId(), broker.GetName(), nil } var choices []string @@ -369,6 +395,31 @@ func (pamClient *PAMClientBase) selectBrokerInteractive( return ids[i], brokersInfo[i].GetName(), nil } +func (pamClient *PAMClientGdm) selectBrokerInteractive( + brokersInfo []*authd.ABResponse_BrokerInfo) (brokerID, brokerName string, err error) { + broker, err := maybeGetBroker(brokersInfo) + if err != nil { + return "", "", err + } else if broker != nil { + return broker.GetId(), broker.GetName(), nil + } + + var choices = orderedmap.NewOrderedMap[string, string]() + sendInfof(pamClient.pamh, "Sending choices to gdm %v", choices) + for _, b := range brokersInfo { + choices.Set(b.GetId(), b.GetName()) + } + choices.Set("go-back", "Go back") + + id, err := gdmChoiceListRequest(pamClient.pamh, "Select broker", choices) + if err != nil { + return "", "", err + } else if id == "go-back" { + return "", "", errGoBack + } + return id, choices.GetOrDefault(id, ""), nil +} + func (pamClient *PAMClientBase) getSupportedLayouts() []*authd.UILayout { required, optional := "required", "optional" supportedEntries := "optional:chars,chars_password" @@ -391,6 +442,21 @@ func (pamClient *PAMClientBase) getSupportedLayouts() []*authd.UILayout { } } +func (pamClient *PAMClientGdm) getSupportedLayouts() []*authd.UILayout { + required := "required" + supportedEntries := "optional:chars,chars_password" + waitOptional := "optional:true,false" + + return []*authd.UILayout{ + { + Type: "form", + Label: &required, + Entry: &supportedEntries, + Wait: &waitOptional, + }, + } +} + // startBrokerSession returns the sessionID and available authentication modes after marking a broker as current. func (pamClient *PAMClientBase) startBrokerSession(brokerID, username string) error { // Start a transaction for this user with the broker. @@ -440,7 +506,7 @@ func (pamClient *PAMClientBase) startBrokerSession(brokerID, username string) er // selectAuthenticationModeInteractive allows interactive authentication mode selection. // Only one choice will be returned immediately. -func (pamClient *PAMClientBase) selectAuthenticationModeInteractive() (name string, err error) { +func (pamClient *PAMClientBase) maybeGetAuthMode() (string, error) { if len(pamClient.authModes) < 1 { return "", errors.New("no authentication mode supported") } @@ -449,6 +515,16 @@ func (pamClient *PAMClientBase) selectAuthenticationModeInteractive() (name stri if len(pamClient.authModes) == 1 { return pamClient.authModes[0].GetName(), nil } + return "", nil +} + +func (pamClient *PAMClientBase) selectAuthenticationModeInteractive() (name string, err error) { + authMode, err := pamClient.maybeGetAuthMode() + if err != nil { + return "", err + } else if authMode != "" { + return authMode, nil + } var choices []string var ids []string @@ -466,6 +542,30 @@ func (pamClient *PAMClientBase) selectAuthenticationModeInteractive() (name stri return ids[i], nil } +func (pamClient *PAMClientGdm) selectAuthenticationModeInteractive() (name string, err error) { + authMode, err := pamClient.maybeGetAuthMode() + if err != nil { + return "", err + } else if authMode != "" { + return authMode, nil + } + + var choices = orderedmap.NewOrderedMap[string, string]() + sendInfof(pamClient.pamh, "Sending choices to gdm %v", choices) + for _, m := range pamClient.authModes { + choices.Set(m.GetName(), m.GetLabel()) + } + choices.Set("go-back", "Go back") + + id, err := gdmChoiceListRequest(pamClient.pamh, "Select authentication mode", choices) + if err != nil { + return "", err + } else if id == "go-back" { + return "", errGoBack + } + return id, nil +} + func (pamClient *PAMClientBase) promptForInt(title string, choices []string, prompt string) ( r int, err error) { pamPrompt := title @@ -480,7 +580,7 @@ func (pamClient *PAMClientBase) promptForInt(title string, choices []string, pro if err != nil { return 0, fmt.Errorf("error while reading stdin: %v", err) } - if r == "r" { + if r == "r" && pamClient.clientType != ClientTypeGdm { return 0, errGoBack } if r == "" { @@ -531,7 +631,7 @@ func (pamClient *PAMClientBase) formChallenge(uiLayout *authd.UILayout) ( return nil, err } - if input == "r" { + if input == "r" /* TODO: && pamClient.clientType != ClientTypeGdm */ { return nil, errGoBack }