Skip to content
Snippets Groups Projects
Commit 10abb205 authored by Recolic's avatar Recolic :house_with_garden:
Browse files

Merge branch 'impl/py-standalone' into 'master'

Add standalone implementation

See merge request !2
parents 50f7b4dd 0f3115c0
No related branches found
No related tags found
1 merge request!2Add standalone implementation
# gnome-keyring-yubikey-unlock
![](https://img.shields.io/badge/CXXSTD-C%2B%2B14-green)
![](https://img.shields.io/badge/CXXSTD-C%2B%2B17-green)
Use GnuPG to unlock gnome-keyring, which is supported by yubikey and other smartcard.
......@@ -15,11 +15,40 @@ Currently the only solution is to set the password of `login` keyring to empty.
I encrypt the `keyring-name : password` pair with GnuPG and save it as `secret-file`. Then on starting gnome, you have yubikey inserted. Then an auto-started script call GnuPG to decrypt the secret file, and pipe use the password to unlock your keyring. GnuPG will ask you to insert yubikey.
## Dependencies
## Usage
> I recommend you to **configure Yubikey as GPG smartcard**. The system would just ask you to unlock gnome-keyring with your default GPG software. You may generate a new GPG key for yubikey, or move your existing GPG key into yubikey. Refer to google for these knowledge.
First, download this repo. Note the `--recursive` flag, that one's important
```
git clone https://github.com/recolic/gnome-keyring-yubikey-unlock --recursive
cd gnome-keyring-yubikey-unlock/src && make && cd ..
```
Secondly, choose an implementation: `standalone` impl only allows to unlock default keyring, and `lib` impl requires an extra library.
<details>
<summary>Standalone Implementation</summary>
```
cd gnome-keyring-yubikey-unlock/src && make KEYRING_IMPL=standalone && cd ..
```
</details>
<details>
<summary>Lib Implementation</summary>
```
cd gnome-keyring-yubikey-unlock/src && make KEYRING_IMPL=lib && cd ..
```
### Extra Dependency for "lib" implementation
The project uses libgnome-keyring-dev
### Ubuntu 20.04
#### Ubuntu 20.04
libgnome-keyring-dev is not in the repositories, you have to install it and its dependencies manually:
......@@ -37,37 +66,32 @@ sudo dpkg -i libgnome-keyring-common_3.12.0-1build1_all.deb libgnome-keyring0_3.
sudo apt --fix-broken -y install
```
### Arch Linux
#### Arch Linux
```
sudo pacman -S libgnome-keyring
```
### Other Distro
#### Other Distro
If your distribution is not providing libgnome-keyring, you can get the `.so` library from <https://archlinux.org/packages/extra/x86_64/libgnome-keyring/download>.
</details>
## Usage
> I recommend you to **configure Yubikey as GPG smartcard**. The system would just ask you to unlock gnome-keyring with your default GPG software. You may generate a new GPG key for yubikey, or move your existing GPG key into yubikey. Refer to google for these knowledge.
First, build the project from source. Note the `--recursive` flag, that one's important
Then, create your secret file. You may use my naive script (just in case you don't know GnuPG usage), or create an GnuPG-encrypted file by yourself.
```
git clone https://github.com/recolic/gnome-keyring-yubikey-unlock --recursive
cd gnome-keyring-yubikey-unlock/src && make && cd ..
```
For example, you could say `login:My_Very_Long_Login_Password`. (You may use `seahorse` or `tools/list_keyrings.sh` to determine the name of your keyring)
Then, create your secret file.
<details>
<summary>To use my naive secret file creation script</summary>
```
gnome-keyring-yubikey-unlock/create_secret_file.sh /path/to/your_secret [Your GnuPG public key]
# input your keyring:password
# input your keyring_name:password
```
As an example, I need to input `login:My_Very_Long_Login_Password`. (You may use `seahorse` or `tools/list_keyrings.sh` to determine the name of your keyring)
</details>
Then, add the following command to gnome-autostart. If you don't know how to do it, [read me](doc/how-to-gnome-autostart.md)!
Then, add the following command to gnome-autostart. If you don't know how to do it, [read me](doc/how-to-gnome-autostart.md).
```
/path/to/this/project/unlock_keyrings.sh /path/to/your_secret
......
CXX ?= g++
# Accepts CXXSTD >= C++14
CXXFLAGS := $(shell pkg-config --cflags --libs gnome-keyring-1) -I ./lib -I . -std=c++14
EXTRA_FLAGS ?=
CXXFLAGS := -I ./lib -I . -std=c++17 -DKEYRING_IMPL_$(KEYRING_IMPL)
ifeq ($(KEYRING_IMPL),lib)
CXXFLAGS += $(shell pkg-config --cflags --libs gnome-keyring-1)
else ifneq ($(KEYRING_IMPL),standalone)
$(error "KEYRING_IMPL must be set to 'lib' or 'standalone'. Example: 'make KEYRING_IMPL=standalone'")
endif
secret:
mkdir -p ../bin/
$(CXX) unlock_keyrings.cc -o ../bin/unlock_keyrings $(CXXFLAGS) $(EXTRA_FLAGS)
$(CXX) unlock_keyrings.cc -o ../bin/unlock_keyrings $(CXXFLAGS)
/*
* This is an implementation to unlock gnome keyring.
* It talks to `org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface` through `/run/user/1000/bus`,
* calling the `UnlockWithMasterPassword` function, to unlock the keyring.
* It involves multiple send/recv, allows unlocking any keyring.
*/
#include <gnome-keyring-1/gnome-keyring.h>
#include <string>
#include <rlib/macro.hpp>
......
/*
* This is another implementation to unlock gnome keyring.
* It sends a hand-crafted control message to `/run/user/1000/keyring/control`, to unlock the keyring.
* This interface is easier, not requiring external lib, but only allows unlocking default keyring.
*
* Credit: https://github.com/umglurf/gnome-keyring-unlock
* https://codeberg.org/umglurf/gnome-keyring-unlock
* (This repo also tells you how to unlock with TPM)
*/
#include <rlib/macro.hpp>
#include <rlib/scope_guard.hpp>
#include <rlib/sys/sio.hpp>
#include <filesystem>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/un.h>
namespace utils {
inline auto get_control_socket() {
namespace fs = std::filesystem;
// Helper function to check if a path is a socket
auto is_socket = [](const fs::path& path) {
struct stat s;
return stat(path.c_str(), &s) == 0 && S_ISSOCK(s.st_mode);
};
if (const char* gnome_keyring_control = std::getenv("GNOME_KEYRING_CONTROL")) {
fs::path control_socket = fs::path(gnome_keyring_control) / "control";
if (fs::exists(control_socket) && is_socket(control_socket)) {
return control_socket;
}
}
if (const char* xdg_runtime_dir = std::getenv("XDG_RUNTIME_DIR")) {
fs::path control_socket = fs::path(xdg_runtime_dir) / "keyring/control";
if (fs::exists(control_socket) && is_socket(control_socket)) {
return control_socket;
}
}
throw std::runtime_error("Unable to find control socket");
}
inline int connect_unix_socket(std::string path) {
// Create a UNIX socket
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
throw std::runtime_error("Unable to create unix socket");
}
// Set up socket address
struct sockaddr_un addr {};
addr.sun_family = AF_UNIX;
std::strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
// Connect to the socket
if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1) {
close(sockfd);
throw std::runtime_error("Unable to connect to unix socket");
}
return sockfd;
}
enum ControlOp : uint32_t {
INITIALIZE = 0,
UNLOCK = 1,
CHANGE = 2,
QUIT = 4
};
enum ControlRes : uint32_t {
OK = 0,
DENIED = 1,
FAILED = 2,
NO_DAEMON = 3
};
}
constexpr auto GNOME_KEYRING_RESULT_OK = utils::ControlRes::OK;
inline auto do_unlock(std::string keyring, std::string password) {
auto sockfd = utils::connect_unix_socket(utils::get_control_socket());
rlib_defer([&] { close(sockfd); });
rlib::sockIO::quick_send(sockfd, std::string(1, '\0'));
uint32_t oplen = 8 + 4 + password.size();
uint32_t pktBuf = htonl(oplen);
rlib::sockIO::sendn_ex(sockfd, &pktBuf, sizeof(pktBuf), 0);
pktBuf = htonl(utils::ControlOp::UNLOCK);
rlib::sockIO::sendn_ex(sockfd, &pktBuf, sizeof(pktBuf), 0);
pktBuf = htonl(password.size());
rlib::sockIO::sendn_ex(sockfd, &pktBuf, sizeof(pktBuf), 0);
rlib::sockIO::quick_send(sockfd, password);
rlib::sockIO::recvn_ex(sockfd, &pktBuf, sizeof(pktBuf));
if (ntohl(pktBuf) != 8)
throw std::runtime_error("invalid api response length: expecting len = 8");
rlib::sockIO::recvn_ex(sockfd, &pktBuf, sizeof(pktBuf));
return ntohl(pktBuf);
}
inline std::string keyringResultToString(int res) {
switch (res) {
#define RLIB_IMPL_GEN_RESULT(value) RLIB_IMPL_GEN_RESULT_1(value, RLIB_MACRO_TO_CSTR(value))
#define RLIB_IMPL_GEN_RESULT_1(value, cstr) case (utils::ControlRes::value): return (cstr)
RLIB_IMPL_GEN_RESULT(OK);
RLIB_IMPL_GEN_RESULT(DENIED);
RLIB_IMPL_GEN_RESULT(FAILED);
RLIB_IMPL_GEN_RESULT(NO_DAEMON);
default:
return std::string("Unknown Result Code: ") + std::to_string(res);
}
}
\ No newline at end of file
#include <rlib/log.hpp>
#include <rlib/opt.hpp>
#include "keyring_op.hpp"
#ifdef KEYRING_IMPL_lib
#include "impl-libgnome-keyring.hpp"
#endif
#ifdef KEYRING_IMPL_standalone
#include "impl-standalone.hpp"
#endif
rlib::logger rlog(std::cerr);
......@@ -39,6 +44,11 @@ int main(int argc, char **argv) {
return 3;
}
#ifdef KEYRING_IMPL_standalone
rlog.warning("This implementation 'standalone' always unlocks your default keyring. Keyring name `{}` will be ignored. Build with KEYRING_IMPL=lib if necessary.", keyring_and_pswd.at(0));
keyring_and_pswd.at(0) = "_ignored_";
#endif
auto res = do_unlock(keyring_and_pswd.at(0), keyring_and_pswd.at(1));
auto msg = keyringResultToString(res);
if(res == GNOME_KEYRING_RESULT_OK)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment