Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Some thoughts on connman and wicd

I discussed these thoughts in the Devuan forum[1] but wanted to publish them on my own site as well. I was a huge fan of wicd, origin site notwithstanding. It met my needs and was easily configurable and understandable. But because it's python2 only (with some experimental python3 work), it was finally dropped from Devuan Ceres. So I had to find an alternative. There's always the unpopular Network Manager. So I checked out connman, which seems to do everything I've asked it to do. It even includes its own gui and trayicon implementation (connman-gtk), but Devuan also provides a separate connman-ui trayicon utility but it duplicates the tray icon feature. So far, to integrate connman into my workflows, I have two major notes.

Disable dns caching

I want /etc/resolv.conf to be the useful file it's always been, and not just point to 127.0.1.1 or some ridiculousness. So run the daemon with -r , by setting file /etc/default/connman.

DAEMON_OPTS="-r"

It's really nice that you can configure this utility in a traditional manner! And it was clearly documented in the man page.

Add xdg autostart etnry

I add file /etc/xdg/autostart/connman-gtk.desktop on all my systems, which is in the xdg autostart directory. And xdg autostart, is, a fd.o specification that describes a common way desktop environments can act in similar ways.

[Desktop Entry]
Type=Application
Exec=connman-gtk
Name=Connman Settings
Categories=GTK;GNOME;Settings;X-GNOME-Settings-Panel;X-Unity-Settings-Panel;HardwareSettings
Keywords=connman;network;settings;wired;wifi;wi-fi;bluetooth;vpn;tethering
X-GNOME-Settings-Panel=connman-gtk
X-Unity-Settings-Panel=connman-gtk
Icon=preferences-system-network

Of course, this file has no bearing on non-xdg-compliant window managers, so I also generally add to my user Fluxbox settings (file ~/.fluxbox/startup ) the following line.

/usr/bin/connman-gtk &

Final thoughts

So far I haven't discovered how connman-gtk stores its settings that appear to be per-user, for the "prefer to load straight to tray icon." Oh dear, now that I think about it, it's probably a gsettings or dconf or similar. Say it ain't so!

References

  1. https://dev1galaxy.org/viewtopic.php?pid=24641#p24641

Using date in rpm macro

When you are building an rpm, and you want to use the date as a variable, or macro, you might already know about the percent-parenthesis syntax.

%define use_date %(date "+%F")

What took me a long time to remember is that you have to use percent percent when you want a literal percent sign.

%define use_date %( date "+%%Y%%m%%d" )

Also, you cannot use dashes (2020-09-29) in a version of a package. Use the %%Y%%m%%d for the 20200929 format! Source: https://rpm.org/user_doc/macros.html

Python3 XApp IconChooserButton demo application

If I ever need a simple iconchooser from the XApp library.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
import gi
gi.require_version("Gtk","3.0")
gi.require_version("XApp","1.0")
from gi.repository import XApp, Gtk, Gdk

class MainWindow(Gtk.Window):
   def __init__(self):
      Gtk.Window.__init__(self, title="Example window")
      self.grid = Gtk.Grid()
      self.add(self.grid)
      self.button0 = XApp.IconChooserButton()
      # technically only a regular Button can use a button-press-event, not the IconChooserButton
      #self.button0.connect("button-press-event", self.on_button0_press_event)
      self.grid.add(self.button0)

   def on_button0_press_event(self, *args):
      print("Do something on the button press!")

icon = MainWindow()
icon.connect("destroy", Gtk.main_quit)
icon.show_all()
Gtk.main()

Screenshot of IconChooserDialog from XApp library demonstrating the Numix
Circle icon
theme The XApp folks (Linux Mint team) have put into their library all the functionality that previously had to be done yourself (see Tomha/python-gtk- themed-icon-chooser). Some decent Internet documentation of the XApp library is available too: http://lazka.github.io/pgi-docs/#XApp-1.0/classes/StatusIcon.html

Setting up a transparent proxy for internal network

Overview

This document explains how to set up a web proxy on the internal network so that it can act as a configured proxy as well as transparent network proxy, including both http and https traffic. Exceptions for sites (destinations) as well as clients can be configured.

Architecture

A dd-wrt router is the heart of the example network, at 192.168.1.2. Proxy server proxy.ipa.example.com at 192.168.1.82 provides both transparent proxy and configured proxy behavior.

Configuring router for transparent proxy

The router is set up with dd-wrt firmware: DD-WRT v3.0-r43055 big (05/05/20) and contains a firewall start script (nvram get rc_firewall) which forces all World Wide Web traffic (tcp ports 80 and 443) to a transparent proxy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/sh
WEB_SERVER=192.168.1.14
PROXY_IP=192.168.1.82
OBI_IP=192.168.1.27
CHROMECAST_IP=192.168.1.29
iptables -t mangle -I PREROUTING 2 -p tcp -m multiport --dports 80,443 -s $OBI_IP -j ACCEPT
iptables -t mangle -I PREROUTING 3 -p tcp -m multiport --dports 80,443 -s $CHROMECAST_IP -j ACCEPT
iptables -t mangle -I PREROUTING 5 -p tcp -m multiport --dports 80,443 -s $PROXY_IP -j ACCEPT
iptables -t mangle -I PREROUTING 6 -p tcp -m multiport --dports 80,443 ! -s $PROXY_IP -j MARK --or 3
iptables -t mangle -I PREROUTING 7 -p tcp -m multiport --dports 80,443 -j CONNMARK --save-mark
ip route add $WEB_SERVER via $WEB_SERVER dev br0 table 2
ip route add default via $PROXY_IP dev br0 table 2
ip rule add fwmark 3 table 2

The MARK rule performs a logical OR to set just a few binary flags, so it does not merely "set" all the flags. See Weblink 7. The web server steps are there to ensure that incoming web traffic get to the web server. Observe that CHROMECAST_IP is granted ACCEPT in the firewall. The Chromecast device is given a reserved IP address in DHCP which is described later. An attempt was made to perform logging on the router level, and while this provides IP addresses, it was not sufficient for the needs of this project. See weblink 12.

