├── .dockerignore ├── integration-tests └── sshd │ ├── .dockerignore │ ├── opt │ └── bin │ │ ├── sshd-entrypoint.sh │ │ └── authorized-keys-command.sh │ ├── etc │ ├── ssh │ │ └── sshd_config │ └── nsswitch.conf │ ├── Dockerfile.ubuntu-16-04 │ ├── Dockerfile.ubuntu-18-04 │ ├── README.md │ └── Makefile ├── CONTRIBUTORS ├── headers.h ├── pam.h ├── helper.c ├── .gitignore ├── Dockerfile.ubuntu-18-04 ├── Dockerfile.ubuntu-16-04 ├── libnss_iam.h ├── .travis.yml ├── test.c ├── RELEASES.md ├── LICENSE ├── README.md ├── pam.cpp ├── Makefile ├── libnss_iam.c └── iam.cpp /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /integration-tests/sshd/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Fred Vogt 2 | -------------------------------------------------------------------------------- /headers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | -------------------------------------------------------------------------------- /integration-tests/sshd/opt/bin/sshd-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu -o pipefail 4 | 5 | /usr/sbin/sshd -d 6 | -------------------------------------------------------------------------------- /pam.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | int iam_authenticate(char *user, char *pass, char *token); 6 | 7 | #ifdef __cplusplus 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /integration-tests/sshd/etc/ssh/sshd_config: -------------------------------------------------------------------------------- 1 | AuthorizedKeysCommand /opt/bin/authorized-keys-command.sh 2 | AuthorizedKeysCommandUser root 3 | 4 | ChallengeResponseAuthentication yes 5 | 6 | UsePAM no 7 | AuthenticationMethods publickey 8 | 9 | X11Forwarding yes 10 | PrintMotd no 11 | 12 | AcceptEnv LANG LC_* 13 | 14 | Subsystem sftp /usr/lib/openssh/sftp-server 15 | -------------------------------------------------------------------------------- /helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "headers.h" 4 | 5 | /* 6 | * dan bernstein at comp.lang.c 7 | * http://www.cse.yorku.ca/~oz/hash.html 8 | */ 9 | unsigned long hash(const char *str) { 10 | unsigned long hash = 5381; 11 | int c; 12 | 13 | while (c = *str++) 14 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 15 | 16 | return hash % 65535; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /integration-tests/sshd/etc/nsswitch.conf: -------------------------------------------------------------------------------- 1 | # /etc/nsswitch.conf 2 | 3 | passwd: compat systemd iam 4 | group: compat systemd iam 5 | shadow: compat 6 | gshadow: files 7 | 8 | hosts: files dns 9 | networks: files 10 | 11 | protocols: db files 12 | services: db files 13 | ethers: db files 14 | rpc: db files 15 | 16 | netgroup: nis 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | aws-sdk-cpp 2 | iam 3 | 4 | libnss-iam-*.deb 5 | libnss-iam-*/ 6 | 7 | # Object files 8 | *.o 9 | *.ko 10 | *.obj 11 | *.elf 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Libraries 18 | *.lib 19 | *.a 20 | *.la 21 | *.lo 22 | 23 | # Shared objects (inc. Windows DLLs) 24 | *.dll 25 | *.so 26 | *.so.* 27 | *.dylib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | *.i*86 34 | *.x86_64 35 | *.hex 36 | 37 | # Debug files 38 | *.dSYM/ 39 | *.su 40 | -------------------------------------------------------------------------------- /Dockerfile.ubuntu-18-04: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | # Setup non-root user 4 | ARG nonroot_user 5 | ARG nonroot_uid 6 | 7 | RUN groupadd -g ${nonroot_uid} ${nonroot_user} 8 | RUN useradd -u ${nonroot_uid} -g ${nonroot_uid} -m ${nonroot_user} 9 | 10 | ENV DEBIAN_FRONTEND=noninteractive 11 | ENV TZ=UTC 12 | 13 | RUN apt-get update \ 14 | && apt-get install -y \ 15 | build-essential \ 16 | cmake \ 17 | git \ 18 | zlib1g-dev \ 19 | libssl-dev \ 20 | libcurl4-openssl-dev 21 | -------------------------------------------------------------------------------- /Dockerfile.ubuntu-16-04: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | # Setup non-root user 4 | ARG nonroot_user 5 | ARG nonroot_uid 6 | 7 | RUN groupadd -g ${nonroot_uid} ${nonroot_user} 8 | RUN useradd -u ${nonroot_uid} -g ${nonroot_uid} -m ${nonroot_user} 9 | 10 | ENV DEBIAN_FRONTEND=noninteractive 11 | ENV TZ=UTC 12 | 13 | RUN apt-get update \ 14 | && apt-get install -y \ 15 | build-essential \ 16 | cmake \ 17 | git \ 18 | zlib1g-dev \ 19 | libssl-dev \ 20 | libcurl3-dev \ 21 | libcurl3-openssl-dev 22 | -------------------------------------------------------------------------------- /integration-tests/sshd/Dockerfile.ubuntu-16-04: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV TZ=UTC 5 | 6 | RUN apt-get update \ 7 | && apt-get install -y \ 8 | openssh-server \ 9 | sudo \ 10 | awscli \ 11 | curl \ 12 | git \ 13 | jq \ 14 | vim 15 | 16 | RUN apt-get update \ 17 | && apt-get install -y \ 18 | zlib1g \ 19 | libssl1.0 \ 20 | libcurl3 21 | 22 | RUN mkdir /var/run/sshd 23 | 24 | EXPOSE 22 25 | -------------------------------------------------------------------------------- /integration-tests/sshd/Dockerfile.ubuntu-18-04: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV TZ=UTC 5 | 6 | RUN apt-get update \ 7 | && apt-get install -y \ 8 | openssh-server \ 9 | sudo \ 10 | awscli \ 11 | curl \ 12 | git \ 13 | jq \ 14 | vim 15 | 16 | RUN apt-get update \ 17 | && apt-get install -y \ 18 | zlib1g \ 19 | libssl1.1 \ 20 | libcurl4 21 | 22 | RUN mkdir /var/run/sshd 23 | 24 | EXPOSE 22 25 | -------------------------------------------------------------------------------- /libnss_iam.h: -------------------------------------------------------------------------------- 1 | enum nss_status get_posix_iam_user(char *buffer, int buflen, struct passwd *p); 2 | enum nss_status get_posix_iam_user_by_uid(uid_t uid, char *buffer, int buflen, struct passwd *p); 3 | 4 | enum nss_status _nss_iam_setpwent(void); 5 | enum nss_status _nss_iam_endpwent(void); 6 | enum nss_status _nss_iam_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop); 7 | enum nss_status _nss_iam_getpwbyuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop); 8 | enum nss_status _nss_iam_getpwbynam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop); 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | dist: bionic 4 | os: 5 | - linux 6 | install: make deps 7 | compiler: 8 | - clang 9 | - gcc 10 | #addons: 11 | # apt: 12 | # sources: 13 | # - george-edison55-precise-backports 14 | # packages: 15 | # - cmake-data 16 | # - cmake 17 | before_install: 18 | - sudo apt-get update && sudo apt-get install -y build-essential cmake git zlib1g-dev libssl-dev libcurl4-openssl-dev 19 | - echo $LANG 20 | - echo $LC_ALL 21 | script: 22 | - 'make ${JOB}' 23 | env: 24 | matrix: 25 | - JOB=deps 26 | - JOB=libnss_iam 27 | - JOB=pam 28 | - JOB=iam 29 | - JOB=test 30 | jobs: 31 | fast_finish: true 32 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | //#include "headers.h" 2 | #include 3 | 4 | /** 5 | * test iam users retrieve 6 | */ 7 | #ifdef TEST 8 | #include 9 | #include 10 | 11 | #include "headers.h" 12 | #include "libnss_iam.h" 13 | #include "pam.h" 14 | 15 | int main(int argc, char** argv) { 16 | char buffer[1024]; 17 | struct passwd p; 18 | memset(buffer, '0', 1024); 19 | if (argc > 1) 20 | strcpy(buffer, argv[1]); 21 | 22 | if (get_posix_iam_user(buffer, 1024, &p)) { 23 | printf("%s (%d)\n", p.pw_name, p.pw_uid); 24 | if (get_posix_iam_user_by_uid(p.pw_uid, buffer, 1024, &p)) 25 | printf("%s (%d)\n", p.pw_name, p.pw_uid); 26 | } 27 | 28 | if (argc > 2) { 29 | int ret = iam_authenticate(argv[1], argv[2], argc > 2 ? argv[3] : NULL); 30 | printf("Ret: %d\n", ret); 31 | } 32 | } 33 | #endif 34 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | ## 0.1 2 | libnss-iam is built and packaged for ubuntu 16.04+: 3 | * libnss-iam-bionic-0.1.deb 4 | * libnss-iam-xenial-0.1.deb 5 | 6 | Newer ubuntu LTS releases require separate builds: 7 | ``` 8 | ubuntu 18.04+ => curl 4, openssl 1.1 9 | ubuntu 16.04 => curl 3, openssl 1.0 10 | ``` 11 | 12 | ubuntu 18.04+ compatible deb: 13 | ```bash 14 | make clean; make docker-build docker-shell 15 | $USER@aws-sdk-builder-ubuntu-18-04:~/libnss-iam$ make deps libnss_iam test deb 16 | 17 | mv libnss-iam-0.1.deb libnss-iam-bionic-0.1.deb 18 | shasum -a 512 libnss-iam-bionic-0.1.deb 19 | ``` 20 | 21 | ubuntu 16.04 compatible deb: 22 | ```bash 23 | make clean; make DISTRO=ubuntu-16-04 docker-build docker-shell 24 | $USER@aws-sdk-builder-ubuntu-16-04:~/libnss-iam$ make deps libnss_iam test deb 25 | 26 | mv libnss-iam-0.1.deb libnss-iam-xenial-0.1.deb 27 | shasum -a 512 libnss-iam-xenial-0.1.deb 28 | ``` 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 George Fleury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /integration-tests/sshd/README.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | OpenSSH Server integration tests installing and using libnss_iam.so.2 with OpenSSH(d). 3 | 4 | ## Setup 5 | Requires an AWS IAM user with ssh public key set. 6 | 7 | Ensure the `[default]` profile is a non-expired shared credentials file entry: 8 | ```ini 9 | [default] 10 | assumed_role = False 11 | aws_access_key_id = ... 12 | aws_secret_access_key = ... 13 | aws_session_token = ... 14 | aws_security_token = ... 15 | expiration = ... 16 | ``` 17 | 18 | ## Testing 19 | Build the target system docker image, run sshd + configs: 20 | ```bash 21 | libnss-iam/integration-tests/sshd$ make docker-build 22 | libnss-iam/integration-tests/sshd$ make docker-sshd 23 | ``` 24 | 25 | In another terminal connect using ssh client: 26 | ```bash 27 | # ssh-add ~/.ssh/id_rsa 28 | $ ssh -p 2222 -o IdentitesOnly=yes -i ~/.ssh/id_rsa @localhost 29 | ``` 30 | 31 | ## Debugging 32 | Run an interactive shell with the sshd configs: 33 | ```bash 34 | libnss-iam/integration-tests/sshd$ make docker-shell 35 | root@sshd-worker-ubuntu-16-04:/# dpkg -i /libnss-iam/libnss-iam-0.1.deb 36 | root@sshd-worker-ubuntu-16-04:/# ldd /lib/libnss_iam.so.2 37 | 38 | root@sshd-worker-ubuntu-16-04:/# getent passwd 39 | _nss_iam_getpwnam_r sshd 1024 0 40 | fvogt:*:65295:10:arn aws iam user/:/home/:/bin/bash 41 | ``` 42 | -------------------------------------------------------------------------------- /integration-tests/sshd/opt/bin/authorized-keys-command.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu -o pipefail 4 | 5 | username=${1?Usage: $0 } 6 | username=$(echo "$username" | sed -e 's/^# //' -e 's/+/plus/' -e 's/=/equal/' -e 's/,/comma/' -e 's/@/at/' ) 7 | 8 | ( 9 | sshkey_id=$( 10 | aws iam list-ssh-public-keys \ 11 | --user-name "$username" \ 12 | --query "SSHPublicKeys[?Status == 'Active'].[SSHPublicKeyId]" \ 13 | --output text 14 | ) 15 | ssh_key=$( 16 | aws iam get-ssh-public-key \ 17 | --user-name "$username" \ 18 | --ssh-public-key-id "$sshkey_id" \ 19 | --encoding SSH \ 20 | --query "SSHPublicKey.SSHPublicKeyBody" \ 21 | --output text 22 | ) 23 | 24 | if ! grep "^${username}:" /etc/passwd &>/dev/null; then 25 | getent passwd fvogt | tail -n 1 >> /etc/passwd 26 | fi 27 | mkdir -p "/home/${username}" 28 | for file in .bashrc .profile .bash_logout; do 29 | if [[ ! -f "/etc/skel/${file}" ]]; then 30 | cp -r "/etc/skel/${file}" "/home/${username}" 31 | fi 32 | done 33 | 34 | echo "$username ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${username}" 35 | chmod 0440 "/etc/sudoers.d/${username}" 36 | 37 | mkdir -p "/home/${username}/.ssh" 38 | if ! grep "$ssh_key" "/home/${username}/.ssh/authorized_keys" &>/dev/null; then 39 | echo "$ssh_key" >> "/home/${username}/.ssh/authorized_keys" 40 | fi 41 | chown -R "${username}" "/home/${username}/.ssh" 42 | chmod 700 "/home/${username}/.ssh" 43 | chmod 0600 "/home/${username}/.ssh/authorized_keys" 44 | ) > >(tee -a "/var/log/sshd-authorized_keys-${username}-out.log") \ 45 | 2> >(tee -a "/var/log/sshd-authorized_keys-${username}-err.log") 46 | 47 | cat "/home/${username}/.ssh/authorized_keys" 48 | -------------------------------------------------------------------------------- /integration-tests/sshd/Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | DISTRO=ubuntu-18-04 4 | 5 | docker-build: 6 | docker build -t "sshd-worker-$(DISTRO)" --file ./Dockerfile.$(DISTRO) . 7 | 8 | docker-shell: 9 | -docker run --rm -it \ 10 | --hostname "sshd-worker-$(DISTRO)" \ 11 | -p 2222:22 \ 12 | --volume "$(HOME)/.aws:/root/.aws" \ 13 | --volume "$(CURDIR)/opt:/opt:ro" \ 14 | --volume "$(CURDIR)/etc/ssh/sshd_config:/etc/ssh/sshd_config:ro" \ 15 | --volume "$(CURDIR)/etc/nsswitch.conf:/etc/nsswitch.conf:ro" \ 16 | --volume "$(CURDIR)/../..:/libnss-iam:ro" \ 17 | --entrypoint "bin/bash" \ 18 | "sshd-worker-$(DISTRO)" 19 | 20 | docker-sshd: 21 | -docker run --rm -it \ 22 | --hostname "sshd-worker-$(DISTRO)" \ 23 | -p 2222:22 \ 24 | --volume "$(HOME)/.aws:/root/.aws" \ 25 | --volume "$(CURDIR)/opt:/opt:ro" \ 26 | --volume "$(CURDIR)/etc/ssh/sshd_config:/etc/ssh/sshd_config:ro" \ 27 | --volume "$(CURDIR)/etc/nsswitch.conf:/etc/nsswitch.conf:ro" \ 28 | --volume "$(CURDIR)/../..:/libnss-iam:ro" \ 29 | --entrypoint "bin/bash" \ 30 | "sshd-worker-$(DISTRO)" \ 31 | -c "dpkg -i /libnss-iam/libnss-iam-0.1.deb && /usr/sbin/sshd -d" 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/gfleury/libnss-iam.svg?branch=master)](https://travis-ci.com/gfleury/libnss-iam) 2 | # libnss-iam 3 | Lib NSS module to integrate AWS IAM users/groups to Linux NSS subsystem. 4 | 5 | ## Building 6 | A docker ubuntu 18.04 environment is used to build the AWS c++ sdk for ubuntu 18.04+. 7 | 8 | For a ubuntu 16.04 compatible .deb use ubuntu 16.04 to build the AWS c++ sdk. 9 | 10 | Create and launch the sdk build environment: 11 | ```bash 12 | libnss-iam$ make docker-build 13 | libnss-iam$ make docker-shell 14 | ``` 15 | 16 | Compile the AWS c++ sdk: 17 | ```bash 18 | $USER@$aws-sdk-builder:~/libnss-iam$ make deps 19 | ``` 20 | 21 | Build libnss_iam.so.2: 22 | ``` 23 | libnss-iam$ make 24 | ``` 25 | 26 | Build .deb package 27 | ``` 28 | libnss-iam$ make test 29 | libnss-iam$ make deb 30 | ``` 31 | 32 | ## Integration Tests 33 | * [OpenSSHd](integration-tests/sshd) 34 | 35 | 36 | ## Links 37 | * https://github.com/aws/aws-sdk-cpp 38 | * https://github.com/aws/aws-sdk-cpp/wiki/Building-the-SDK-from-source-on-EC2 39 | * https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/welcome.html 40 | * https://aws.amazon.com/blogs/developer/aws-sdk-for-c-simplified-configuration-and-initialization/ 41 | 42 | libc: 43 | * https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html 44 | * https://www.gnu.org/software/libc/manual/html_node/Extending-NSS.html#Extending-NSS 45 | * https://www.gnu.org/software/libc/manual/html_node/NSS-Module-Function-Internals.html 46 | * https://www.gnu.org/software/libc/manual/html_node/NSS-Modules-Interface.html 47 | * https://www.gnu.org/software/libc/manual/html_node/NSS-Module-Names.html 48 | * https://www.gnu.org/software/libc/manual/html_node/Actions-in-the-NSS-configuration.html 49 | * https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html 50 | * https://www.gnu.org/software/libc/manual/html_node/Services-in-the-NSS-configuration.html 51 | * https://www.gnu.org/software/libc/manual/html_node/Adding-another-Service-to-NSS.html 52 | -------------------------------------------------------------------------------- /pam.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "pam.h" 11 | 12 | int __pam_was_initialized = 0; 13 | 14 | Aws::String get_account_id() { 15 | 16 | Aws::SDKOptions options; 17 | 18 | if (!__pam_was_initialized) { 19 | Aws::InitAPI(options); 20 | __pam_was_initialized = 1; 21 | } 22 | 23 | Aws::STS::STSClient sts; 24 | Aws::STS::Model::GetCallerIdentityRequest get_request; 25 | 26 | auto outcome = sts.GetCallerIdentity(get_request); 27 | 28 | if (!outcome.IsSuccess()) 29 | std::cout << "Failed to get called account id:" << outcome.GetError().GetMessage() << std::endl; 30 | else 31 | std::cout << outcome.GetResult().GetAccount(); 32 | 33 | return outcome.GetResult().GetAccount(); 34 | 35 | } 36 | 37 | 38 | #define DATA "redirect_uri=https%%3A%%2F%%2Fus-west-2.console.aws.amazon.com&client_id=arn%%3Aaws%%3Aiam%%3A%%3A015428540659%%3Auser%%2Fhomepage&forceMobileApp=&forceMobileLayout=&isIAMUser=1&mfaLoginFailure=&mfaType=SW&Action=login&RemainingExpiryPeriod=&account=%s&username=%s&password=%s&mfacode=%s&next_mfacode=" 39 | 40 | int iam_authenticate(char *user, char *pass, char *token) { 41 | 42 | CURL *curl; 43 | CURLcode res; 44 | 45 | if (user == NULL || pass == NULL) 46 | return 500; 47 | 48 | curl_global_init(CURL_GLOBAL_ALL); 49 | 50 | /* get a curl handle */ 51 | curl = curl_easy_init(); 52 | 53 | if (curl) { 54 | struct curl_slist *chunk = NULL; 55 | char data[1024]; 56 | 57 | /* Add a referer header */ 58 | chunk = curl_slist_append(chunk, "Referer: https://us-west-2.signin.aws.amazon.com/"); 59 | 60 | /* set our custom set of headers */ 61 | res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); 62 | 63 | /* Using HTTPS with libcurl do check certificate validity */ 64 | curl_easy_setopt(curl, CURLOPT_URL, "https://us-west-2.signin.aws.amazon.com/oauth"); 65 | 66 | /* Now specify the POST data */ 67 | snprintf(data, 1024, DATA, get_account_id().c_str(), user, pass, token == NULL ? "" : token); 68 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); 69 | 70 | /* Perform the request, res will get the return code */ 71 | res = curl_easy_perform(curl); 72 | 73 | /* Check for errors */ 74 | if (res != CURLE_OK) 75 | fprintf(stderr, "curl_easy_perform() failed: %d\n", res); 76 | 77 | curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &res); 78 | 79 | /* always cleanup */ 80 | curl_easy_cleanup(curl); 81 | } 82 | 83 | curl_global_cleanup(); 84 | return res; 85 | } 86 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | user_name=$(shell whoami) 4 | user_id=$(shell id -u) 5 | 6 | INCLUDES=\ 7 | -Iaws-sdk-cpp/aws-cpp-sdk-core/include \ 8 | -Iaws-sdk-cpp/aws-cpp-sdk-iam/include \ 9 | -Iaws-sdk-cpp/aws-cpp-sdk-sts/include \ 10 | -Iaws-sdk-cpp/build/.deps/install/include 11 | 12 | LIBS=\ 13 | aws-sdk-cpp/build/aws-cpp-sdk-iam/libaws-cpp-sdk-iam.a \ 14 | aws-sdk-cpp/build/aws-cpp-sdk-sts/libaws-cpp-sdk-sts.a \ 15 | aws-sdk-cpp/build/aws-cpp-sdk-core/libaws-cpp-sdk-core.a \ 16 | aws-sdk-cpp/build/.deps/install/lib/libaws-c-event-stream.a \ 17 | aws-sdk-cpp/build/.deps/install/lib/libaws-checksums.a \ 18 | aws-sdk-cpp/build/.deps/install/lib/libaws-c-common.a \ 19 | -lssl \ 20 | -lcrypto \ 21 | -lcurl \ 22 | -lpthread 23 | 24 | CC=gcc 25 | CPP=g++ 26 | DEBUG=-g 27 | INSTALL=/usr/bin/install 28 | INSTALL_PROGRAM=${INSTALL} 29 | INSTALL_DATA=${INSTALL} -m 644 30 | 31 | prefix="" 32 | 33 | libnss_iam: iam libnss_iam.c 34 | ${CC} ${INCLUDES} ${DEBUG} -fPIC -Wall -shared -o libnss_iam.so.2 -Wl,-soname,libnss_iam.so.2 libnss_iam.c iam.o helper.o ${LIBS} -lstdc++ 35 | 36 | iam: helper.c iam.cpp 37 | ${CPP} ${INCLUDES} ${DEBUG} -c -O3 helper.c iam.cpp -std=c++11 -fno-exceptions helper.c -fpermissive -fPIC 38 | 39 | pam: pam.cpp 40 | ${CPP} ${INCLUDES} ${DEBUG} -c -O3 pam.cpp -std=c++11 -fno-exceptions helper.c -fpermissive -fPIC 41 | 42 | test: iam pam 43 | ${CC} -O3 -o iam test.c -fno-exceptions -fPIC -DTEST pam.o iam.o helper.o ${LIBS} -lstdc++ 44 | 45 | install: 46 | # /lib/libnss_compat.so.2 -> libnss_compat-2.3.6.so 47 | ${INSTALL_DATA} libnss_iam.so.2 ${prefix}/lib/libnss_iam-2.3.6.so 48 | cd ${prefix}/lib && ln -fs libnss_iam-2.3.6.so libnss_iam.so.2 49 | ldconfig -n 50 | 51 | DISTRO=ubuntu-18-04 52 | docker-build: 53 | docker build -t "sdk-builder-$(DISTRO)" \ 54 | --build-arg nonroot_user=$(user_name) \ 55 | --build-arg nonroot_uid=$(user_id) \ 56 | --file ./Dockerfile.$(DISTRO) . 57 | 58 | docker-shell: 59 | @docker run -it --rm \ 60 | --hostname "aws-sdk-builder-$(DISTRO)" \ 61 | --volume $(HOME)/.aws:/home/$(user_name)/.aws \ 62 | --volume $(HOME)/.ssh:/home/$(user_name)/.ssh \ 63 | --volume $(CURDIR):/home/$(user_name)/libnss-iam \ 64 | --user $(user_name) \ 65 | --workdir=/home/$(user_name)/libnss-iam \ 66 | --env PS1='\u:\w\$$ ' \ 67 | --entrypoint /bin/bash \ 68 | "sdk-builder-$(DISTRO)" \ 69 | 70 | deps: 71 | if [[ ! -d aws-sdk-cpp ]]; then git clone https://github.com/aws/aws-sdk-cpp.git; fi 72 | mkdir -p aws-sdk-cpp/build 73 | (cd aws-sdk-cpp/build; cmake .. \ 74 | -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 75 | -DBUILD_ONLY="monitoring;sts;iam" \ 76 | -DAUTORUN_UNIT_TESTS=OFF \ 77 | -DBUILD_SHARED_LIBS=false) 78 | (cd aws-sdk-cpp/build; make -j 1) 79 | 80 | DPKG_VERSION=0.1 81 | DPKG_ROOT=libnss-iam-$(DPKG_VERSION) 82 | deb: 83 | mkdir -p "$(DPKG_ROOT)/DEBIAN" 84 | 85 | echo -e \ 86 | Package: libnss-iam \\n\ 87 | Version: $(DPKG_VERSION) \\n\ 88 | Architecture: amd64 \\n\ 89 | Maintainer: George Fleury \ \\n\ 90 | Homepage: https://github.com/gfleury/libnss-iam \\n\ 91 | Description: Lib NSS module to integrate IAM users/groups \\n\ 92 | > "$(DPKG_ROOT)/DEBIAN/control" 93 | 94 | echo -e \ 95 | \#!/bin/sh \\n\ 96 | set -e \\n\ 97 | ldconfig -n \\n\ 98 | exit 0 \\n\ 99 | > "$(DPKG_ROOT)/DEBIAN/postinst" && chmod 755 "$(DPKG_ROOT)/DEBIAN/postinst" 100 | 101 | mkdir -p "$(DPKG_ROOT)/lib/" 102 | cp "libnss_iam.so.2" "$(DPKG_ROOT)/lib/libnss_iam-$(DPKG_VERSION).so" 103 | chmod 644 "$(DPKG_ROOT)/lib/libnss_iam-$(DPKG_VERSION).so" 104 | cd "$(DPKG_ROOT)/lib" && ln -fs "libnss_iam-$(DPKG_VERSION).so" "libnss_iam.so.2" 105 | mkdir -p "$(DPKG_ROOT)/usr/local/bin" 106 | cp "iam" "$(DPKG_ROOT)/usr/local/bin/iam" 107 | 108 | dpkg-deb --build "$(DPKG_ROOT)" 109 | 110 | clean: 111 | rm -f libnss_iam.so.2 libnss_iam_test iam "$(DPKG_ROOT).deb" 112 | rm -rf "$(DPKG_ROOT)" 113 | rm -rf aws-sdk-cpp/build 114 | -------------------------------------------------------------------------------- /libnss_iam.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "headers.h" 5 | #include "libnss_iam.h" 6 | 7 | /* 8 | * NSS_STATUS_TRYAGAIN EAGAIN One of the functions used ran temporarily out of resources or a service is currently not available. 9 | * ERANGE The provided buffer is not large enough. The function should be called again with a larger buffer. 10 | * NSS_STATUS_UNAVAIL ENOENT A necessary input file cannot be found. 11 | * NSS_STATUS_NOTFOUND ENOENT The requested entry is not available. 12 | * NSS_STATUS_NOTFOUND SUCCESS There are no entries. Use this to avoid returning errors for inactive services which may be enabled at a later time. This is not the same as the service being temporarily unavailable. 13 | */ 14 | 15 | enum nss_status _nss_iam_setpwent(void) { 16 | printf("%s\n", __FUNCTION__); 17 | 18 | return NSS_STATUS_NOTFOUND; 19 | } 20 | 21 | enum nss_status _nss_iam_endpwent(void) { 22 | printf("%s\n", __FUNCTION__); 23 | 24 | return NSS_STATUS_NOTFOUND; 25 | } 26 | 27 | /* 28 | * struct passwd { 29 | * char *pw_name; username 30 | * char *pw_passwd; user password 31 | * uid_t pw_uid; user ID 32 | * gid_t pw_gid; group ID 33 | * char *pw_gecos; user information 34 | * char *pw_dir; home directory 35 | * char *pw_shell; shell program 36 | * }; 37 | */ 38 | enum nss_status _nss_iam_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) { 39 | printf("%s %s %s %zd %d\n", __FUNCTION__, name, buffer, buflen, *errnop); 40 | 41 | strcpy(buffer, name); 42 | 43 | return get_posix_iam_user(buffer, buflen, result); 44 | } 45 | 46 | enum nss_status _nss_iam_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) { 47 | // Lista users, each call list one user sucessive 48 | printf("%s %s %zd %d\n", __FUNCTION__, buffer, buflen, *errnop); 49 | 50 | return NSS_STATUS_NOTFOUND; 51 | } 52 | 53 | enum nss_status _nss_iam_getpwbyuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) { 54 | printf("%s %d %s %zd %d\n", __FUNCTION__, uid, buffer, buflen, *errnop); 55 | 56 | return NSS_STATUS_SUCCESS; 57 | } 58 | 59 | enum nss_status _nss_iam_getpwbynam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) { 60 | printf("%s %s %s %zd %d\n", __FUNCTION__, name, buffer, buflen, *errnop); 61 | 62 | strcpy(buffer, name); 63 | 64 | return get_posix_iam_user(buffer, buflen, result); 65 | } 66 | 67 | enum nss_status _nss_iam_getpwuid_r(uid_t uid, struct passwd* result_buf, char* buffer, size_t buflen, struct passwd** result) { 68 | printf("%s %d %s %zd \n", __FUNCTION__, uid, buffer, buflen); 69 | 70 | return get_posix_iam_user_by_uid(uid, buffer, buflen, result_buf); 71 | } 72 | 73 | /* 74 | * struct spwd { 75 | * char *sp_namp; / Login name / 76 | * char *sp_pwdp; / Encrypted password / 77 | * long sp_lstchg; / Date of last change 78 | * (measured in days since 79 | * 1970-01-01 00:00:00 +0000 (UTC)) / 80 | * long sp_min; / Min # of days between changes / 81 | * long sp_max; / Max # of days between changes / 82 | * long sp_warn; / # of days before password expires 83 | * to warn user to change it / 84 | * long sp_inact; / # of days after password expires 85 | * until account is disabled / 86 | * long sp_expire; / Date when account expires 87 | * (measured in days since 88 | * 1970-01-01 00:00:00 +0000 (UTC)) / 89 | * unsigned long sp_flag; / Reserved / 90 | * }; 91 | */ 92 | 93 | enum nss_status _nss_iam_getspnam_r(const char *name, struct spwd *s, char *buffer, size_t buflen, int *errnop) { 94 | 95 | // return NSS_STATUS_TRYAGAIN; 96 | printf("%s %s %zd %d\n", __FUNCTION__, name, buflen, *errnop); 97 | 98 | return NSS_STATUS_NOTFOUND; 99 | } 100 | 101 | enum nss_status _nss_iam_getgrent_r(struct group* result_buf, char* buffer, size_t buflen, struct group** result) { 102 | printf("%s %zd \n", __FUNCTION__, buflen); 103 | 104 | return NSS_STATUS_NOTFOUND; 105 | } 106 | 107 | enum nss_status _nss_iam_getgrgid_r(gid_t gid, struct group* result_buf, char* buffer, size_t buflen, struct group** result) { 108 | printf("%s %zd\n", __FUNCTION__, buflen); 109 | 110 | return NSS_STATUS_NOTFOUND; 111 | } 112 | 113 | enum nss_status _nss_iam_nss_iam_getgrnam_r(const char* name, struct group* result_buf, char* buffer, size_t buflen, struct group** result) { 114 | printf("%s %s %zd\n", __FUNCTION__, name, buflen); 115 | 116 | return NSS_STATUS_NOTFOUND; 117 | } 118 | 119 | 120 | -------------------------------------------------------------------------------- /iam.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CPP IAM Code linked to aws-sdk-cpp and exporting C symbols 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | Aws::IAM::Model::User get_iam_user(char *username); 17 | Aws::IAM::Model::User get_iam_uid(uid_t uid); 18 | 19 | static const char* DATE_FORMAT = "%Y-%m-%d"; 20 | 21 | unsigned long hash(const char *str); 22 | 23 | int __was_initialized = 0; 24 | 25 | /** 26 | * iam to posix users retriever 27 | */ 28 | #define __LEN 64 29 | 30 | extern "C" enum nss_status get_posix_iam_user(char *buffer, int buflen, struct passwd *p) { 31 | 32 | Aws::IAM::Model::User iam_user = get_iam_user(buffer); 33 | if (!iam_user.GetUserId().empty()) { 34 | if (p) { 35 | Aws::String home = "/home/" + iam_user.GetUserName(); 36 | char shell[] = "/bin/bash"; 37 | char pwd[] = "*"; 38 | int bytes = iam_user.GetUserName().size() + 1 > __LEN ? __LEN : iam_user.GetUserName().size() + 1, offset = 0; 39 | p->pw_name = (char*)memcpy(buffer + offset, iam_user.GetUserName().c_str(), bytes); 40 | offset += bytes; 41 | p->pw_uid = hash(iam_user.GetUserId().c_str()); 42 | p->pw_gid = 10; /* wheel harded */ 43 | bytes = iam_user.GetArn().size() + 1 > __LEN ? __LEN : iam_user.GetArn().size() + 1; 44 | p->pw_gecos = (char*)memcpy(buffer + offset, iam_user.GetArn().c_str(), bytes); 45 | offset += bytes; 46 | bytes = home.size() + 1 > __LEN ? __LEN : home.size() + 1; 47 | p->pw_dir = (char*)memcpy(buffer + offset, home.c_str(), bytes); 48 | offset += bytes; 49 | bytes = sizeof (shell) > __LEN ? __LEN : sizeof (shell); 50 | p->pw_shell = (char*)memcpy(buffer + offset, shell, bytes); 51 | offset += bytes; 52 | bytes = sizeof (pwd) > __LEN ? __LEN : sizeof (pwd); 53 | p->pw_passwd = (char*)memcpy(buffer + offset, pwd, buflen - offset); 54 | } 55 | return NSS_STATUS_SUCCESS; 56 | } 57 | return NSS_STATUS_NOTFOUND; 58 | } 59 | 60 | /** 61 | * iam uid user retriever 62 | */ 63 | extern "C" enum nss_status get_posix_iam_user_by_uid(uid_t uid, char *buffer, int buflen, struct passwd *p) { 64 | 65 | Aws::IAM::Model::User iam_user = get_iam_uid(uid); 66 | if (!iam_user.GetUserId().empty()) { 67 | if (p) { 68 | Aws::String home = "/home/" + iam_user.GetUserName(); 69 | char shell[] = "/bin/bash"; 70 | char pwd[] = "*"; 71 | int bytes = iam_user.GetUserName().size() + 1 > __LEN ? __LEN : iam_user.GetUserName().size() + 1, offset = 0; 72 | p->pw_name = (char*)memcpy(buffer + offset, iam_user.GetUserName().c_str(), bytes); 73 | offset += bytes; 74 | p->pw_uid = hash(iam_user.GetUserId().c_str()); 75 | p->pw_gid = 10; /* wheel harded */ 76 | bytes = iam_user.GetArn().size() + 1 > __LEN ? __LEN : iam_user.GetArn().size() + 1; 77 | p->pw_gecos = (char*)memcpy(buffer + offset, iam_user.GetArn().c_str(), bytes); 78 | offset += bytes; 79 | bytes = home.size() + 1 > __LEN ? __LEN : home.size() + 1; 80 | p->pw_dir = (char*)memcpy(buffer + offset, home.c_str(), bytes); 81 | offset += bytes; 82 | bytes = sizeof (shell) > __LEN ? __LEN : sizeof (shell); 83 | p->pw_shell = (char*)memcpy(buffer + offset, shell, bytes); 84 | offset += bytes; 85 | bytes = sizeof (pwd) > __LEN ? __LEN : sizeof (pwd); 86 | p->pw_passwd = (char*)memcpy(buffer + offset, pwd, buflen - offset); 87 | } 88 | return NSS_STATUS_SUCCESS; 89 | } 90 | return NSS_STATUS_NOTFOUND; 91 | } 92 | 93 | /** 94 | * iam user retriever 95 | */ 96 | Aws::IAM::Model::User get_iam_uid(uid_t uid) { 97 | Aws::SDKOptions options; 98 | Aws::IAM::Model::User user; 99 | if (!__was_initialized) { 100 | Aws::InitAPI(options); 101 | __was_initialized = 1; 102 | } 103 | { 104 | Aws::IAM::IAMClient iam; 105 | Aws::IAM::Model::ListUsersRequest request; 106 | 107 | bool done = false; 108 | while (!done) { 109 | auto outcome = iam.ListUsers(request); 110 | if (!outcome.IsSuccess()) { 111 | std::cout << "Failed to list iam users:" << 112 | outcome.GetError().GetMessage() << std::endl; 113 | break; 114 | } 115 | 116 | const auto &users = outcome.GetResult().GetUsers(); 117 | for (const auto &user : users) { 118 | if (hash(user.GetUserId().c_str()) == uid) { 119 | // Aws::ShutdownAPI(options); 120 | return user; 121 | } 122 | } 123 | 124 | if (outcome.GetResult().GetIsTruncated()) { 125 | request.SetMarker(outcome.GetResult().GetMarker()); 126 | } else { 127 | done = true; 128 | } 129 | } 130 | } 131 | // Aws::ShutdownAPI(options); 132 | return user; 133 | } 134 | 135 | /** 136 | * iam user retriever 137 | */ 138 | Aws::IAM::Model::User get_iam_user(char *username) { 139 | Aws::SDKOptions options; 140 | if (!__was_initialized) { 141 | Aws::InitAPI(options); 142 | __was_initialized = 1; 143 | } 144 | { 145 | Aws::IAM::IAMClient iam; 146 | Aws::IAM::Model::GetUserRequest get_request; 147 | get_request.SetUserName(username); 148 | 149 | auto outcome = iam.GetUser(get_request); 150 | if (!outcome.IsSuccess()) { 151 | std::cout << "Failed to list iam users:" << 152 | outcome.GetError().GetMessage() << std::endl; 153 | } 154 | // Aws::ShutdownAPI(options); 155 | return outcome.GetResult().GetUser(); 156 | 157 | } 158 | } 159 | 160 | --------------------------------------------------------------------------------