Categories: git, Security

Storing Git Credentials on Ubuntu

To store encrypted git credentials on disk in Ubuntu, install pass and the git-credential-manager. We will use gpg to generate a key that pass will use for secure storage and retrieval of credentials. Use these commands to get everything set up for git:

cd ~/Downloads
wget https://github.com/git-ecosystem/git-credential-manager/releases/download/v2.1.2/gcm-linux_amd64.2.1.2.deb

sudo dpkg -i gcm-linux_amd64.2.1.2.deb
git-credential-manager configure
git config --global credential.credentialStore

gpg --gen-key

sudo apt install pass
pass init <generated-key>

Background

The GitHub PAT I have been using on my Ubuntu VM recently expired. Authentication failed when I tried to push to my repo. I generated a new PAT as outlined at https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token and entered it on the command line when running git push. Of course, I got something wrong entering the PAT manually (assuming it would get saved).

saint@ubuntuvm:~/repos/scratchpad$ git push
Username for 'https://github.com': swesonga
Password for 'https://swesonga@github.com': 
remote: Permission to swesonga/scratchpad.git denied to swesonga.
fatal: unable to access 'https://github.com/swesonga/scratchpad/': The requested URL returned error: 403

Instead of fighting with this command line, I decided to educate myself on the proper way to do this. ubuntu git keychain – Search (bing.com) led me to this post on git – How to store your github https password on Linux in a terminal keychain? – Stack Overflow, which states that the 2022 answer would be to use the Microsft cross-platform GCM (Git Credential Manager). The git-credential-manager/docs/install.md page links to the instructions at git-credential-manager/docs/credstores.md. I download the .deb file from Release GCM 2.1.2 ยท git-ecosystem/git-credential-manager (github.com).

saint@ubuntuvm:~/repos/scratchpad$ sudo dpkg -i ~/Downloads/gcm-linux_amd64.2.1.2.deb 
[sudo] password for saint: 
Selecting previously unselected package gcm.
(Reading database ... 272980 files and directories currently installed.)
Preparing to unpack .../gcm-linux_amd64.2.1.2.deb ...
Unpacking gcm (2.1.2) ...
Setting up gcm (2.1.2) ...
saint@ubuntuvm:~/repos/scratchpad$ which git-credential-manager
/usr/local/bin/git-credential-manager
saint@ubuntuvm:~/repos/scratchpad$ git-credential-manager configure
Configuring component 'Git Credential Manager'...
Configuring component 'Azure Repos provider'...

The git push experience is now different:

saint@ubuntuvm:~/repos/scratchpad$ git push
fatal: No credential store has been selected.

Set the GCM_CREDENTIAL_STORE environment variable or the credential.credentialStore Git configuration setting to one of the following options:

  secretservice : freedesktop.org Secret Service (requires graphical interface)
  gpg           : GNU `pass` compatible credential storage (requires GPG and `pass`)
  cache         : Git's in-memory credential cache
  plaintext     : store credentials in plain-text files (UNSECURE)

See https://aka.ms/gcm/credstores for more information.

Username for 'https://github.com':
saint@ubuntuvm:~/repos/scratchpad$ git config --global credential.credentialStore
saint@ubuntuvm:~/repos/scratchpad$ git push
fatal: Password store has not been initialized at '/home/saint/.password-store'; run `pass init <gpg-id>` to initialize the store.
See https://aka.ms/gcm/credstores for more information.
Username for 'https://github.com': 

Since I own the VM, I don’t mind credentials being stored on disk (but not in plain text), so I set up gpg and pass as instructed.

saint@ubuntuvm:~$ gpg --gen-key
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: Saint Wesonga
Email address: saint@swesonga.org
You selected this USER-ID:
    "Saint Wesonga <saint@swesonga.org>"
...

saint@ubuntuvm:~$ sudo apt install pass
[sudo] password for saint: 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  libqrencode4 qrencode tree xclip
Suggested packages:
  libxml-simple-perl python ruby
The following NEW packages will be installed:
  libqrencode4 pass qrencode tree xclip
0 upgraded, 5 newly installed, 0 to remove and 92 not upgraded.
Need to get 151 kB of archives.
After this operation, 442 kB of additional disk space will be used.
Do you want to continue? [Y/n]
...