Configuring the proxy server

First, configure the firewall. Set contents of file /etc/firewalld/direct.xml.

<?xml version="1.0" encoding="utf-8"?>
<direct>
<rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3130</rule>
<rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3129</rule>
</direct>

I was unable to find any other mechanism within firewalld that works for getting the traffic transparently to squid (i.e., that keeps the client IP address). See weblink 7. Also allow a few services and additional ports.

firewall-cmd --permanent --add-service=http --add-service=https --add-service=squid --add-port=3129/tcp
firewall-cmd --permanent --add-port=3130/tcp --add-port=3129/tcp
firewall-cmd --reload
Configuring squid

Configure squid itself. Fill file /etc/squid/squid.conf with contents.

# Research for a log filter includes:
# tail -f /var/log/squid/access.log | grep -iE '200 [0-9]+ GET https?:\/\/[^ ]+'

acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl localnet src fc00::/7       # RFC 4193 local private network range
acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines
acl SSL_ports port 443
acl Safe_ports port 80      # http
acl Safe_ports port 21      # ftp
acl Safe_ports port 443     # https
acl Safe_ports port 70      # gopher
acl Safe_ports port 210     # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280     # http-mgmt
acl Safe_ports port 488     # gss-http
acl Safe_ports port 591     # filemaker
acl Safe_ports port 777     # multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 3128 ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  cafile=/etc/pki/tls/certs/ca-ipa.example.com.crt \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
https_port 3129 intercept ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  cafile=/etc/pki/tls/certs/ca-ipa.example.com.crt \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
http_port 3130 intercept ssl-bump \
  cert=/etc/pki/tls/certs/proxy.ipa.example.com.pem \
  key=/etc/pki/tls/private/proxy.ipa.example.com-nopw.key \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB
sslcrtd_program /usr/lib64/squid/ssl_crtd -s /var/lib/ssl_db -M 4MB
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER

acl step1 at_step SslBump1
acl SAFE_sites ssl::server_name .telephony.goog .discord.gg
acl DIRECT_sites_name dstdomain .youtube.com
# DIRECT_sites_ip is primarily for youtube; not from nslookup; only from examining squid logs
acl DIRECT_sites_ip dst 172.217.164.74 64.233.177.106 173.194.219.95 64.233.177.99 31.13.65.1 31.13.65.36 74.125.138.95 64.233.185.139 64.233.185.91 172.217.164.78
acl DIRECT_sites any-of DIRECT_sites_name DIRECT_sites_ip
acl DIRECT_site_clients srcdomain tab1.ipa.example.com
acl WORK_clients srcdomain device1.ipa.example.com

always_direct allow DIRECT_sites DIRECT_site_clients
ssl_bump splice DIRECT_sites DIRECT_site_clients
ssl_bump splice SAFE_sites
ssl_bump splice WORK_clients
ssl_bump peek step1
ssl_bump bump all # needed for youtube somehow
coredump_dir /var/spool/squid
refresh_pattern ^ftp:       1440    20% 10080
refresh_pattern ^gopher:    1440    0%  1440
refresh_pattern -i (/cgi-bin/|\?) 0 0%  0
refresh_pattern .       0   20% 4320
strip_query_terms off    #This will allow checking which youtube URLs were visited by user

