Spring naar hoofdtekst

Wachtwoordexport met kpcli en Expect

Geplaatst op door ,
Laatste aanpassing op .

Inleiding

Zoals ik in een eerdere blogpost beschreef, maak ik gebruik van kpcli, een commandline applicatie die databases met KeePass-wachtwoorden kan beheren. De tot nu toe ontbrekende functie binnen kpcli was een mogelijkheid om een lijst van wachtwoorden te exporteren. Hiermee zou je dan bijvoorbeeld een papieren back-up kunnen maken.

Ja, er bestaat minimaal één KeePass2-compatibel programma voor GNU/Linux dat deze mogelijkheden wel biedt, maar het is jammer genoeg (nog) niet toegankelijk voor mijn schermlezer. Ik zocht verder en vond geen alternatief dat zowel toegankelijk was, als kon exporteren onder GNU/Linux.

Automatisering

Toen bedacht ik, dat kpcli op zich alle informatie op het scherm toont, als je het juiste commando geeft:

kpcli:/> show /Root/Path/To/Entry

 Path: /Root/Path/To
Title: Entry
Uname: myuser
 Pass: My$upeR$EcReTp@s$W0Rd
  URL: https://example.com/login.php
Notes:

kpcli:/>

Er moest toch een manier zijn om de volledige boomstructuur van de database te doorlopen, en zo de gegevens op het scherm te toveren? Ik moest op de één of andere manier een interactieve sessie met kpcli kunnen automatiseren. Dan restte alleen nog het 'van het scherm plukken' en wegschrijven als bestand.

Expect

Met de juiste zoektermen kwam ik uiteindelijk terecht bij Expect, een uitbreiding van de TCL programmeer­taal, die ik enkel kende uit Mastering Regular Expressions van Jeffrey Friedl. Het is een programma dat speciaal bedoeld is, om volgens een script te kunnen reageren en anticiperen op bestaande commandline programma's - precies wat ik zocht!

#!/usr/bin/expect -f
# Een simpel voorbeeld van een Expect-script, met dank aan Wikipedia

# Open an ftp session to a remote server, and wait for a username prompt.
spawn ftp "ftp.example.com"
expect "username:"
# Send the username, and then wait for a password prompt.
send "my_user_id\r"
expect "password:"
# Send the password, and then wait for an ftp prompt.
send "my_password\r"
expect "ftp>"
# Switch to binary mode, and then wait for an ftp prompt.
send "bin\r"
expect "ftp>"
# Turn off prompting.
send "prompt\r"
expect "ftp>"
# Get all the files
send "mget *\r"
expect "ftp>"
# Exit the ftp session, and wait for a special end-of-file character.
send "bye\r"
expect eof

Bovenstaand voorbeeld toont de meest gebruikte Expect-specifieke commando's. In totaal had ik drie dagen nodig om me thuis te voelen in de syntax van TCL. Daarbij was het principe van de export relatief eenvoudig:

  1. de hoofdmap van de wachtwoorddatabase uitlezen
  2. voor elk item een show-commando uitvoeren
  3. elke submap uitlezen en items daarin verwerken

Script

Ik zal hieronder het script in verkorte versie beschrijven. De volledige versie is als download beschikbaar.

# Functie die een tekenreeks voorbereid om te worden gebruikt in een CSV-bestand
proc escapeCSV { s } {
  regsub -all {\"} $s "\"\"" s      # verdubbel alle aanhalingstekens
  regsub -all {\r\n\s+} $s "\r\n" s # verwijder alle witruimte aan regelbegin
  set s [ string trim $s ]          # verwijder alle witruimte aan begin/einde
  return \"$s\"                     # zet aanhalingstekens voor en achter
}

# Laat één item zien en schrijf dit weg naar het export-bestand
proc doEntry { itemT e } {
  global exportFile

  set j [regsub {\s*[0-9]+\.\s*(.*?)(\s{3,}.*)?} $e {\1}]
  send "show \"$itemT$j\"\r"
  sleep 0.3

  # Hier wordt het spannend; deze reguliere expressie plukt alle gegevens
  # uit de output van 'show'. Let met name op de ANSI-escape sequence die
  # kpcli rondom het wachtwoord gebruikt om rode tekst op rode achtergrond te
  # tonen. Dit is daardoor wel te kopiëren, maar niet visueel te zien.
  expect -re "Title: (.*?)\r\nUname: (.*?)\r\n Pass: \u001b\\\[31;41m(.*?)\u001b\\\[0m\r\n  URL: (.*?)\r\nNotes: (.*)\r\n\r\n.*"

  set fTitle [escapeCSV $expect_out(1,string)]
  set fUname [escapeCSV $expect_out(2,string)]
  set fPass  [escapeCSV $expect_out(3,string)]
  set fURL   [escapeCSV $expect_out(4,string)]
  set fNotes [escapeCSV $expect_out(5,string)]

  set csv "$fTitle,$fUname,$fPass,$fURL,$fNotes"
  puts $exportFile $csv   # schrijf de regel weg naar bestandsbuffer
  flush $exportFile       # schrijf de bestandsbuffer naar het bestand
}

# Main processing
proc doTotal { item } {

  if [regexp {/$} $item] then {
    send "ls \"$item\"\r"
    sleep 0.2

    expect {
      -re "=== Groups ===\r\n(.*)" {
        set itemS [ split $expect_out(1,string) "\r\n" ]
        foreach i $itemS {
          doTotal "$item$i"     # Heerlijk recursief
        }
      }
      -re "=== Entries ===\r\n(.*)" {
        set itemS [ split $expect_out(1,string) "\r\n" ]
        foreach i $itemS {
          doEntry $item $i
        }
      }
    }
  } else {
    doEntry "" $item
  }
  return 0
}

# Check number of arguments
# ...
# Chech for file to process
# ...

# Open export-file for writing
set exportFile [open "~/keepass-export.csv" "w"]
puts $exportFile "Title,Uname,Pass,URL,Notes"                    # CSV-header
flush $exportFile

# Ask user for password to open file
# ...

# Spawn an instance of kpcli
spawn kpcli --kdb [lindex $::argv 0]           # Open kpcli met .kdbx-bestand
expect "Please provide the master password: "
send "$pass\r"
sleep 1
expect 'kpcli:/> ' {

  # Execute main call to 'ls'
  send "ls /\r"
  sleep 0.2
  expect -re "=== (Groups|Entries) ===\r\n(.*)"
  set body $expect_out(2,string)

  set itemS [ split $body "\r\n" ]
  foreach i $itemS {
    doTotal "$i"
  }

  send "\r"
  sleep 0.2
  expect 'kpcli:/> '
  send "quit\r"
  expect eof
}

# Close export-file
close $exportFile

exit

Conclusie

Een programma of -uitbreiding begint vaak als project dat een bepaald probleem of tekortkoming van een bestaande applicatie oplost. Dit script is precies zo: Ik bouwde wat ik nodig had en nergens anders kon vinden. Het was voor mij een uitdaging om in de vorm van TCL en Expect met een nieuwe programmeertaal kennis te maken.

Natuurlijk was het even doorbijten, maar het was de moeite meer dan waard. Ik kan nu met de spreekwoor­delijke druk op de knop onder GNU/Linux een KeePass-export maken met enkel en alleen toegankelijke CLI-programma's.

Inhoudsopgave

Doorzoek de onderstaande categorieën om de lijst met artikelen te filteren.