saint@ubuntuvm:~$ pass init ABCDEF0123456789
mkdir: created directory '/home/saint/.password-store/'
Password store initialized for ABCDEF0123456789

Apparently I used the wrong value for the key but git push is unfazed – it pushes successfully after the browser authentication completes. I’m not sure what is happening now since browser authentication is in use but as long as I can push, I can forge ahead with other tasks.

saint@ubuntuvm:~/repos/scratchpad$ git push
info: please complete authentication in your browser...
fatal: Failed to encrypt file '/home/saint/.password-store/git/https/github.com/swesonga.gpg' with gpg. exit=2, out=, err=gpg: <WRONG HEX VALUE>: skipped: No public key
gpg: [stdin]: encryption failed: No public key

Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 6 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 745 bytes | 745.00 KiB/s, done.
Total 6 (delta 3), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (3/3), completed with 3 local objects

Update: 2023-09-20. Use pass rm -r git to authenticate in the browser the next time git push is executed (e.g. if the password store secret is lost).


Categories: Security

Limiting SSH Identifier File Permissions

I recently needed to Use SSH keys to connect to a Linux Azure VMs from my primary development machine, a Windows desktop. OpenSSH has been available on Windows since 2018 as per this OpenSSH for Windows overview. I downloaded my private key for the Azure VM to a file called my_key.pem. Just to be sure I knew which executable would run when I launched ssh, I used this command line.

C:\> where ssh
C:\Windows\System32\OpenSSH\ssh.exe

I then passed the -i my_key.pem option to ssh when connecting to the VM.

ssh -J user1@ipaddress1 -i my_key.pem user2@ipaddress2

It was then that I discovered that ssh checks the file permissions on Windows and considered them too open by default. This is the error I got:

Bad permissions. Try removing permissions for user: BUILTIN\\Users (S-1-5-32-545) on file C:/.../my_key.pem.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'my_key.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "my_key.pem": bad permissions
someuser@0.0.0.0: Permission denied (publickey).

The Security identifiers on Windows are well documented. BUILTIN\\Users (S-1-5-32-545)

A security identifier is used to uniquely identify a security principal or security group. Security principals can represent any entity that can be authenticated by the operating system, such as a user account, a computer account, or a thread or process that runs in the security context of a user or computer account.

Security identifiers
S-1-5-32-545UsersA built-in group. After the initial installation of the operating system, the only member is the Authenticated Users group.
Description of the “Users” Group
Security identifiers

Fortunately, someone else ran into this way before me: amazon web services – Set pem file permissions for AWS without chmod on Windows – Stack Overflow. To see the current file permissions, run icacls without any additional flags.

C:\> icacls my_key.pem
my_key.pem BUILTIN\Administrators:(I)(F)
           NT AUTHORITY\SYSTEM:(I)(F)
           BUILTIN\Users:(I)(RX)
           NT AUTHORITY\Authenticated Users:(I)(M)

Successfully processed 1 files; Failed processing 0 files

The solution from that StackOverflow post is to run the commands below.

icacls my_key.pem /reset
icacls my_key.pem /grant:r %username%:(R)
icacls my_key.pem /inheritance:r

The icacls docs explain that /reset “Replaces ACLs with default inherited ACLs for all matching files.” That doesn’t change anything on my system. The /grant option adds my personal account to the list of accounts with permission to the file.

Grants specified user access rights. Permissions replace previously granted explicit permissions.

Not adding the :r, means that permissions are added to any previously granted explicit permissions.

icacls | Microsoft Learn

The /inheritance:r option removes the 4 security identifiers shown previously from the private key file’s DACL. SSH is now happy to get the authentication identity from this private key file.


Categories: Security

Building OpenSSL on Windows

OpenSSL is one of the easier open source projects I have built on Windows. To build it for inclusion with any projects that will be built with Visual C++:

  1. Install Visual Studio 2022 with the “Desktop development with C++” workload.
  2. Install Strawberry Perl for Windows. Ensure perl.exe is added to your PATH environment variable (manually update the PATH if not).
  3. Install NASM and add its path to the PATH environment variable.
  4. Open a Developer Command prompt.
  5. Clone the openssl: TLS/SSL and crypto library GitHub repo (if not yet done).
  6. Change the current directory to the root of the cloned OpenSSL repo then run these commands, substituting your own paths in the first command:
perl Configure VC-WIN64A --prefix=C:/dev/ssl/package-nmake/OpenSSL --openssldir=C:/dev/ssl/package-nmake/SSL

nmake
nmake install

The installation should be in the directory specified by the --openssldir configuration option. The rest of this post is the background experimentation I did to get a local build of the sources.

Native Builds Using MinGW

The instructions for building OpenSSL on Windows have prerequisites like perl and nasm that I didn’t have. Therefore, I decided to start with the instructions for Native builds using MinGW since I already have MSYS2 installed from earlier work Building Octave on Windows. I still wasn’t sure which executable to check for to verify that the compiler is present, so the list of files in the mingw-w64-x86_64-gcc MSYS2 Package was helpful. I launched the MSYS2 MINGW64 shell then run the commands below (see openssl/INSTALL.md)

cd /c/repos/openssl
./Configure
make

The configure command took about 30sec on my desktop and output:

Configuring OpenSSL version 3.2.0-dev for target mingw64
Using os-specific seed configuration
Created configdata.pm
Running configdata.pm
Created Makefile.in
Created Makefile
Created include/openssl/configuration.h

**********************************************************************
***                                                                ***
***   OpenSSL has been successfully configured                     ***
***                                                                ***
***   If you encounter a problem while building, please open an    ***
***   issue on GitHub <https://github.com/openssl/openssl/issues>  ***
***   and include the output from the following command:           ***
***                                                                ***
***       perl configdata.pm --dump                                ***
***                                                                ***
***   (If you are new to OpenSSL, you might want to consult the    ***
***   'Troubleshooting' section in the INSTALL.md file first)      ***
***                                                                ***
**********************************************************************

The make command took about 21.5 minutes to complete. make test completed in about 27.5 minutes with the report below. I completely ignored the fact that there were failed tests in a build of the cloned sources.

Test Summary Report
-------------------
01-test_symbol_presence.t             (Wstat: 512 (exited 2) Tests: 3 Failed: 2)
  Failed tests:  2-3
  Non-zero exit status: 2
81-test_cmp_cli.t                     (Wstat: 512 (exited 2) Tests: 9 Failed: 2)
  Failed tests:  8-9
  Non-zero exit status: 2
Files=281, Tests=3338, 1652 wallclock secs ( 5.20 usr  1.36 sys + 193.58 cusr 525.59 csys = 725.74 CPU)
Result: FAIL
make[1]: *** [Makefile:3446: run_tests] Error 1
make[1]: Leaving directory '/c/repos/openssl'
make: *** [Makefile:3442: tests] Error 2

Next, I looked into how to create an install of the build. Only the DESTDIR option was needed.

make DESTDIR=/c/dev/ssl/package install

Unfortunately, the build generated was not going to be useful outside MinGW (obvious from looking at the artifacts, but should have been obvious from the outset). Well, it was now time to look into using Visual C++.

Native Builds using Visual C++

I downloaded those pesky prerequisites: Strawberry Perl for Windows and NASM (actually was trivial, compared to the nightmare of installation packages I had anticipated). After installing Strawberry and reopening the command prompt, perl.exe is in the path.

Installing Strawberry Perl for Windows

NASM looked like it has support for the now ancient Visual Studio 2008. It did not update the PATH though so I manually added C:\Program Files\NASM to my user PATH environment variable.

Installing NASM on Windows

I then opened a developer command prompt and ran the build commands:

perl Configure VC-WIN64A

This took about 23 seconds on my desktop and gave similar output to the MSYS configure:

Configuring OpenSSL version 3.2.0-dev for target VC-WIN64A
Using os-specific seed configuration
Created configdata.pm
Running configdata.pm
Created makefile.in
Created makefile
Created include\openssl\configuration.h

**********************************************************************
***                                                                ***
***   OpenSSL has been successfully configured                     ***
***                                                                ***
***   If you encounter a problem while building, please open an    ***
***   issue on GitHub <https://github.com/openssl/openssl/issues>  ***
***   and include the output from the following command:           ***
***                                                                ***
***       perl configdata.pm --dump                                ***
***                                                                ***
***   (If you are new to OpenSSL, you might want to consult the    ***
***   'Troubleshooting' section in the INSTALL.md file first)      ***
***                                                                ***
**********************************************************************