visible_hostname proxy.ipa.example.com
logformat squid %ts.%03tu %>a %>A %03>Hs %rm %>ru %[un %<a %mt

The order of these entries matters. Additionally, the Chromecast materials may be out of date, but Youtube it still important. An important consideration is to keep the work computer's vpn uninterrupted. Several comments in this file demonstrate which lines are important to that end. The certificate used here is described in a few steps. The cafile used is the root CA cert for the entire FreeIPA infrastructure in the example network. Without this configuration, it is possible that clients who trust the root CA would still not trust the web traffic because squid would have an incomplete cert chain.

Initializing squid

The dynamic ssl certificate database needs to be initialized outside of squid itself. This only needs to be run once.

/usr/lib64/squid/ssl_crtd -c -s /var/lib/ssl_db
chown -R squid /var/lib/ssl_db
restorecon -Rv /var/lib/ssl_db

Without this step, squid could fail to start, or it could fail with an error about crashing too rapidly after so long. See weblink 3.

Getting a valid ssl certificate for squid

See post Getting a valid subCA certificate for squid from FreeIPA

Operations

It is probable that in the future I will want to make changes. A few possible changes are described below.

Disabling transparent proxy at router

If the proxy server is offline, then all outbound www traffic will fail. The best way to disable the transparent proxy setting entirely is to disable the ip rule on the router. Ssh in to the router or visit the web portal. Run command:

ip rule del fwmark 0x3

To re-enable, run the one ip rule command from the rc_firewall startup script.

ip rule add fwmark 3 table 2

Adding a new exclusion for a client on router

To allow a single client unmonitored access to the www, you can add a new rule to the router. This can be achieved by running a single command, or by updating the rc_firewall script.

iptables -t mangle -I PREROUTING 1 -p tcp -m multiport --dports 80,443 -s 192.168.1.300 -j ACCEPT

Where the IP address here is the target client. To remove a specific rule, run

iptables -t mangle -D PREROUTING 1

But ensure that the rule it will remove is not the main one that allows outbound www traffic from the proxy server.

Adding new sites and clients in squid

To exclude the ssl decryption of a site in squid, add it to the SAFE_sites entry in squid.conf and restart squid. You can confirm the syntax is valid without affecting the running daemon by running the following command.

squid -k parse ; echo $?

To exclude a combination of site and client, you can add a site or client to their respesctive lists uner attributes

  • DIRECT_sites_name
  • DIRECT_sites_ip
  • DIRECT_site_clients

Using the proxy by choice

If for some reason you want to manually configure a proxy, you can set the exact following values.

export http_proxy=http://proxy:3128
export https_proxy=http://proxy:3128

It is worth noting that squid has to act differently when being used as a "transparent proxy" so those ports are 3129 (https) and 3130 (http). But port 3128 can be used explicitly for both protocols. Manually choosing to use the proxy can make troubleshooting easier. For example, mobile devices' applications rarely use the host proxy settings, but it can be easier to whitelist requests.

Assign a reserved IP address in dhcp

The internal network uses ISC dhcpd for assigning IP addresses to clients. To add a host to have a specific reservation, you have to edit the dhcpd.conf through the local mechanism dhcpd-control on host dns1.

sudo dhcpd-control --edit

This will open a cached copy of the dhcpd.conf.combined which will get replicated to host dns2 if any changes are made. The changes must be made between headings:

    • BEGIN POOLS FOR SYNC
    • END POOLS FOR SYNC

Add an entry using the MAC address of the client and the desired reserved IP address.

host Obi200 { hardware ethernet 9c:7e:fe:60:64:f3; fixed-address 192.168.1.27; }

Improvements

Need to discover how to get android to think it has Internet access.

References

Man pages

/usr/share/doc/squid-3.5.20/squid.conf.documented

Weblinks

  1. http://silverskysoft.com/open-stack-xwrpr/2015/09/creating-a-subordinate-certificate-authority-in-freeipa/
  2. https://serverfault.com/questions/624879/ssl-crtd-helpers-are-crashing-too-rapidly-in-squid
  3. https://netfilter.org/documentation/HOWTO//packet-filtering-HOWTO.txt General guide for iptables
  4. https://www.agix.com.au/minimal-transparent-squid-proxy-with-ssl-interception-bumping-on-centos-7/ useful examples about ssl_bump
  5. https://wiki.squid-cache.org/Features/SslPeekAndSplice
  6. https://wiki.dd-wrt.com/wiki/index.php/Squid_Transparent_Proxy "Alternative Solution" to Proxy Server on the LAN Subnet
  7. https://forum.dd-wrt.com/phpBB2/viewtopic.php?p=1217511#1217511 I opened a thread on dd-wrt forum for help, but it turned out to be squid-side firewall problems.
  8. https://docs.mitmproxy.org/stable/howto-transparent/ minor examples of ip rules
  9. http://www.silverhawk.net/2016/07/centos-7-squid-and-firewall.html Ultimately unused examples for firewalld and squid acls
  10. https://elatov.github.io/2019/01/using-squid-to-proxy-ssl-sites/ Great initial reference for establishing squid ssl_bump
  11. https://nlug.ml1.co.uk/2014/01/the-missing-firewall-logs-of-dd-wrt/4528 A modest attempt to log traffic on the router, without regard to www hosts and requests.

FreeFileSync 11.2 Traditional View patch

In a rare departure from established procedure, I am publishing this post immediately. FreeFileSync 11.2 was released yesterday, and I have finalized the patch that makes it possible to use the traditional view and traditional relative view! screenshot of FreeFileSync
11.2 using Traditional and Trad. Relative
views If you like this tool, and you like the traditional view where you can see the path for each file, then you should check out the patch. Source is on my gitlab. The patch is already incorporated in the freefilesync package on my OBS instance for Debian derivatives.

Getting a valid subCA certificate for squid from FreeIPA

This post is part of a larger set, and is the first that I felt like publishing.

Getting a valid ssl certificate for squid

In order to do ssl interception, squid needs to use a certificate that includes the basic constraint for signing certificates, i.e., a subordinate Certificate Authority cert. Without such a flag on the certificate used, clients could throw needless errors. Most web clients will not tolerate root or intermediate certificates that do not have the basic constraint of "Is a certificate authority." This document describes how to use FreeIPA to request such a cert. You should think carefully before doing this, because of course, a subordinate CA wields a lot of authority (when your full cert chain is being served, of course!). By default, FreeIPA does not include multiple certificate profiles, but it does include support for them.

Generating private key and csr

Of course, you start with a new private key and then certificate signing request. To request certain attributes, you have to modify the openssl.cnf template. Here is the grep -vE '^.s*(#|$)' openssl.cnf for you.

HOME            = .
RANDFILE        = $ENV::HOME/.rnd
oid_section     = new_oids
[ new_oids ]
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
[ ca ]
default_ca  = CA_default        # The default ca section
[ CA_default ]
dir     = /etc/pki/CA       # Where everything is kept
certs       = $dir/certs        # Where the issued certs are kept
crl_dir     = $dir/crl      # Where the issued crl are kept
database    = $dir/index.txt    # database index file.
new_certs_dir   = $dir/newcerts     # default place for new certs.
certificate = $dir/cacert.pem   # The CA certificate
serial      = $dir/serial       # The current serial number
crlnumber   = $dir/crlnumber    # the current crl number
crl     = $dir/crl.pem      # The current CRL
private_key = $dir/private/cakey.pem# The private key
RANDFILE    = $dir/private/.rand    # private random number file
x509_extensions = usr_cert      # The extentions to add to the cert
name_opt    = ca_default        # Subject Name options
cert_opt    = ca_default        # Certificate field options
default_days    = 365           # how long to certify for
default_crl_days= 30            # how long before next CRL
default_md  = sha256        # use SHA-256 by default
preserve    = no            # keep passed DN ordering
policy      = policy_match
[ policy_match ]
countryName     = match
stateOrProvinceName = match
organizationName    = match
organizationalUnitName  = optional
commonName      = supplied
emailAddress        = optional
[ policy_anything ]
countryName     = optional
stateOrProvinceName = optional
localityName        = optional
organizationName    = optional
organizationalUnitName  = optional
commonName      = supplied
emailAddress        = optional
[ req ]
default_bits        = 2048
default_md      = sha256
default_keyfile     = privkey.pem
distinguished_name  = req_distinguished_name
attributes      = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
string_mask = utf8only
req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName         = Country Name (2 letter code)
countryName_default     = XX
countryName_min         = 2
countryName_max         = 2
stateOrProvinceName     = State or Province Name (full name)
localityName            = Locality Name (eg, city)
localityName_default        = Default City
0.organizationName      = Organization Name (eg, company)
0.organizationName_default  = Default Company Ltd
organizationalUnitName      = Organizational Unit Name (eg, section)
commonName          = Common Name (eg, your name or your server\'s hostname)
commonName_max          = 64
emailAddress            = Email Address
emailAddress_max        = 64
[ req_attributes ]
challengePassword       = A challenge password
challengePassword_min       = 4
challengePassword_max       = 20
unstructuredName        = An optional company name
[ usr_cert ]
basicConstraints=CA:FALSE
nsComment           = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ v3_req ]
basicConstraints = CA:TRUE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, **keyCertSign**
subjectAltName = @alt_names
[alt_names]
**DNS.1 = proxy.ipa.example.com
DNS.2 = proxy.example.com
DNS.3 = webproxy.example.com
DNS.4 = http-proxy.ipa.example.com
DNS.5 = https-proxy.ipa.example.com**
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment           = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
[ tsa ]
default_tsa = tsa_config1   # the default TSA section
[ tsa_config1 ]
dir     = ./demoCA      # TSA root directory
serial      = $dir/tsaserial    # The current serial number (mandatory)
crypto_device   = builtin       # OpenSSL engine to use for signing
signer_cert = $dir/tsacert.pem  # The TSA signing certificate
certs       = $dir/cacert.pem   # Certificate chain to include in reply
signer_key  = $dir/private/tsakey.pem # The TSA private key (optional)
default_policy  = tsa_policy1       # Policy if request did not specify it
other_policies  = tsa_policy2, tsa_policy3  # acceptable policies (optional)
digests     = sha1, sha256, sha384, sha512  # Acceptable message digests (mandatory)
accuracy    = secs:1, millisecs:500, microsecs:100  # (optional)
clock_precision_digits  = 0 # number of digits after dot. (optional)
ordering        = yes   # Is ordering defined for timestamps?
tsa_name        = yes   # Must the TSA name be included in the reply?
ess_cert_id_chain   = no    # Must the ESS cert id chain be included?

The important parts are the SANs, which I've described before, and the keyCertSign. So with the prepared config file, make a key and then the csr.

openssl genrsa -aes256 -out /etc/pki/tls/private/proxy.ipa.example.com.key 2048

At this point I normally remove the passphrase because I'm working on in a single-user lab environment.

openssl rsa -in /etc/pki/tls/private/proxy.ipa.example.com.key -out /etc/pki/tls/private/proxy.ipa.example.com-nopw.key

And then formulate the request.

openssl req -config /root/openssl.cnf -key /etc/pki/tls/private/proxy.ipa.example.com-nopw.key -out /etc/pki/tls/private/proxy.ipa.example.com.csr

Follow the prompts of course, unless you hardcoded the config file. Save the contents of the csr to a FreeIPA system, preferably one of the ipa servers so the process can happen more efficiently.

Fulfilling CSR in FreeIPA

As mentioned earlier, FreeIPA starting with version 4.2.0 supports additional certificate profiles beyond the basic one that is already built in. You need to add a subordinate CA certificate profile that we will use to sign the csr for squid. Set up a file that stores the template that we will import as a new certificate profile. This is lifted entirely from Weblink 1.

desc=This certificate profile is for enrolling Subordinate Certificate Authority certificates.
visible=true
enable=true
auth.instance_id=raCertAuth
classId=caEnrollImpl
enableBy=ipara
name=Manual Certificate Manager Subordinate Signing Certificate Enrollment
input.list=i1,i2
input.i1.class_id=certReqInputImpl
input.i2.class_id=submitterInfoInputImpl
output.list=o1
output.o1.class_id=certOutputImpl
policyset.list=caSubCertSet
policyset.caSubCertSet.list=1,2,3,4,5,6,8,9,10
policyset.caSubCertSet.1.constraint.class_id=subjectNameConstraintImpl
policyset.caSubCertSet.1.constraint.name=Subject Name Constraint
policyset.caSubCertSet.1.constraint.params.pattern=.*CN=.+
policyset.caSubCertSet.1.constraint.params.accept=true
policyset.caSubCertSet.1.default.class_id=userSubjectNameDefaultImpl
policyset.caSubCertSet.1.default.name=Subject Name Default
policyset.caSubCertSet.1.default.params.name=
policyset.caSubCertSet.2.constraint.class_id=validityConstraintImpl
policyset.caSubCertSet.2.constraint.name=Validity Constraint
policyset.caSubCertSet.2.constraint.params.range=7305
policyset.caSubCertSet.2.constraint.params.notBeforeCheck=false
policyset.caSubCertSet.2.constraint.params.notAfterCheck=false
policyset.caSubCertSet.2.default.class_id=caValidityDefaultImpl
policyset.caSubCertSet.2.default.name=CA Certificate Validity Default
policyset.caSubCertSet.2.default.params.range=7305
policyset.caSubCertSet.2.default.params.startTime=0
policyset.caSubCertSet.3.constraint.class_id=keyConstraintImpl
policyset.caSubCertSet.3.constraint.name=Key Constraint
policyset.caSubCertSet.3.constraint.params.keyType=-
policyset.caSubCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp384,nistp521
policyset.caSubCertSet.3.default.class_id=userKeyDefaultImpl
policyset.caSubCertSet.3.default.name=Key Default
policyset.caSubCertSet.4.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.4.constraint.name=No Constraint
policyset.caSubCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl
policyset.caSubCertSet.4.default.name=Authority Key Identifier Default
policyset.caSubCertSet.5.constraint.class_id=basicConstraintsExtConstraintImpl
policyset.caSubCertSet.5.constraint.name=Basic Constraint Extension Constraint
policyset.caSubCertSet.5.constraint.params.basicConstraintsCritical=true
policyset.caSubCertSet.5.constraint.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.constraint.params.basicConstraintsMinPathLen=0
policyset.caSubCertSet.5.constraint.params.basicConstraintsMaxPathLen=0
policyset.caSubCertSet.5.default.class_id=basicConstraintsExtDefaultImpl
policyset.caSubCertSet.5.default.name=Basic Constraints Extension Default
policyset.caSubCertSet.5.default.params.basicConstraintsCritical=true
policyset.caSubCertSet.5.default.params.basicConstraintsIsCA=true
policyset.caSubCertSet.5.default.params.basicConstraintsPathLen=0
policyset.caSubCertSet.6.constraint.class_id=keyUsageExtConstraintImpl
policyset.caSubCertSet.6.constraint.name=Key Usage Extension Constraint
policyset.caSubCertSet.6.constraint.params.keyUsageCritical=true
policyset.caSubCertSet.6.constraint.params.keyUsageDigitalSignature=true
policyset.caSubCertSet.6.constraint.params.keyUsageNonRepudiation=true
policyset.caSubCertSet.6.constraint.params.keyUsageDataEncipherment=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyEncipherment=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyAgreement=false
policyset.caSubCertSet.6.constraint.params.keyUsageKeyCertSign=true
policyset.caSubCertSet.6.constraint.params.keyUsageCrlSign=true
policyset.caSubCertSet.6.constraint.params.keyUsageEncipherOnly=false
policyset.caSubCertSet.6.constraint.params.keyUsageDecipherOnly=false
policyset.caSubCertSet.6.default.class_id=keyUsageExtDefaultImpl
policyset.caSubCertSet.6.default.name=Key Usage Default
policyset.caSubCertSet.6.default.params.keyUsageCritical=true
policyset.caSubCertSet.6.default.params.keyUsageDigitalSignature=true
policyset.caSubCertSet.6.default.params.keyUsageNonRepudiation=true
policyset.caSubCertSet.6.default.params.keyUsageDataEncipherment=false
policyset.caSubCertSet.6.default.params.keyUsageKeyEncipherment=false
policyset.caSubCertSet.6.default.params.keyUsageKeyAgreement=false
policyset.caSubCertSet.6.default.params.keyUsageKeyCertSign=true
policyset.caSubCertSet.6.default.params.keyUsageCrlSign=true
policyset.caSubCertSet.6.default.params.keyUsageEncipherOnly=false
policyset.caSubCertSet.6.default.params.keyUsageDecipherOnly=false
policyset.caSubCertSet.8.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.8.constraint.name=No Constraint
policyset.caSubCertSet.8.default.class_id=subjectKeyIdentifierExtDefaultImpl
policyset.caSubCertSet.8.default.name=Subject Key Identifier Extension Default
policyset.caSubCertSet.8.default.params.critical=false
policyset.caSubCertSet.9.constraint.class_id=signingAlgConstraintImpl
policyset.caSubCertSet.9.constraint.name=No Constraint
policyset.caSubCertSet.9.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withEC,SHA512withEC
policyset.caSubCertSet.9.default.class_id=signingAlgDefaultImpl
policyset.caSubCertSet.9.default.name=Signing Alg
policyset.caSubCertSet.9.default.params.signingAlg=-
policyset.caSubCertSet.9.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.9.constraint.name=No Constraint
policyset.caSubCertSet.9.default.class_id=crlDistributionPointsExtDefaultImpl
policyset.caSubCertSet.9.default.name=CRL Distribution Points Extension Default
policyset.caSubCertSet.9.default.params.crlDistPointsCritical=false
policyset.caSubCertSet.9.default.params.crlDistPointsEnable_0=true
policyset.caSubCertSet.9.default.params.crlDistPointsIssuerName_0=CN=Certificate Authority,o=ipaca
policyset.caSubCertSet.9.default.params.crlDistPointsIssuerType_0=DirectoryName
policyset.caSubCertSet.9.default.params.crlDistPointsNum=1
policyset.caSubCertSet.9.default.params.crlDistPointsPointName_0= **http://ipa-ca.ipa.example.com/ipa/crl/MasterCRL.bin**
policyset.caSubCertSet.9.default.params.crlDistPointsPointType_0=MasterCRL
policyset.caSubCertSet.9.default.params.crlDistPointsReasons_0=
policyset.caSubCertSet.10.constraint.class_id=noConstraintImpl
policyset.caSubCertSet.10.constraint.name=No Constraint
policyset.caSubCertSet.10.default.class_id=authInfoAccessExtDefaultImpl
policyset.caSubCertSet.10.default.name=AIA Extension Default
policyset.caSubCertSet.10.default.params.authInfoAccessADEnable_0=true
policyset.caSubCertSet.10.default.params.authInfoAccessADLocationType_0=URIName
policyset.caSubCertSet.10.default.params.authInfoAccessADLocation_0=
policyset.caSubCertSet.10.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1
policyset.caSubCertSet.10.default.params.authInfoAccessCritical=false
policyset.caSubCertSet.10.default.params.authInfoAccessNumADs=1
profileId=SubCA

The place I found this included hardcoding in the CRL to the template. So I did the same. If you don't know how to find the CRL for your own root cert, then you probably do not understand this post anyway. Import the certificate profile.

ipa certprofile-import SubCA --store=true --file=subCA.cfg --desc="Enrolling subordinate certificate authority certificates"

We have to pause to make sure we have the relevant services (and hosts for those services.

ipa host-add --force proxy.ipa.example.com
ipa host-add --force proxy.example.com
ipa host-add --force webproxy.ipa.example.com
ipa host-add --force http-proxy.ipa.example.com
ipa host-add --force https-proxy.ipa.example.com
ipa service-add --force HTTP/proxy.ipa.example.com 
ipa service-add --force HTTP/proxy.example.com 
ipa service-add --force HTTP/webproxy.ipa.example.com 
ipa service-add --force HTTP/http-proxy.ipa.example.com 
ipa service-add --force HTTP/https-proxy.ipa.example.com

Fulfill the request.

ipa cert-request --profile-id=SubCA --principal=HTTP/proxy.ipa.example.com proxy.ipa.example.com.csr

Save down the contents of the cert from that command, or from ipa cert-show 30 where 30 is the serial number. The contents, without any form of CRLF or ilk, can be a single line between the "-----BEGIN CERTIFICATE-----" and "----- END CERTIFICATE-----" lines in a .pem file. This is the public certificate to save down on the squid server as /etc/pki/tls/certs/proxy.ipa.example.com.pem Squid can read this format, but openssl might struggle. You can add a --certificate-out=/path/to/file to either the csr or cert-show commands, which will insert the correct end-of- line characters for openssl itself. That's it for requesting the subCA cert from FreeIPA.

References

Weblinks

http://silverskysoft.com/open-stack-xwrpr/2015/09/creating-a-subordinate- certificate-authority-in-freeipa/

Detect default gtk3 theme

I packaged up a small C utility written by an enterprising user at Stackoverflow. This is a small utility that makes it easy to discover what the current default theme is for gtk3. Patches are welcome for providing support for other versions of gtk. I also added a python3 implementation because, one shouldn't always need a C compiler for something so basic. I decided to package this up, because reading ~/.gtkrc-2.0 or ~/.config/gtk-3.0/settings.ini should be a oneliner! Plus, I can never have too many packages of my own. You should run these programs with stderr redirected to null (i.e., 2>/dev/null) if your theme causes Gtk minor indigestion.

Code

The entire C program follows.

#include 
#include <gtk/gtk.h>
int main() {
    gchar *prop;
   gtk_init(0, 0);
   g_object_get(gtk_settings_get_default(), "gtk-theme-name", &prop, 0);
   return !printf("%s\n", prop);
}

It is worth noting that the original author uses pkg-config to select all the include paths for the linker. Go read the Makefile for the details of that. The python program follows.

1
2
3
4
5
6
#!/usr/bin/python3
import gi
gi.require_version("Gtk","3.0")
from gi.repository import Gtk
settings=Gtk.Settings.get_default()
print(settings.get_property('gtk-theme-name'))

References

Weblinks

  1. C version lifted entirely from linux - GTK2 vs. GTK3: detect GTK3 theme in bash script - Stack Overflow

Apt show repositories for currently installed packages

Apt makes some things very difficult. If I want to see what repository a package comes from, I need this crazy oneliner.

apt-cache policy $( dpkg -l | awk '$1 ~ /^ii/ {print $2}' ) | awk '/^[^ ]+:$/{gsub(":$","",$0);a=$0;b="";c=""} /\*\*\*/ {b=$2} $1 > "100" {c=$2"/"$3} $1 == "100" {print a,b,c}'


xfonts-encodings 1:1.0.4-2 http://deb.devuan.org/merged/beowulf/main
xfonts-utils 1:7.7+6 http://deb.devuan.org/merged/beowulf/main
xml-core 0.18+nmu1 http://deb.devuan.org/merged/beowulf/main
xmlto 0.0.28-2.1 http://deb.devuan.org/merged/beowulf/main
xorg-sgml-doctools 1:1.11-1 http://deb.devuan.org/merged/beowulf/main
yad 0.40.0-1 http://deb.devuan.org/merged/beowulf/main
yasm 1.3.0-2+b1 http://deb.devuan.org/merged/beowulf/main
zip 3.0-11+b1 http://deb.devuan.org/merged/beowulf/main

This would have been a little easier with yum/dnf, but that's not the point of today's article.

Bonus

And a quick count of packages for each repository:

apt-cache policy $( dpkg -l | awk '$1 ~ /^ii/ {print $2}' ) | awk '/^[^ ]+:$/{gsub(":$","",$0);a=$0;b="";c=""} /\*\*\*/ {b=$2} $1 > "100" {c=$2"/"$3} $1 == "100" {print a,b,c}' | \
{
   if test -n "${SUMMARIZE}" ;
   then
      awk '$NF ~ /:\/\// {x[$NF]++} END {for(i in x) print i": "x[i]}'
   else
      cat
   fi
}



$ SUMMARIZE=1 ~/bin/apt-summary.sh 
http://deb.devuan.org/merged/beowulf/main: 377
http://deb.devuan.org/merged/ceres/main: 1979
http://deb.devuan.org/merged/ceres/non-free: 2

Ignore the different releases mixed in there together. Let's pretend I know what I'm doing!

Always show Gtk3 scrollbars

The Gtk3 people think that making scrollbars disappear until your mouse moves near where the scrollbar will appear. For people who always want to see the scrollbar, you can set an environment variable. This is what I have done:

# File: /etc/profile.d/90_gtk.sh
export GTK_OVERLAY_SCROLLING=0

So all users get this value. It is good to be the admin!

Bonus

Apparently the screwy behavior for the scrollbar where it doesn't really follow the mouse correctly is set in the theme.

# Example file: /usr/share/themes/Ambiant-MATE/gtk-3.0/settings.ini
[Settings]
gtk-primary-button-warps-slider = true

I have not implemented this last one yet, but it's worth preserving here. I might get stuck using more gtk3 in the future.

References

Weblinks

  1. GTK3: Changing the overlay/momentium scroll bar - Development Discussion - Ubuntu MATE Community

FreeFileSync 11.1 with traditional view

The FreeFileSync project is one of those amazing, cross-platform GPL-licensed utilities that I use daily. With the recent version updates 10.25, 11.0, and 11.1, the main grid layout has been updated. Users are interested in seeing the old layout again. The upstream author refactored the whole section that displays the grid, so simply reverting these changes wasn't possible for me. But what I have done is write a patch that adds a "Traditional" option to the list of views when you right click the column heading for the path. Check out the entire patch or read it here.

diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp
index 4ee72b97..d9b9a4c7 100644
--- a/FreeFileSync/Source/ui/file_grid.cpp
+++ b/FreeFileSync/Source/ui/file_grid.cpp
@@ -437,6 +437,7 @@ private:
                             case ItemPathFormat::relative:
                                 return utfTo<std::wstring>(fsObj->getRelativePath<side>());
                             case ItemPathFormat::full:
+                            case ItemPathFormat::traditional:
                                 return AFS::getDisplayPath(fsObj->getAbstractPath<side>());
                         }
                         assert(false);
@@ -615,6 +616,16 @@ private:
                 else //=> BaseFolderPair
                     groupParentFolder = AFS::getDisplayPath(pdi.fsObj->base().getAbstractPath<side>());
                 break;
+            case ItemPathFormat::traditional:
+                if (auto groupFolder = dynamic_cast<const FolderPair*>(pdi.folderGroupObj))
+                {
+                    groupName = utfTo<std::wstring>(groupFolder->template getItemName<side>());
+                    groupParentFolder = AFS::getDisplayPath(groupFolder->parent().template getAbstractPath<side>()) + \
+                        FILE_NAME_SEPARATOR + utfTo<std::wstring>(groupFolder->template getItemName<side>());
+                }
+                else //=> BaseFolderPair
+                    groupParentFolder = AFS::getDisplayPath(pdi.fsObj->base().getAbstractPath<side>());
+                break;
         }
         //add slashes for better readability
         assert(!contains(groupParentFolder, L'/') || !contains(groupParentFolder, L'\\'));
