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 {
129        request: PamAuthRequest,
130        session_id: u64,
131    },
132    PamAccountAllowed(String),
133    PamAccountBeginSession(String),
134    InvalidateCache,
135    ClearCache,
136    Status,
137}
138
139impl ClientRequest {
140    /// Get a safe display version of the request, without credentials.
141    pub fn as_safe_string(&self) -> String {
142        match self {
143            ClientRequest::SshKey(id) => format!("SshKey({id})"),
144            ClientRequest::NssAccounts => "NssAccounts".to_string(),
145            ClientRequest::NssAccountByUid(id) => format!("NssAccountByUid({id})"),
146            ClientRequest::NssAccountByName(id) => format!("NssAccountByName({id})"),
147            ClientRequest::NssGroups => "NssGroups".to_string(),
148            ClientRequest::NssGroupByGid(id) => format!("NssGroupByGid({id})"),
149            ClientRequest::NssGroupByName(id) => format!("NssGroupByName({id})"),
150            ClientRequest::PamAuthenticateInit { account_id, info } => format!(
151                "PamAuthenticateInit{{ account_id={} tty={} pam_secvice{} rhost={} }}",
152                account_id,
153                info.service,
154                info.tty.as_deref().unwrap_or(""),
155                info.rhost.as_deref().unwrap_or("")
156            ),
157            ClientRequest::PamAuthenticateStep { .. } => "PamAuthenticateStep".to_string(),
158            ClientRequest::PamAccountAllowed(id) => {
159                format!("PamAccountAllowed({id})")
160            }
161            ClientRequest::PamAccountBeginSession(_) => "PamAccountBeginSession".to_string(),
162            ClientRequest::InvalidateCache => "InvalidateCache".to_string(),
163            ClientRequest::ClearCache => "ClearCache".to_string(),
164            ClientRequest::Status => "Status".to_string(),
165        }
166    }
167}
168
169#[derive(Serialize, Deserialize, Debug)]
170pub struct ProviderStatus {
171    pub name: String,
172    pub online: bool,
173}
174
175#[derive(Serialize, Deserialize, Debug)]
176pub enum ClientResponse {
177    SshKeys(Vec<String>),
178    NssAccounts(Vec<NssUser>),
179    NssAccount(Option<NssUser>),
180    NssGroups(Vec<NssGroup>),
181    NssGroup(Option<NssGroup>),
182
183    PamStatus(Option<bool>),
184    PamAuthenticateStepResponse {
185        response: PamAuthResponse,
186        session_id: u64,
187    },
188
189    ProviderStatus(Vec<ProviderStatus>),
190
191    Ok,
192    Error(OperationError),
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 alias: Option<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}