kanidm_unix_common/
unix_proto.rs

1use crate::unix_passwd::{EtcDb, EtcGroup, EtcUser};
2use kanidm_proto::internal::OperationError;
3use serde::{Deserialize, Serialize};
4
5#[derive(Serialize, Deserialize, Debug)]
6pub struct NssUser {
7    pub name: String,
8    pub uid: u32,
9    pub gid: u32,
10    pub gecos: String,
11    pub homedir: String,
12    pub shell: String,
13}
14
15impl<T> From<&T> for NssUser
16where
17    T: AsRef<EtcUser>,
18{
19    fn from(etc_user: &T) -> Self {
20        let etc_user = etc_user.as_ref();
21        NssUser {
22            name: etc_user.name.clone(),
23            uid: etc_user.uid,
24            gid: etc_user.gid,
25            gecos: etc_user.gecos.clone(),
26            homedir: etc_user.homedir.clone(),
27            shell: etc_user.shell.clone(),
28        }
29    }
30}
31
32#[derive(Serialize, Deserialize, Debug)]
33pub struct NssGroup {
34    pub name: String,
35    pub gid: u32,
36    pub members: Vec<String>,
37}
38
39impl<T> From<&T> for NssGroup
40where
41    T: AsRef<EtcGroup>,
42{
43    fn from(etc_group: &T) -> Self {
44        let etc_group = etc_group.as_ref();
45        NssGroup {
46            name: etc_group.name.clone(),
47            gid: etc_group.gid,
48            members: etc_group.members.clone(),
49        }
50    }
51}
52
53/* RFC8628: 3.2. Device Authorization Response */
54#[derive(Serialize, Deserialize, Clone, Debug)]
55pub struct DeviceAuthorizationResponse {
56    pub device_code: String,
57    pub user_code: String,
58    pub verification_uri: String,
59    pub verification_uri_complete: Option<String>,
60    pub expires_in: u32,
61    pub interval: Option<u32>,
62    /* The message is not part of RFC8628, but an add-on from MS. Listed
63     * optional here to support all implementations. */
64    pub message: Option<String>,
65}
66
67#[derive(Serialize, Deserialize, Debug)]
68pub enum PamAuthResponse {
69    Unknown,
70    Success,
71    Denied,
72    Password,
73    DeviceAuthorizationGrant {
74        data: DeviceAuthorizationResponse,
75    },
76    /// PAM must prompt for an authentication code
77    MFACode {
78        msg: String,
79    },
80    /// PAM will poll for an external response
81    MFAPoll {
82        /// Initial message to display as the polling begins.
83        msg: String,
84        /// Seconds between polling attempts.
85        polling_interval: u32,
86    },
87    MFAPollWait,
88    /// PAM must prompt for a new PIN and confirm that PIN input
89    SetupPin {
90        msg: String,
91    },
92    Pin,
93    // CTAP2
94}
95
96#[derive(Serialize, Deserialize, Debug)]
97pub enum PamAuthRequest {
98    Password { cred: String },
99    DeviceAuthorizationGrant { data: DeviceAuthorizationResponse },
100    MFACode { cred: String },
101    MFAPoll,
102    SetupPin { pin: String },
103    Pin { cred: String },
104}
105
106#[derive(Serialize, Deserialize, Debug)]
107pub struct PamServiceInfo {
108    pub service: String,
109    // Somehow SDDM doesn't set this ...
110    pub tty: Option<String>,
111    // Only set if it really is a remote host?
112    pub rhost: Option<String>,
113}
114
115#[derive(Serialize, Deserialize, Debug)]
116pub enum ClientRequest {
117    SshKey(String),
118    NssAccounts,
119    NssAccountByUid(u32),
120    NssAccountByName(String),
121    NssGroups,
122    NssGroupByGid(u32),
123    NssGroupByName(String),
124    PamAuthenticateInit {
125        account_id: String,
126        info: PamServiceInfo,
127    },
128    PamAuthenticateStep(PamAuthRequest),
129    PamAccountAllowed(String),
130    PamAccountBeginSession(String),
131    InvalidateCache,
132    ClearCache,
133    Status,
134}
135
136impl ClientRequest {
137    /// Get a safe display version of the request, without credentials.
138    pub fn as_safe_string(&self) -> String {
139        match self {
140            ClientRequest::SshKey(id) => format!("SshKey({})", id),
141            ClientRequest::NssAccounts => "NssAccounts".to_string(),
142            ClientRequest::NssAccountByUid(id) => format!("NssAccountByUid({})", id),
143            ClientRequest::NssAccountByName(id) => format!("NssAccountByName({})", id),
144            ClientRequest::NssGroups => "NssGroups".to_string(),
145            ClientRequest::NssGroupByGid(id) => format!("NssGroupByGid({})", id),
146            ClientRequest::NssGroupByName(id) => format!("NssGroupByName({})", id),
147            ClientRequest::PamAuthenticateInit { account_id, info } => format!(
148                "PamAuthenticateInit{{ account_id={} tty={} pam_secvice{} rhost={} }}",
149                account_id,
150                info.service,
151                info.tty.as_deref().unwrap_or(""),
152                info.rhost.as_deref().unwrap_or("")
153            ),
154            ClientRequest::PamAuthenticateStep(_) => "PamAuthenticateStep".to_string(),
155            ClientRequest::PamAccountAllowed(id) => {
156                format!("PamAccountAllowed({})", id)
157            }
158            ClientRequest::PamAccountBeginSession(_) => "PamAccountBeginSession".to_string(),
159            ClientRequest::InvalidateCache => "InvalidateCache".to_string(),
160            ClientRequest::ClearCache => "ClearCache".to_string(),
161            ClientRequest::Status => "Status".to_string(),
162        }
163    }
164}
165
166#[derive(Serialize, Deserialize, Debug)]
167pub struct ProviderStatus {
168    pub name: String,
169    pub online: bool,
170}
171
172#[derive(Serialize, Deserialize, Debug)]
173pub enum ClientResponse {
174    SshKeys(Vec<String>),
175    NssAccounts(Vec<NssUser>),
176    NssAccount(Option<NssUser>),
177    NssGroups(Vec<NssGroup>),
178    NssGroup(Option<NssGroup>),
179
180    PamStatus(Option<bool>),
181    PamAuthenticateStepResponse(PamAuthResponse),
182
183    ProviderStatus(Vec<ProviderStatus>),
184
185    Ok,
186    Error(OperationError),
187}
188
189impl From<PamAuthResponse> for ClientResponse {
190    fn from(par: PamAuthResponse) -> Self {
191        ClientResponse::PamAuthenticateStepResponse(par)
192    }
193}
194
195#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
196pub struct HomeDirectoryInfo {
197    pub uid: u32,
198    pub gid: u32,
199    pub name: String,
200    pub aliases: Vec<String>,
201}
202
203#[derive(Serialize, Deserialize, Debug, Clone)]
204pub struct TaskRequestFrame {
205    pub id: u64,
206    pub req: TaskRequest,
207}
208
209#[derive(Serialize, Deserialize, Debug, Clone)]
210pub enum TaskRequest {
211    HomeDirectory(HomeDirectoryInfo),
212}
213
214#[derive(Serialize, Deserialize, Debug)]
215pub enum TaskResponse {
216    Success(u64),
217    Error(String),
218    NotifyShadowChange(EtcDb),
219}
220
221#[test]
222fn test_clientrequest_as_safe_string() {
223    assert_eq!(
224        ClientRequest::NssAccounts.as_safe_string(),
225        "NssAccounts".to_string()
226    );
227    assert_eq!(
228        ClientRequest::SshKey("cheese".to_string()).as_safe_string(),
229        format!("SshKey({})", "cheese")
230    );
231}