@@ -650,72 +661,84 @@ private:
         int widthGroupName   = groupName        .empty() ? 0 : ((iconMgr ? iconSize + gridGap_ : 0) + getTextExtentBuffered(dc, groupName).x + (iconMgr ? gridGap_ : 0));
         int widthGroupItems  = (iconMgr ? iconSize + gridGap_ : 0) + groupItemNamesWidth;

-        //not enough space? => collapse
-        if (int excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
-            excessWidth > 0)
+        switch (itemPathFormat_)
         {
-            if (multiItemGroup && !groupParentFolder.empty() && !groupName.empty())
-            {
-                //1. render group components on two rows
-                stackedGroupRender = true;
-
-                if (!endsWith(groupParentFolder, L'/' ) &&
-                    !endsWith(groupParentFolder, L'\\'))
-                    groupParentFolder += groupParentSep;
-                groupParentFolder += ELLIPSIS;
-
-                widthGroupParent = getTextExtentBuffered(dc, groupParentFolder).x + gridGap_;
-
-                int widthGroupStack = std::max(widthGroupParent, widthGroupName);
-                excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
-
-                if (excessWidth > 0)
-                {
-                    //2. shrink group stack (group parent only)
-                    if (widthGroupParent > widthGroupName)
-                    {
-                        widthGroupStack = widthGroupParent = std::max(widthGroupParent - excessWidth, widthGroupName);
-                        excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
-                    }
-                    if (excessWidth > 0)
-                    {
-                        //3. shrink item rendering
-                        widthGroupItems = std::max(widthGroupItems - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth);
-                        excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
-
-                        if (excessWidth > 0)
-                        {
-                            //4. shrink group stack
-                            widthGroupStack = std::max(widthGroupStack - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth + (iconMgr ? gridGap_ : 0));
-
-                            widthGroupParent = std::min(widthGroupParent, widthGroupStack);
-                            widthGroupName   = std::min(widthGroupName,   widthGroupStack);
-                        }
-                    }
-                }
-            }
-            else //group details on single row
-            {
-                //1. shrink group parent
-                if (!groupParentFolder.empty())
-                {
-                    widthGroupParent = std::max(widthGroupParent - excessWidth, ellipsisWidth + (iconMgr ? gridGap_ : 0));
-                    excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
-                }
-                if (excessWidth > 0)
-                {
-                    //2. shrink item rendering
-                    widthGroupItems = std::max(widthGroupItems - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth);
-                    excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
-
-                    if (excessWidth > 0)
-                        //3. shrink group name
-                        if (!groupName.empty())
-                            widthGroupName = std::max(widthGroupName - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth + (iconMgr ? gridGap_ : 0));
-                }
-            }
+            case ItemPathFormat::traditional:
+                //widthGroupName = 0;
+                widthGroupParent -= widthGroupName ;
+                break;
+            case ItemPathFormat::name:
+            case ItemPathFormat::relative:
+            case ItemPathFormat::full:
+            default:
+               // the insane logic of the new views
+               //not enough space? => collapse
+               if (int excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
+                   excessWidth > 0)
+               {
+                   if (multiItemGroup && !groupParentFolder.empty() && !groupName.empty())
+                   {
+                       //1. render group components on two rows
+                       stackedGroupRender = true;
+
+                       if (!endsWith(groupParentFolder, L'/' ) &&
+                           !endsWith(groupParentFolder, L'\\'))
+                           groupParentFolder += groupParentSep;
+                       groupParentFolder += ELLIPSIS;
+
+                       widthGroupParent = getTextExtentBuffered(dc, groupParentFolder).x + gridGap_;
+
+                       int widthGroupStack = std::max(widthGroupParent, widthGroupName);
+                       excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
+
+                       if (excessWidth > 0)
+                       {
+                           //2. shrink group stack (group parent only)
+                           if (widthGroupParent > widthGroupName)
+                           {
+                               widthGroupStack = widthGroupParent = std::max(widthGroupParent - excessWidth, widthGroupName);
+                               excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
+                           }
+                           if (excessWidth > 0)
+                           {
+                               //3. shrink item rendering
+                               widthGroupItems = std::max(widthGroupItems - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth);
+                               excessWidth = gridGap_ + widthGroupStack + widthGroupItems - maxWidth;
+
+                               if (excessWidth > 0)
+                               {
+                                   //4. shrink group stack
+                                   widthGroupStack = std::max(widthGroupStack - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth + (iconMgr ? gridGap_ : 0));
+
+                                   widthGroupParent = std::min(widthGroupParent, widthGroupStack);
+                                   widthGroupName   = std::min(widthGroupName,   widthGroupStack);
+                               }
+                           }
+                       }
+                   }
+                   else //group details on single row
+                   {
+                       //1. shrink group parent
+                       if (!groupParentFolder.empty())
+                       {
+                           widthGroupParent = std::max(widthGroupParent - excessWidth, ellipsisWidth + (iconMgr ? gridGap_ : 0));
+                           excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
+                       }
+                       if (excessWidth > 0)
+                       {
+                           //2. shrink item rendering
+                           widthGroupItems = std::max(widthGroupItems - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth);
+                           excessWidth = gridGap_ + widthGroupParent + widthGroupName + widthGroupItems - maxWidth;
+
+                           if (excessWidth > 0)
+                               //3. shrink group name
+                               if (!groupName.empty())
+                                   widthGroupName = std::max(widthGroupName - excessWidth, (iconMgr ? iconSize + gridGap_ : 0) + ellipsisWidth + (iconMgr ? gridGap_ : 0));
+                       }
+                   }
+                 }
+                 break;
         }