Kicked off nmake and about 10 minutes later, it ended with these errors, which appear to be a result of conflicts between the Visual C++ and GCC compilers:

   Creating library libcrypto.lib and object libcrypto.exp
libcrypto-shlib-a_time.obj : error LNK2019: unresolved external symbol __imp__timezone referenced in function ossl_asn1_string_to_time_t
libcrypto-shlib-srp_vfy.obj : error LNK2019: unresolved external symbol ___chkstk_ms referenced in function t_fromb64.constprop.0
libcrypto-shlib-ts_rsp_verify.obj : error LNK2001: unresolved external symbol ___chkstk_ms
...
libcrypto-shlib-bio_addr.obj : error LNK2019: unresolved external symbol WspiapiGetNameInfo referenced in function addr_strings
libcrypto-shlib-bio_addr.obj : error LNK2019: unresolved external symbol gai_strerrorA referenced in function addr_strings
libcrypto-shlib-bio_addr.obj : error LNK2019: unresolved external symbol WspiapiFreeAddrInfo referenced in function BIO_ADDRINFO_free
libcrypto-shlib-bio_addr.obj : error LNK2019: unresolved external symbol WspiapiGetAddrInfo referenced in function BIO_lookup
libcrypto-shlib-bss_log.obj : error LNK2019: unresolved external symbol __mingw_vsprintf referenced in function sprintf.constprop.0
libcrypto-shlib-dso_win32.obj : error LNK2001: unresolved external symbol __mingw_vsprintf
libcrypto-shlib-eng_openssl.obj : error LNK2019: unresolved external symbol __mingw_vfprintf referenced in function fprintf
libcrypto-shlib-ui_openssl.obj : error LNK2001: unresolved external symbol __mingw_vfprintf
libcrypto-shlib-http_lib.obj : error LNK2019: unresolved external symbol __mingw_vsscanf referenced in function sscanf
libcrypto-shlib-v3_utl.obj : error LNK2001: unresolved external symbol __mingw_vsscanf
libcrypto-shlib-cryptlib.obj : error LNK2019: unresolved external symbol __imp__vsnwprintf referenced in function OPENSSL_showfatal
libcrypto-shlib-cryptlib.obj : error LNK2019: unresolved external symbol __imp__vsnprintf referenced in function OPENSSL_showfatal
libcrypto-3-x64.dll : fatal error LNK1120: 11 unresolved externals
NMAKE : fatal error U1077: 'cmd' : return code '0x1'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\bin\HostX64\x64\nmake.exe"' : return code '0x2'
Stop. 

Running nmake clean deleted the 2328 .obj files revealed by dir /w /s *.obj – this is where using a separate folder for the build would be helpful.

nmake ran for just under 12 minutes. Since I didn’t use the --prefix and --openssldir options when configuring, I tried to set these environment variables before packaging (as described in the docs).

set PREFIX=C:\dev\ssl\package-nmake\OpenSSL
set OPENSSLDIR=C:\dev\ssl\package-nmake\SSL

nmake install

nmake install failed with a permission denied error, which was unexpected given that I set the environment variables as instructed:

...
        cmd /C ""link" /nologo /debug /dll  /nologo /debug @C:\Users\saint\AppData\Local\Temp\nm6D37.tmp /implib:libssl.lib || (DEL /Q libssl-3-x64.* libssl.lib & EXIT 1)"
LINK : libssl-3-x64.dll not found or not built by the last incremental link; performing full link
   Creating library libssl.lib and object libssl.exp
        IF EXIST libssl-3-x64.dll.manifest  "mt" -nologo -manifest libssl-3-x64.dll.manifest -outputresource:libssl-3-x64.dll
        IF EXIST apps\libssl-3-x64.dll DEL /Q /F apps\libssl-3-x64.dll
        IF EXIST test\libssl-3-x64.dll DEL /Q /F test\libssl-3-x64.dll
        IF EXIST fuzz\libssl-3-x64.dll DEL /Q /F fuzz\libssl-3-x64.dll
        COPY libssl-3-x64.dll apps
        1 file(s) copied.
        COPY libssl-3-x64.dll test
        1 file(s) copied.
        COPY libssl-3-x64.dll fuzz
        1 file(s) copied.
