diff --git a/gnu/packages/opentaxsolver.scm b/gnu/packages/opentaxsolver.scm
new file mode 100644
index 0000000000..85ba40a2ea
--- /dev/null
+++ b/gnu/packages/opentaxsolver.scm
@@ -0,0 +1,94 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Skylar Hill <stellarskylark@posteo.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu packages opentaxsolver)
+  #:use-module (gnu packages)
+  #:use-module (guix packages)
+  #:use-module (guix download)
+  #:use-module (guix build-system glib-or-gtk)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (gnu packages gcc)
+  #:use-module (gnu packages gtk)
+  #:use-module (gnu packages pkg-config))
+
+;; The version number seems to consist of two separate parts, a typical
+;; version number (ots-version) and a tax year.  At time of writing, the
+;; version is 2022_20.00.  Both parts are used separately in the tarball uri,
+;; it is convenient to define both as separate variables.
+
+(define tax-year "2022")
+(define ots-version "20.00")
+
+(define-public opentaxsolver
+  (package
+    (name "opentaxsolver")
+    (version (string-append tax-year "_" ots-version))
+    (source (origin
+              (method url-fetch)
+              (uri (string-append "mirror://sourceforge/opentaxsolver/OTS_"
+                                  tax-year
+                                  "/v"
+                                  ots-version
+                                  "_linux/OpenTaxSolver"
+                                  version
+                                  "_linux64.tgz"))
+              (sha256
+               (base32
+                "06k0a72bmwdmr71dvrp8b4vl8vilnggsh92hrp7wjdgcjj9m074w"))
+              (patches (search-patches "opentaxsolver-file-browser-fix.patch"))))
+    (build-system glib-or-gtk-build-system)
+
+    (arguments
+     '(#:phases (modify-phases %standard-phases
+                  (delete 'configure)
+                  (replace 'build
+                    (lambda _
+                      ;; The provided Build_taxsolve_packages.sh would do this, but
+                      ;; then we can't configure CC=gcc in the make calls, which
+                      ;; errors.  Also, the provided `make clean` doesn't delete the
+                      ;; GUI.
+                      (delete-file "Run_taxsolve_GUI")
+                      (delete-file-recursively "bin")
+                      (mkdir "bin")
+                      (chdir "src/Gui_gtk")
+                      (invoke "make" "CC=gcc")
+                      (chdir "..")
+                      (invoke "make" "CC=gcc")))
+                  (delete 'check)
+                  (replace 'install
+                    (lambda* (#:key outputs #:allow-other-keys)
+                      (let* ((out (assoc-ref outputs "out"))
+                             (bin (string-append out "/bin")))
+                        ;; OTS was designed to be run straight out of the unpacked
+                        ;; tarball. Thus, the installation procedure is extremely
+                        ;; cursed. Also note we don't use the provided
+                        ;; Run_taxsolve_GUI because it's pointless in this context.
+                        (copy-recursively "../bin" bin)
+                        (copy-recursively "../tax_form_files"
+                                          (string-append out "/tax_form_files"))
+                        (copy-recursively "formdata"
+                                          (string-append out "/src/formdata"))))))))
+    (inputs (list (specification->package "gtk+@2")))
+    (native-inputs (list gcc pkg-config))
+    (synopsis "Yearly tax preparation tool")
+    (description
+     "OpenTaxSolver is a free, safe, and secure program for calculating tax form entries for federal and state personal income taxes.  It automatically fills out and prints your forms for mailing.
+
+Invoke with @code{ots_gui2} rather than the usual @code{Run_taxsolve_GUI}.")
+    (home-page "https://opentaxsolver.sourceforge.net/")
+    (license license:gpl2+)))
diff --git a/gnu/packages/patches/opentaxsolver-file-browser-fix.patch b/gnu/packages/patches/opentaxsolver-file-browser-fix.patch
new file mode 100644
index 0000000000..0e6be74f85
--- /dev/null
+++ b/gnu/packages/patches/opentaxsolver-file-browser-fix.patch
@@ -0,0 +1,58 @@
+From 96fda11a53a89c6647031f2c05ef12f1a9df6a08 Mon Sep 17 00:00:00 2001
+From: Skylar Hill <stellarskylark@posteo.net>
+Date: Tue, 31 Jan 2023 21:02:18 -0600
+Subject: [PATCH] Change default directory in file browser
+
+---
+ src/Gui_gtk/ots_gui2.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/Gui_gtk/ots_gui2.c b/src/Gui_gtk/ots_gui2.c
+index ff3366b..1247933 100644
+--- a/src/Gui_gtk/ots_gui2.c
++++ b/src/Gui_gtk/ots_gui2.c
+@@ -82,6 +82,7 @@ char ots_release_package[]="20.00";
+ #include <string.h>
+ #include <stdlib.h>
+ #include <ctype.h>
++#include <unistd.h>
+ #include <sys/stat.h>
+ // #include "backcompat.c"
+ #include "gtk_utils.c"		/* Include the graphics library. */
+@@ -109,6 +110,7 @@ char toolpath[MaxFname]="", *start_cmd;
+ int pending_compute=0, supported_pdf_form=1;
+ int filingstatus_mfj=1;
+ int round_to_whole_nums=0;
++char *working_dir[MaxFname+512];
+ 
+ void pick_file( GtkWidget *wdg, void *data );	/* Prototype */
+ void consume_leading_trailing_whitespace( char *line );
+@@ -2364,7 +2366,7 @@ void save_taxfile( GtkWidget *wdg, void *data )
+  if (cpt != 0)
+   strcpy( cpt, "_xxxx.txt" );
+  // printf("OTS_save_taxfile: dir='%s', wc='%s', fname='%s'\n", directory_dat, wildcards_fb, filename_fb );
+- Browse_Files( "File to Save As:", 2048, directory_dat, wildcards_fb, filename_fb, Save_Tax_File );
++ Browse_Files( "File to Save As:", 2048, working_dir, wildcards_fb, filename_fb, Save_Tax_File );
+ }
+ 
+ 
+@@ -3878,7 +3880,7 @@ void pick_file( GtkWidget *wdg, void *data )
+   strcpy( wildcards_fb, ".txt" );
+   strcpy( filename_fb, "" );
+   // printf("OTS_pick_file: dir='%s', wc='%s', fname='%s'\n", directory_dat, wildcards_fb, filename_fb );
+-  Browse_Files( "Select File", 2048, directory_dat, wildcards_fb, filename_fb, receive_filename );
++  Browse_Files( "Select File", 2048, working_dir, wildcards_fb, filename_fb, receive_filename );
+ }
+ 
+ 
+@@ -4019,6 +4021,7 @@ int main(int argc, char *argv[] )
+  invocation_path[k] = '\0';
+  // printf("Invocation path = '%s'\n", invocation_path);
+  set_ots_path();
++ getcwd(working_dir, MaxFname+512);
+ 
+  /* Decode any command-line arguments. */
+  argn = 1;  k=1;
+-- 
+2.38.1
+