-
         return
         {
             itemName,
@@ -786,6 +809,9 @@ private:
                     rectGroup = rectGroupParent = rectGroupName = rectTmp;

                     rectGroupParent.width = widthGroupParent;
+                    // re-add back the width of groupname so that the directory name is clickable
+                    if (itemPathFormat_ == ItemPathFormat::traditional)
+                        rectGroupParent.width += widthGroupName;
                     rectGroupName  .width = widthGroupName;

                     if (stackedGroupRender)
@@ -837,7 +863,7 @@ private:
                             dc.GradientFillLinear(rectNav, getColorSelectionGradientFrom(), backCol, wxEAST);
                         }

-                        if (!groupName.empty() && row == groupBeginRow)
+                        if (!(itemPathFormat_ == ItemPathFormat::traditional) && !groupName.empty() && row == groupBeginRow)
                         {
                             wxDCTextColourChanger textColorGroupName(dc);
                             if (static_cast<HoverAreaGroup>(rowHover) == HoverAreaGroup::groupName)
@@ -855,9 +881,9 @@ private:
                             drawCellText(dc, rectGroupName, groupName, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, &getTextExtentBuffered(dc, groupName));
                         }

-                        if (!groupParentFolder.empty() &&
+                        if (itemPathFormat_ == ItemPathFormat::traditional || (!groupParentFolder.empty() &&
                             ((stackedGroupRender && row == groupBeginRow + 1) ||
-                             (!stackedGroupRender && row == groupBeginRow)))
+                             (!stackedGroupRender && row == groupBeginRow))))
                         {
                             drawCellText(dc, rectGroupParent, groupParentFolder, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, &getTextExtentBuffered(dc, groupParentFolder));
                         }
