Migrating from Gnome-Keyring to KeepasXC
I never though I'd come to this point, but here we are. Now I'll describe how I came to this point and how I migrated my GKR data to KPXC. Why I did it? Mainly because it just stopped working as it used to be. And I didn't manage (didn't have passion?) to make it working again as it has been before.
For a long time now I had GKR set using PAM, which means the GKR passphrase was also login passphrase, and mainly GKR was providing a handler for libsecrets (fdo.secrets DBus service). So yes it was accumulating all the various inputs from evolution, empathy, epiphany, gajim, GOA, whatnot. Oh, even skype! And then suddenly it stopped working. I recovered it to the level it started working with manual unlock but didn't manage to hook up back to PAM.
But then - what's a point? Its interface is very poor. Actually it doesn't even exist. Seahorse, secret-tool - they are simple, quick and dirty frontends to libsecret DBuse service, GKR doesn't have a frontend as far as I know. Not even cli utility (don't mention gnome-keyring3). And seahorse with all honesty - it does its simple job but heck, after trying KeepasXC where you can literally edit the attributes for fdo.secrets - to hell with that.
So we're coming to the point where I tried KeepassXC. And realized - it has fdo.secrets support!111. Now, I have always been a proponent of GKR but mainly because it was the only service (the only service I knew) providing fdo.libsecrets backend. And after seeing this piece - the anser is obvious. HOW DO I MIGRATE MY GKR DATA TO KEEPASXC?!?!?!. I didn't find the answer on DDG (don't take it serios, my wife is insisting I cannot speak google so how can I quack). So I made a simple script to harvest the data via fdo.secret DBus interface and secret-tool.
Conversion
secret-tool provides a simple way to retrieve passwords from GKR. I realized how simple and hence dangerous this is! It doesn't even give you a wink that somehting has retrieved a password. Very convenient. For me and malicious adversary. But this is not the point. You need to know what you are looking for - eg. provide at least one attribute which is set on an entry. But - there will always be a well-known attribute: xdg:schema. But again - we don't really want to traverse all possible known schemas, so what we can do is to list all entries with their attributes via recursive DBus call (introspect), aggregate the attributes by schema, and retrieve all entries for each schema. This is literally what the coversion script does.
First - we list all entries anf sort/unique xdg:schema attribute, then for each attribute we 'search --all xdg:schema <schema>'. Finally we need to parse output and transform it to KepassXC format. Classic ETL. As for a long-time Perl user File::KeePass was an obvious choice. This can store data in Keepass2 kdbx format which is natively (without import) supported by KeepasXC. We'd still want to use native KeePass4 kdbx file so generated kdbx is supposed to be merged into your pre-created native database, after which converted file should be deleted.
#!/usr/bin/perl -w # vim: set tw=4 sts=4 et: use strict; use utf8; use Encode; use File::KeePass; my $kdbx = File::KeePass->new; my $rg = $kdbx->add_group({title=>"Root"}); my $sg = $kdbx->add_group({title=>"Secrets", group => $rg}); # attribute.uri,attribute.username,created,label,modified,schema,secret my %map = ( label => 'title', secret => 'password' ); my %icons = ( 'org.epiphany.FormPassword' => 1, 'org.freedesktop.Secret.Generic' => 3, 'org.gnome.Empathy.Account' => 9, 'org.gnome.Evolution.Data.Source' => 10, 'org.gnome.Geary' => 19, 'org.gnome.keyring.NetworkPassword' => 27, 'org.gnome.keyring.Note' => 7, 'org.gnome.OnlineAccounts' => 12 ); sub add_entry { my %entry = @_; if($entry{password}) { if($entry{strings}{'xdg:schema'} eq 'org.gnome.keyring.NetworkPassword') { # Apply schema corrections to populate other fields if($entry{strings}{protocol} && $entry{strings}{server}) { my $url = $entry{strings}{protocol}.'://'.$entry{strings}{server}; $url .= ':'.$entry{strings}{port} if($entry{strings}{port} && scalar(grep($entry{strings}{port},(80,443)))); $url .= '/'; $entry{url} = $url; } $entry{username} = $entry{strings}{user} if($entry{strings}{user}); } $kdbx->add_entry(\%entry); } } sub parse_data { my %entry = (); for my $l (@_) { chomp($l); if($l =~ /^\[(.+)\]$/){ add_entry(%entry); %entry = ( comment => $1, group => $g, strings => {} ); } elsif($l =~ /([^ ]+) = (.+)$/) { my ($k,$v) = ($1,Encode::decode('utf8',$2)); if ($map{$k}) { $entry{ $map{$k} } = $v; } elsif($k eq 'schema') { $entry{strings}{'xdg:schema'} = $v; $entry{icon} = $icons{$v} || 0; } elsif($k =~ /^attribute\.(.+)$/) { $entry{strings}{$1} = $v; # This is duplication, but otherwise it does not work, neither refs work if($1 eq 'username') { $entry{$1} = $v; } elsif($1 eq 'uri') { $entry{url} = $v; } } else { $entry{$k} = $v; } } } add_entry(%entry); } my $schemass = `gdbus introspect -e -d org.freedesktop.secrets \ -o /org/freedesktop/secrets/collection -rp \ | grep ' Attributes = ' | sed 's/ \\+readwrite a{ss} Attributes = \ //;s/{.\\+xdg:schema.: .\\(.\\+\\).};/\\1/' | sort -u`; #print "$schemass\n"; for my $schema (split(/\n/,$schemass)) { my $data = `secret-tool search --all xdg:schema $schema 2>&1`; parse_data(split("\n", $data)); } $kdbx->save_db("Passwords.kdbx","MergeAndRemoveMe")
I was thinking to store it on git(hub|lab) repo and maybe I'll do so at some point, so far here it is in plain text. The script will create a folder named Secrets, which is supposed to be shared over fdo.secrets service. You then need to run the script, merge the generated Passwords.kdbx into your own one, kill and wipe GKR (wipe it only after you're happy with the results), enable fdo.secrets sharing of the folder, remove generated Passwords.kdbx file. And that's it. You still need to unlock the keepasxc manually but you get much more powerful interface to manage it.
Link... Wed Nov 10 21:12:55 2021