Skip to content
Snippets Groups Projects
nss.rs 5.53 KiB
Newer Older
/* Copyright 2021 Dominik George <dominik.george@teckids.org>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::config::{
    get_config,
use config::Config;
use crate::cache::get_cache;

use crate::logging::setup_log;

use crate::oauth::{get_access_token_client, get_data_jq};
use serde::{Serialize, Deserialize};
use libc::{getpwuid, geteuid};
use std::ffi::CStr;

use libnss::interop::Response;
use libnss::passwd::{PasswdHooks, Passwd};

#[derive(Serialize, Deserialize)]
#[serde(remote = "Passwd")]
struct PasswdDef {
    name: String,
    passwd: String,
    uid: libc::uid_t,
    gid: libc::gid_t,
    gecos: String,
    dir: String,
    shell: String
}
#[derive(Deserialize)] struct PasswdHelper(#[serde(with = "PasswdDef")] Passwd);

fn nss_hook_prepare() -> Config {
    let conf = get_config(None);

    let mut log_level = log::LevelFilter::Error;
    if get_optional(&conf, "nss.debug").unwrap_or_default() {
        log_level = log::LevelFilter::Debug;
    }
    setup_log(log_level);

    return conf;
}

fn get_current_user() -> String {
    let euid;
    let euser;
    unsafe {
        euid = geteuid();
        euser = CStr::from_ptr((*getpwuid(euid)).pw_name);
    };
    euser.to_str().ok().unwrap().to_string()
}

struct OidcPasswd;

impl PasswdHooks for OidcPasswd {
    fn get_all_entries() -> Response<Vec<Passwd>> {
        let conf = nss_hook_prepare();
        let mut cache = get_cache();
        let user = get_current_user();
        let token = match cache.load_user_token(&user) {
            Some(t) => t,
                // FIXME Implement caching of system token
                debug!("Could not find a user token for {} to request NSS data; trying client credentials", user);
                match get_access_token_client(&conf, "nss", "", "") {
                    Ok(ct) => {
                        ctc = ct.clone();
                        &ctc
                    },
                    Err(_) => {
                        error!("Failed to get access token with client credentials");
                        return Response::Unavail;
                    }
                }
        let data: Vec<PasswdHelper> = match get_data_jq(&conf, "nss", "passwd.list", "".to_string(), &token, true) {
            Err(_) => {
                error!("Could not load JSON data for passwd");
                return Response::Unavail;
            }
        Response::Success(data.into_iter().map(|p| p.0).collect())
    fn get_entry_by_uid(uid: libc::uid_t) -> Response<Passwd> {
        let conf = nss_hook_prepare();
        let mut cache = get_cache();

        let user = get_current_user();
        let ctc;
        let token = match cache.load_user_token(&user) {
            Some(t) => t,
            None => {
                // FIXME Implement caching of system token
                debug!("Could not find a user token for {} to request NSS data; trying client credentials", user);
                match get_access_token_client(&conf, "nss", "", "") {
                    Ok(ct) => {
                        ctc = ct.clone();
                        &ctc
                    },
                    Err(_) => {
                        error!("Failed to get access token with client credentials");
                        return Response::Unavail;
                    }
                }
            }
        };

        let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_uid", uid.to_string(), &token, false).map(|PasswdHelper(p)| p) {
            Ok(d) => d,
            Err(_) => {
                error!("Could not load JSON data for passwd");
                return Response::NotFound;
            }
        };
        Response::Success(data)
    fn get_entry_by_name(name: String) -> Response<Passwd> {
        let conf = nss_hook_prepare();
        let mut cache = get_cache();

        let user = get_current_user();
        let ctc;
        let token = match cache.load_user_token(&user) {
            Some(t) => t,
            None => {
                // FIXME Implement caching of system token
                debug!("Could not find a user token for {} to request NSS data; trying client credentials", user);
                match get_access_token_client(&conf, "nss", "", "") {
                    Ok(ct) => {
                        ctc = ct.clone();
                        &ctc
                    },
                    Err(_) => {
                        error!("Failed to get access token with client credentials");
                        return Response::Unavail;
                    }
                }
            }
        };

        let data: Passwd = match get_data_jq(&conf, "nss", "passwd.by_name", name, &token, false).map(|PasswdHelper(p)| p) {
            Ok(d) => d,
            Err(_) => {
                error!("Could not load JSON data for passwd");
                return Response::NotFound;
            }
        };
        Response::Success(data)
    }
}

libnss_passwd_hooks!(oidc, OidcPasswd);