@@ -1020,6 +1046,8 @@ private:
                         return _("Relative path");
                     case ItemPathFormat::full:
                         return _("Full path");
+                    case ItemPathFormat::traditional:
+                        return _("Traditional");
                 }
                 assert(false);
                 break;
diff --git a/FreeFileSync/Source/ui/file_grid_attr.h b/FreeFileSync/Source/ui/file_grid_attr.h
index 324619c1..7511a1ab 100644
--- a/FreeFileSync/Source/ui/file_grid_attr.h
+++ b/FreeFileSync/Source/ui/file_grid_attr.h
@@ -79,6 +79,7 @@ enum class ItemPathFormat
     name,
     relative,
     full,
+    traditional,
 };

 const ItemPathFormat defaultItemPathFormatLeftGrid  = ItemPathFormat::relative;
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 491b7321..2d5c20c3 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -2716,6 +2716,7 @@ void MainDialog::onGridLabelContextRim(bool leftSide)
     addFormatEntry(_("Item name"    ), ItemPathFormat::name);
     addFormatEntry(_("Relative path"), ItemPathFormat::relative);
     addFormatEntry(_("Full path"    ), ItemPathFormat::full);
+    addFormatEntry(_("Traditional"  ), ItemPathFormat::traditional);

     //----------------------------------------------------------------------------------------------
     menu.addSeparator();
