Hey BeBox, My SMB Network Hates You!
Sheesh. After getting the machine up and running, getting it on the network was next-level shite! The BeBox, with BeOS 4.5 PPC, comes with an optional folder and, included in there, is an experimental folder containin an application known as WON. This stands for World 'O Networking (I REALLY blame this use of O' for my apostrophicities in this entire blog as I was introduced to WON back in '99) and is a CIFS implementation. Running the setup app easily installs it, but opening the newly placed World 'O Networking icon on the desktop does nothing but tell me that my CIFS Master Browser didn't exist.
Do excuse the crappy photo...
In this day'n'age, there's no #@%#$%'n reason for a CIFS browser to exist on anyone's network. We have random french-named services to say 'hello' to eachother and list eachother in eachother's network lists. There's no need for this (now inappropriate) client/server (master/slave) relationship. Of course, should we expect BeOS 4.5 PPC to be up with the times? No. So? Let's just try the cifsmount call from the command line.
$cifsmount \\\\192.168.1.61\\public [user] [password] ./testMountFolder General OS Error -1
General OS error? Awful. Fortunately, my NAS can log the snot out of Samba, so I turned on verbosity. Whenever BeOS tried to access the share, the following was visible:
[2022/12/19 14:37:58.602852, 2, pid=6552, effective(0, 0), real(0, 0)] ../../source3/smbd/reply.c:708(reply_special) netbios connect: name1=AS6604T-BDE6 0x20 name2=BEBOX 0x0 [2022/12/19 14:37:58.603018, 2, pid=6552, effective(0, 0), real(0, 0)] ../../source3/smbd/reply.c:749(reply_special) netbios connect: local=as6604t-bde6 remote=bebox, name type = 0 [2022/12/19 14:37:58.613129, 0, pid=6552, effective(0, 0), real(0, 0)] ../../source3/smbd/negprot.c:600(reply_negprot) negprot protocols not 0-terminated
Nice! It actually says BEBOX!. Somehow, samba does nothing after that line. It doesn't say "ERROR"... but it also doesn't continue? Checking the code...
if (req->inbuf[size-1] != '\0') { DEBUG(0, ("negprot protocols not 0-terminated\n")); reply_nterror(req, NT_STATUS_INVALID_PARAMETER); END_PROFILE(SMBnegprot); return; }
Oh yup. That's a hard-stop. It seems that the CIFS implementation on BeOS hasn't null-terminated a string as it sent it to Samba. We'll need to build the code and rip this out, or rollback to an older version. SMB is running as a package on my NAS and ... I assume there's source somewhere, but every time I've SSH'd into that console the tools I need don't exist, so let's virtualise this shit.
Samba Proxies or Virtual Box?
We have two options here. Spin up a RedHat Linux (~7.0) version and just start Samba on that (it works, I tried it!) or spin up a possibly awesome docker image and "re-share" our existing security-conscious shares. Yes, I got the VM going, but I wasn't happy with an entire VM running endlessly for no reason when my retro machines were off. I also hated the need to cleanly bring-up and shut-down the machine, not to mention the speed of interaction.
So, I went for a Docker image. I'm running an Asustor Lockerstor AS6600T which has a neatly packaged version of Docker and Portainer running. It's currently doing all sorts of things and adding a samba container wasn't going to hurt. The only real consideration was that, even though I was using a macvlan to get real IPs for my containers, I still had not successfully managed to get the NAS to talk directly into the hosted docker containers. I was initially surprised by this, but it turns out that's by-design... docker images aren't 'allowed' to see the metal they're hosted on. Although there's articles mentioning how to fix this (and a slightly different one here), I couldn't get it to work. Therefore, a 'samba proxy' wont actually work... as it needs to be able to SMB mount the host's share(s) and share those forward via it's own SMB server.
Fortunately, we don't have to fret yet. It occurred to me that I can simply mount the host's drives in the docker image and use David Personette's Samba Server container to share those! There's no need to actually proxy anything.
Samba Versions
This negprot 0-termination check has been in Samba since r24001, which was around 15 years ago, which equates to something around Samba 3.1. Docker can run older Linux versions, but only until around 8 years ago. Centos 5 might work, but you'll be hard-pressed finding functional package repos once it's installed. Alpine Linux is the way to go, but there's only docker images going back to 3.1, which doesn't line up with Samba 3.1! If we can't use a version of Samba (in Docker, anyway) that doesn't include the check, then maybe we can remove the check from a newer version? We just need to make sure the newer version supports SMB v1.0 (LANMAN, CIFS, etc..) and it turns out support for these older protocols was removed in version 4.11+.
As that David's container is build on the latest Alpine, it includes Samba 4.18 and this is obviously too new. It turns out that the last version of Samba before 4.11 is v4.18.10 and it's conveniently included in Alpine v3.10. Thanks for Docker's customisable recipes, we can modify David's Dockerfile, adjusting the top line to peg the Alpine base image version to this. Finally, we also need to make sure that the install scripts don't get Samba from the base Alpine 3.10 repo as we actually want to custom-build the APKs and carve out the zero-termination check.
I was initially going to download the source of Samba and try to build/install it myself in a new docker container... but I realised that it would be a tonne of extra work as there's probably distro-specific guff that needs to be configured/carried-out. So instead, as above, I chose to roll my own APK using the APK recipe from the Alpine repo.
Adding a package to Alpine Linux to make Docker better again
If you want to actually build the APK yourself, then here's a basic run-through. I've taken the package recipe from the alpine packages store and made a few changes. You'll find them here. Mainly just a sed script to hack out the 0-termination check.
If you don't want to bother compiling your own APK with this modification, then you can just download the already-compiled APK here. The only reason you'd want to do the next chunk yourself is if you don't trust my hard work. I wont be offended if that's the case.
I started by spinning up a new docker container...
sudo docker run --name apk-builder -v /volume1/Public:/public-share -it alpine:3.10
Note that I've mapped a local drive. You'll want to do this as otherwise you'll have to work out a better way to get the compiled APKs out. Once up, you'll be already at the console and you can get started...
apk add --update alpine-sdk wget nano sudo adduser builder
At this point you'll need to give builder a good password. Once done, continue to set up the APK build environment. Note that the sed line injects the rule to sudoers for the user builder at line 80. If you're doing this on a tainted Docker container (not the fresh one I just built) then you might want to be wary as to which line you insert on.
sed -i "80i builder ALL=(ALL) ALL" /etc/sudoers sudo -lU builder addgroup builder abuild mkdir -p /var/cache/distfiles chmod a+w /var/cache/distfiles chgrp abuild /var/cache/distfiles chmod g+w /var/cache/distfiles
We've done all we need to as root, so switch to builder:
su builder
And then generate your signing keys which'll be used when compressing the APK...
abuild-keygen -a -i
You'll need to type in builder's password for the key storage. Finally, go into the dir and download the APKBUILD files from my server. Please do check out the files downloaded... specifically APKBUILD! The hack to remove the 0-term if statement is already contained inside. It's simply a sed line requesting the deletion of lines 599-605 in negprot.c.
cd /tmp wget https://modelrail.otenko.com/assets/samba-negprot-hack/samba.zip unzip samba.zip
Finally, kick it off. Go grab a coffee.
abuild -r
The build takes around 20 minutes on my quad core i3 NAS. The build is nice as it tells you the progress via the files-completed numbers at the start of each line.
... [3287/5138] Linking bin/default/source3/libads-samba4.inst.so [3288/5138] Linking bin/default/source3/libsmbd-conn-samba4.inst.so [3289/5138] Linking bin/default/source3/libsmbd-base-samba4.inst.so [3290/5138] Linking bin/default/source3/libprinting-migrate-samba4.inst.so [3292/5138] Linking bin/default/source3/libnet-keytab-samba4.inst.so [3294/5138] Linking bin/default/source3/libtrusts-util-samba4.inst.so [3295/5138] Linking bin/default/source3/libsamba3-util-samba4.inst.so [3297/5138] Linking bin/default/source3/libxattr-tdb-samba4.inst.so [3299/5138] Linking bin/default/source3/libCHARSET3-samba4.inst.so [3301/5138] Linking bin/default/source3/liblibcli-lsa3-samba4.inst.so [3303/5138] Linking bin/default/source3/liblibcli-netlogon3-samba4.inst.so [3306/5138] Linking bin/default/source3/libcli-spoolss-samba4.inst.so [3308/5138] Linking bin/default/source3/pysmbd.inst.cpython-37m-x86_64-linux-gnu.so [3310/5138] Linking bin/default/source3/pylibsmb.inst.cpython-37m-x86_64-linux-gnu.so [3312/5138] Linking bin/default/source3/auth/libauth-samba4.inst.so [3313/5138] Linking bin/default/source3/auth/libauth_module_unix.inst.so [3316/5138] Linking bin/default/source3/auth/libauth_module_script.inst.so [3318/5138] Linking bin/default/source3/auth/libauth_module_samba4.inst.so [3320/5138] Linking bin/default/source3/modules/libnon-posix-acls-samba4.inst.so [3323/5138] Linking bin/default/source3/modules/libvfs_module_audit.inst.so [3325/5138] Linking bin/default/source3/modules/libvfs_module_extd_audit.inst.so [3327/5138] Linking bin/default/source3/modules/libvfs_module_full_audit.inst.so [3329/5138] Linking bin/default/source3/modules/libvfs_module_fake_perms.inst.so [3331/5138] Linking bin/default/source3/modules/libvfs_module_recycle.inst.so [3333/5138] Linking bin/default/source3/modules/libvfs_module_netatalk.inst.so [3335/5138] Linking bin/default/source3/modules/libvfs_module_fruit.inst.so [3337/5138] Linking bin/default/source3/modules/libvfs_module_default_quota.inst.so [3339/5138] Linking bin/default/source3/modules/libvfs_module_readonly.inst.so ... (93/105) Purging tevent (0.9.39-r0) (94/105) Purging talloc (2.2.0-r0) (95/105) Purging tdb-libs (1.3.18-r0) (96/105) Purging libbz2 (1.0.6-r7) (97/105) Purging gdbm (1.13-r1) (98/105) Purging xz-libs (5.2.4-r0) (99/105) Purging readline (8.0.0-r0) (100/105) Purging sqlite-libs (3.28.0-r3) (101/105) Purging lz4-libs (1.9.1-r1) (102/105) Purging keyutils-libs (1.6-r1) (103/105) Purging libverto (0.3.1-r0) (104/105) Purging libsasl (2.1.27-r4) (105/105) Purging db (5.3.28-r1) Executing busybox-1.30.1-r5.trigger OK: 184 MiB in 56 packages >>> samba: Updating the /x86_64 repository index... >>> samba: Signing the index...
Once built, you'll end up with a bunch of APKs in /home/builder/packages.
~/packages/x86_64 $ ls APKINDEX.tar.gz samba-dev-4.10.18-r0.apk libsmbclient-4.10.18-r0.apk samba-doc-4.10.18-r0.apk libwbclient-4.10.18-r0.apk samba-heimdal-libs-4.10.18-r0.apk pam-winbind-4.10.18-r0.apk samba-libnss-winbind-4.10.18-r0.apk py3-samba-4.10.18-r0.apk samba-libs-4.10.18-r0.apk samba-4.10.18-r0.apk samba-libs-py3-4.10.18-r0.apk samba-client-4.10.18-r0.apk samba-pidl-4.10.18-r0.apk samba-client-libs-4.10.18-r0.apk samba-server-4.10.18-r0.apk samba-common-4.10.18-r0.apk samba-server-libs-4.10.18-r0.apk samba-common-libs-4.10.18-r0.apk samba-server-openrc-4.10.18-r0.apk samba-common-server-libs-4.10.18-r0.apk samba-test-4.10.18-r0.apk samba-common-tools-4.10.18-r0.apk samba-winbind-4.10.18-r0.apk samba-dc-4.10.18-r0.apk samba-winbind-clients-4.10.18-r0.apk samba-dc-libs-4.10.18-r0.apk samba-winbind-krb5-locator-4.10.18-r0.apk
Copy these all somewhere to your local drive, as we'll need to use them for the Docker container creation. If you are rolling this yourself, then your Dockerfile/docker-compose.yaml will need to map a local volume to the folder where all of these files are. If you're not rolling them yourself, then use the zip that's in this Dockerfile.
The container script uses David's as a base, but sets Alpine to 3.10 and uses Samba from my APKs. It also does a lot of mucking around with smb.conf. Prior to that configuration mucking around, BeOS would get past the negprot issue only to bring up another error:
Allowed connection from 192.168.1.131 (192.168.1.131) init_oplocks: initializing messages. Transaction 0 of length 72 (0 toread) netbios connect: name1=192.168.1.46 0x20 name2= 0x0 netbios connect: local=192.168.1.46 remote=, name type = 0 Transaction 0 of length 51 (0 toread) switch message SMBnegprot (pid 1071) conn 0x0 Requested protocol [NT LM 0.12] reply_negprot: No protocol supported ! Server exit (no protocol supported)
Seems that NT LM 0.12 is NT LAN Manager 2.1, or somesuch. I'd thought about digging back into negprot.c and checking the reply_negprot function, but a quick google lead to something very obvious... configuration!
SERVER MIN PROTOCOL
As always, someone has already faced this issue and it seems that all they had to do was set the 'lowest' protocol that the server would accept, via a setting? David's smb.conf had a default minimum of SMB2_10, which seems to mean that we only supported Windows 7 and above? Wowzers. For those not clicking links, here's the protocol limitation options:
Option | Description |
---|---|
LANMAN1 | First modern version of the protocol. Long filename support. |
LANMAN2 | Updates to Lanman1 protocol. |
NT1 | Current up to date version of the protocol. Used by Windows NT. Known as CIFS. |
SMB2 | Re-implementation of the SMB protocol. Used by Windows Vista and later versions of Windows. SMB2 has sub protocols available. |
SMB2_02 | The earliest SMB2 version. (Windows Vista and higher) |
SMB2_10 | Windows 7 SMB2 version. |
SMB2 | By default selects the SMB2_10 variant. |
SMB3 | The same as SMB2. Used by Windows 8. SMB3 has sub protocols available. |
SMB3_00 | Windows 8 SMB3 version. |
SMB3_02 | Windows 8.1 SMB3 version. |
SMB3_11 | Windows 10 SMB3 version. |
There's actually a really cool piece of code explaining all this in the negprot.c source file:
/* these are the protocol lists used for auto architecture detection: WinNT 3.51: protocol [PC NETWORK PROGRAM 1.0] protocol [XENIX CORE] protocol [MICROSOFT NETWORKS 1.03] protocol [LANMAN1.0] protocol [Windows for Workgroups 3.1a] protocol [LM1.2X002] protocol [LANMAN2.1] protocol [NT LM 0.12] Win95: protocol [PC NETWORK PROGRAM 1.0] protocol [XENIX CORE] protocol [MICROSOFT NETWORKS 1.03] protocol [LANMAN1.0] protocol [Windows for Workgroups 3.1a] protocol [LM1.2X002] protocol [LANMAN2.1] protocol [NT LM 0.12] Win2K: protocol [PC NETWORK PROGRAM 1.0] protocol [LANMAN1.0] protocol [Windows for Workgroups 3.1a] protocol [LM1.2X002] protocol [LANMAN2.1] protocol [NT LM 0.12] Vista: protocol [PC NETWORK PROGRAM 1.0] protocol [LANMAN1.0] protocol [Windows for Workgroups 3.1a] protocol [LM1.2X002] protocol [LANMAN2.1] protocol [NT LM 0.12] protocol [SMB 2.001] OS/2: protocol [PC NETWORK PROGRAM 1.0] protocol [XENIX CORE] protocol [LANMAN1.0] protocol [LM1.2X002] protocol [LANMAN2.1] OSX: protocol [NT LM 0.12] protocol [SMB 2.002] protocol [SMB 2.???] */ /* * Modified to recognize the architecture of the remote machine better. * * This appears to be the matrix of which protocol is used by which * product. Protocol WfWg Win95 WinNT Win2K OS/2 Vista OSX PC NETWORK PROGRAM 1.0 1 1 1 1 1 1 XENIX CORE 2 2 MICROSOFT NETWORKS 3.0 2 2 DOS LM1.2X002 3 3 MICROSOFT NETWORKS 1.03 3 DOS LANMAN2.1 4 4 LANMAN1.0 4 2 3 2 Windows for Workgroups 3.1a 5 5 5 3 3 LM1.2X002 6 4 4 4 LANMAN2.1 7 5 5 5 NT LM 0.12 6 8 6 6 6 1 SMB 2.001 7 SMB 2.002 2 SMB 2.??? 3 * * tim@fsg.com 09/29/95 * Win2K added by matty 17/7/99 */ #define PROT_PC_NETWORK_PROGRAM_1_0 0x0001 #define PROT_XENIX_CORE 0x0002 #define PROT_MICROSOFT_NETWORKS_3_0 0x0004 #define PROT_DOS_LM1_2X002 0x0008 #define PROT_MICROSOFT_NETWORKS_1_03 0x0010 #define PROT_DOS_LANMAN2_1 0x0020 #define PROT_LANMAN1_0 0x0040 #define PROT_WFWG 0x0080 #define PROT_LM1_2X002 0x0100 #define PROT_LANMAN2_1 0x0200 #define PROT_NT_LM_0_12 0x0400 #define PROT_SMB_2_001 0x0800 #define PROT_SMB_2_002 0x1000 #define PROT_SMB_2_FF 0x2000 #define PROT_SAMBA 0x4000 #define PROT_POSIX_2 0x8000 #define ARCH_WFWG ( PROT_PC_NETWORK_PROGRAM_1_0 | PROT_MICROSOFT_NETWORKS_3_0 | \ PROT_DOS_LM1_2X002 | PROT_DOS_LANMAN2_1 | PROT_WFWG ) #define ARCH_WIN95 ( ARCH_WFWG | PROT_NT_LM_0_12 ) #define ARCH_WINNT ( PROT_PC_NETWORK_PROGRAM_1_0 | PROT_XENIX_CORE | \ PROT_MICROSOFT_NETWORKS_1_03 | PROT_LANMAN1_0 | PROT_WFWG | \ PROT_LM1_2X002 | PROT_LANMAN2_1 | PROT_NT_LM_0_12 ) #define ARCH_WIN2K ( ARCH_WINNT & ~(PROT_XENIX_CORE | PROT_MICROSOFT_NETWORKS_1_03) ) #define ARCH_OS2 ( ARCH_WINNT & ~(PROT_MICROSOFT_NETWORKS_1_03 | PROT_WFWG) ) #define ARCH_VISTA ( ARCH_WIN2K | PROT_SMB_2_001 ) #define ARCH_SAMBA ( PROT_SAMBA ) #define ARCH_CIFSFS ( PROT_POSIX_2 ) #define ARCH_OSX ( PROT_NT_LM_0_12 | PROT_SMB_2_002 | PROT_SMB_2_FF ) /* List of supported protocols, most desired first */ static const struct { const char *proto_name; const char *short_name; NTSTATUS (*proto_reply_fn)(struct smb_request *req, uint16_t choice); int protocol_level; } supported_protocols[] = { {"SMB 2.???", "SMB2_FF", reply_smb20ff, PROTOCOL_SMB2_10}, {"SMB 2.002", "SMB2_02", reply_smb2002, PROTOCOL_SMB2_02}, {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1}, {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1}, {"POSIX 2", "NT1", reply_nt1, PROTOCOL_NT1}, {"LANMAN2.1", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, {NULL,NULL,NULL,0}, };
So, is it just as-simple-as setting the lowest bloody value and rebooting? I edited the Dockerfile and updated the injected smb.conf configuration:
echo ' # Security' >>$file && \ echo ' client ipc max protocol = SMB3' >>$file && \ echo ' client ipc min protocol = LANMAN1' >>$file && \ echo ' client max protocol = SMB3' >>$file && \ echo ' client min protocol = LANMAN1' >>$file && \ echo ' server max protocol = SMB3' >>$file && \ echo ' server min protocol = LANMAN1' >>$file && \
And it worked! Finally! BeOS has connected to my NAS with a reasonably-recent version of linux and Samba! Sure, commenting out the zero-termination might reinstate a bug that can be exploited, but this service will never be public. Meanwhile, World 'O Networking still hated me... but that's OK, at least I can now transfer files!
Giving Docker a Hostname
As you can see above, all interactions with the new samba docker server are via IP. It turns out that the --hostname field that you configure in Docker only applies internally to the docker container. It's not exported! Googlin' around, I found a great stack overflow article describing the same issue, with many "can't do that" responses.
It's not until you scroll a few answers down that you'll see a --net-alias flag that seems to do want I want. Just a note, it's --alias if you're using docker network connect and --net-alias when using docker compose. I added the required configuration to the Dockerfile and spun up a new instance.
services: samba-bridge: networks: mynet: ipv4_address: 192.168.1.46 aliases:old-samba-bridge networks: mynet: external: true
mynet has been set up using the instructions over here, which were pertinent to getting PiHole running in an adjacent Docker container. The rest of the configuration was borrowed from here.
I honestly hoped this would get Samba showing up on the local LAN with a hostname, but no such luck! I wonder if the docker network is preventing promiscuous traffic or somesuch... Maybe I'll spin this up on a physical machine on the network just to rule a few things out.
But what about that CIFS Browser error?
Oh yeah, I would still love WON to actually list my computers... can I do it? It seems that you can configure Samba to be a CIFS Server with a single local master = yes in the configuration file. Unfortuantely this didn't work... neither did wins support = yes. I'll have to keep fighting to get my WORKGROUP listed.