*** Installing runtime libraries
Cannot create directory C:/Program Files/OpenSSL: Permission denied
NMAKE : fatal error U1077: 'C:\software\strawberry\perl\bin\perl.exe' : return code '0x2'
Stop.

Here are the commands that were needed. The install step took about 2 minutes (or about 30 sec if output is redirected to disk).

perl Configure VC-WIN64A --prefix=C:/dev/ssl/package-nmake/OpenSSL --openssldir=C:/dev/ssl/package-nmake/SSL

nmake
nmake install

Outstanding Questions

  1. Why don’t the environment variables work for nmake install?

Questioning Programming Assumptions

One of the more interesting facets of programming is probably the close relationship between a program’s correctness and the set of assumptions about its execution environment and/or data. In the web world, the significance of these assumptions takes on a new height since they determine not just the correctness of the application/browser, but also how secure it is (“hackability?”). I decided to do a little test and what faster way to get em browsers running code than by bombarding em with WM_* messages. Here’s a little code snippet that I wrote up for this test (warning: will blow your computer to smithereens, compile and run at your own risk):

// SendMsg.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#include "windows.h"

#define START_MESSAGE WM_CLOSE
#define END_MESSAGE (START_MESSAGE + 0x0001)

int _tmain(int argc, _TCHAR* argv[])
{
  WPARAM wParam = 0;
  LPARAM lParam = 0;
  HWND hWnd = FindWindow(NULL, TEXT("Mozilla Firefox"));

  if (!hWnd) {
    MessageBox(NULL, TEXT("Could not find firefox window"), TEXT("Error"), MB_OK);
    return -1;
  }

  int errors = 0;

  for (DWORD i = 0; i < 10; i++) {
    if (!PostMessage(hWnd, START_MESSAGE, wParam, lParam)) {
      wchar_t buffer[4096];
      DWORD lasterror = GetLastError();

      FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lasterror, 0, buffer, 4096, NULL);

      MessageBox(NULL, buffer, TEXT("Error"), MB_OK);
      errors++;
    }
  }

  if (errors)
    return -1;

  return 0;
}

The objective of this code is to send the WM_CLOSE message to a browser a given number of times (10 in this case). It starts out with a call to FindWindow to get a handle to the Firefox window. Since the window title is hard coded, a new blank tab must be active in Firefox. Once we have the handle, all that’s left is to call PostMessage the desired number of times. We use GetLastError and FormatMessage for feedback if things do not proceed as planned. I settled on WM_CLOSE since its effect should be the most noticeable ;). Below are some of the results I found:

Firefox Close Boxes

Interestingly enough, the concept of getting a window handle based the window title worked well for Firefox, Opera, and IE8. It didn’t work in Chrome, but I didn’t want to invest the time to figure out how to get the handle there. Opera 10.53 suffers from the same problem as Firefox as pictured below (the only difference being that the browser ends up closing when one of the timeouts hits 0 so you’d better be sure you have saved all your work).

Opera Close Box

So the concept of a “Modal” dialog box seems to depend on having exactly one way to launch the box, which is definitely a questionable assumption as shown. IE8 displayed only 1 close prompt box. Kudos to them. I wonder if this type of bug effectively disappears with multiprocess browsing?

In closing, I should note that I also tried the WM_QUIT message (only 1 of which is necessary). Firefox just died as did just about everything else on my system, including explorer.exe and all the other EXEs unfortunate enough to have been running at the time (I was doing some mass message posting initially and it was only after losing a bunch of data that I decided it was in my best interest to post these messages to a specific window).

For anyone interested in running this program, note that Opera 10.53 will CRASH if you send it WM_QUIT! Crash report explains that “Opera.exe 3374 caused exception C0000005 at address 677DD1F3 (Base: 1250000).” I’ve sent in a crash report so that should hopefully get fixed soon. This shouldn’t affect web content since it can’t pull these PostMessage stunts.

Note that this program is easily modified to automate the sending of a range of messages by changing to loop limits to [START_MESSAGE, END_MESSAGE]. I will hopefully have enough time to build a full blown GUI tool for this.