diff -x .git -Naur 11.1-1/FreeFileSync/Source/config.cpp 11.1-2/FreeFileSync/Source/config.cpp
--- 11.1-1/FreeFileSync/Source/config.cpp 2020-09-01 19:07:43.715122167 -0400
+++ 11.1-2/FreeFileSync/Source/config.cpp 2020-09-10 09:38:14.539542699 -0400
@@ -528,6 +528,9 @@
         case ItemPathFormat::full:
             output = "Full";
             break;
+        case ItemPathFormat::traditional:
+            output = "Traditional";
+            break;
     }
 }

@@ -541,6 +545,8 @@
         value = ItemPathFormat::relative;
     else if (tmp == "Full")
         value = ItemPathFormat::full;
+    else if (tmp == "Traditional")
+        value = ItemPathFormat::traditional;
     else
         return false;
     return true;
diff -x .git -Naur 11.1-1/FreeFileSync/Source/ui/file_view.cpp 11.1-2/FreeFileSync/Source/ui/file_view.cpp
--- 11.1-1/FreeFileSync/Source/ui/file_view.cpp 2020-09-01 19:07:43.719122215 -0400
+++ 11.1-2/FreeFileSync/Source/ui/file_view.cpp 2020-09-10 09:40:11.609044834 -0400
@@ -798,6 +798,7 @@
                     break;

                 case ItemPathFormat::full:
+                case ItemPathFormat::traditional:
                     if      ( ascending &&  onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true,   LEFT_SIDE>(folderPairs_));
                     else if ( ascending && !onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<true,  RIGHT_SIDE>(folderPairs_));
                     else if (!ascending &&  onLeft) std::sort(sortedRef_.begin(), sortedRef_.end(), LessFullPath<false,  LEFT_SIDE>(folderPairs_));

This patch, which is valid for FreeFileSync 11.1 source code, still allows the "select all in this directory" function that exists on the other views. The view provided shows a full path for every item, just like version 10.24 did! I bundle this patch into my package for FreeFileSync on my Devuan OBS repository. I have submitted this patch to upstream, so hopefully it can be added to the mainline FreeFileSync. Otherwise, I intend to keep providing updated versions of this patch for each new release of FreeFileSync. I need this for myself, and everyone else is allowed to benefit from it too.